Spring Boot + Jersey 类型过滤器 - 服务的错​​误请求 400 消耗 MULTIPART_FORM_DATA

Posted

技术标签:

【中文标题】Spring Boot + Jersey 类型过滤器 - 服务的错​​误请求 400 消耗 MULTIPART_FORM_DATA【英文标题】:Spring Boot + Jersey type filter - Bad request 400 for service Consumes MULTIPART_FORM_DATA 【发布时间】:2018-10-27 11:56:02 【问题描述】:

我正在使用 Spring boot v1.5.10 + Jersey v2.25.1,将 jersey 配置为过滤器以访问静态文件夹文件。我收到 HTTP 响应 400 Bad Request 服务使用 MULTIPART_FORM_DATA

将 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 链接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(有效)

【问题讨论】:

@Paul Samsotha - 请检查 GitHub 链接和请求。 localhost:8080/save (works) 当 spring.jersey.type=servlet 【参考方案1】:

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

注意:在多部分 POST 请求的情况下,此过滤器需要在多部分处理之后运行,因为它固有地需要检查 POST 正文参数。

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

spring.jersey.filter.order

在 Spring Boot 存储库中为HiddenHttpMethodFilter 执行Github search,我们可以看到使用的子类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。一些需要关注的问题 [1, 2]2.见Change order of RequestContextFilter in the filter chain

【讨论】:

这很好地解释了这个问题,但是在这么早的时候应用 Jersey 过滤器的解决方案意味着它在 Spring Security 之前运行。 @AdrianBaker 是的,我没有使用 Spring Security,所以我没有考虑到这一点。但这对其他人来说是很好的信息。【参考方案2】:

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

奇怪的是,设置 spring.jersey.type=servlet 即使事先调用了 getParameterMap,我也可以获得请求正文,所以我猜 ServletRequest 的实现会有所不同,具体取决于您为 spring.jersey.type 配置的内容。

【讨论】:

以上是关于Spring Boot + Jersey 类型过滤器 - 服务的错​​误请求 400 消耗 MULTIPART_FORM_DATA的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + Jersey

Spring-Boot Jersey:允许 Jersey 提供静态内容

jersey在 spring boot 添加 packages 扫描路径支持

Spring Boot Jersey 和监控 URL

Spring boot、jwt、jersey、spring security和CORS问题

列出所有已部署的 REST 端点(spring-boot、jersey)