Spring 启动 + 球衣类型过滤器 - 服务消费的错误请求 400 MULTIPART_FORM_DATA

Spring Boot + Jersey type filter - Bad request 400 for service Consumes MULTIPART_FORM_DATA

我正在使用 Spring boot v1.5.10 + Jersey v2.25.1,将 jersey 配置为过滤器以访问静态文件夹文件。对于使用 MULTIPART_FORM_DATA.

的服务,我收到 HTTP 响应 400 Bad Request

将 Jersey 配置为过滤器的道具。

spring.jersey.type=filter

如果我删除以上 属性,即使用 Jersey 作为 Servlet,该服务正在运行,但我无法访问静态文件夹。

这里是控制器,

@POST
@Path("/save")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public ResponseBean save(
        @FormDataParam("fileToUpload") InputStream file,
        @FormDataParam("fileToUpload") FormDataContentDisposition fileDisposition,
        @FormDataParam("fromData") FormDataDto data) {
    // stuff
}

编辑:

GitHub link https://github.com/sundarabalajijk/boot-jersey

当您启动应用程序时,spring.jersey.type=filter

http://localhost:8080/(有效)

http://localhost:8080/hello.html(有效)

http://localhost:8080/save(不工作)- 使用邮递员。

spring.jersey.type=servlet

http://localhost:8080/(有效)

http://localhost:8080/hello.html(不工作)

http://localhost:8080/save(有效)

经过一些研究和发现相关问题1,似乎 Spring 的 HiddenHttpMethodFilter 读取了输入流,这使得任何输入流都为空其他过滤器在过滤器链的下游。这就是我们在 Jersey 过滤器中收到 Bad Request 的原因;因为实体流是空的。这是来自 Javadoc

的说明

NOTE: This filter needs to run after multipart processing in case of a multipart POST request, due to its inherent need for checking a POST body parameter.

所以我们需要做的是将 Jersey 过滤器配置为在此 Spring 过滤器2 之前调用。在 Spring Boot docs 的基础上,有一个 属性 我们可以用来轻松配置此过滤器的顺序。

spring.jersey.filter.order

执行 Github search in the Spring Boot repo for the HiddenHttpMethodFilter, we can see the subclass that is used OrderedHiddenHttpMethodFilter,其中顺序设置为 -10000。因此,我们希望将 Jersey 过滤器的顺序设置为小于该顺序(更高优先级)。所以我们可以设置如下值

spring.jersey.filter.order=-100000

如果你现在测试它,它现在应该可以工作了。

我们需要修复的另一件事是 Spring RequestContextFilter 的顺序。这最初配置为在 Jersey 过滤器之前被调用。当我们为 Jersey 过滤器设置上面的顺序配置时,RequestContextFilter 保持在原来的位置。所以我们需要改变这一点。我们可以通过添加一个 bean 来覆盖原始 bean 并设置顺序来做到这一点。

@Bean
public RequestContextFilter requestContextFilter() {
    OrderedRequestContextFilter filter = new OrderedRequestContextFilter();
    filter.setOrder(-100001);
    return filter;
}

现在,如果我们在启动时检查日志,我们应该会看到我们想要的文件管理器顺序。

Mapping filter: 'characterEncodingFilter' to: [/*]
Mapping filter: 'requestContextFilter' to: [/*]
Mapping filter: 'jerseyFilter' to urls: [/*]
Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
Mapping filter: 'httpPutFormContentFilter' to: [/*]

放在一边

您需要将 Jersey 配置为过滤器的原因是静态内容。如果您没有为 Jersey 应用程序配置根路径,那么它默认为 /*,这将占用所有请求,包括对静态内容的请求。因此,当对静态内容发出请求时,Jersey 将抛出 404 错误。我们将 Jersey 配置为过滤器并告诉它转发它找不到的请求。

如果我们只为 Jersey 配置根路径,那么我们就不需要担心静态内容的这个问题,我们可以让 Jersey 默认配置为 servlet。

要更改 Jersey 应用程序的基本路径,我们可以将 @ApplicatuonPath 注释添加到我们的 ResourceConfig 或者我们可以使用 属性 spring.jersey.application-path

@Component
@ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {
    ...
}

或在您的 application.properties

spring.jersey.application-path=/api

另见

  • File upload along with other object in Jersey restful web service。这有一些关于如何接受 JSON 作为多部分请求中的正文部分的信息。

脚注

1.需要注意的一些问题 [ , 2 ]
2。参见

我想接受的答案已经不是最新的了。使用 Spring Boot 2 我没有遇到这样的问题。但是有一个问题。我有一个自定义过滤器,它在请求对象上调用 getParameterMap。显然 getParameterMap 隐式读取请求的输入流,然后将其关闭,这样其他代码就无法再读取请求正文了。

奇怪的是,设置 spring.jersey.type=servlet 即使事先调用了 getParameterMap,我也可以获得请求正文,所以我猜 ServletRequest 的实现因您的配置而异spring.jersey.type.