Spring Boot + Jersey 类型过滤器 - 服务消耗 MULTIPART_FORM_DATA 的错误请求 400 [英] 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("/保存")@Consumes(MediaType.MULTIPART_FORM_DATA)@Produces(MediaType.APPLICATION_JSON)公共 ResponseBean 保存(@FormDataParam("fileToUpload") 输入流文件,@FormDataParam("fileToUpload") FormDataContentDisposition fileDisposition,@FormDataParam("fromData") FormDataDto 数据) {//东西}
GitHub 链接
经过一番研究,发现相关问题1,看来 Spring 的 HiddenHttpMethodFilter
读取输入流,其中对于过滤器链下游的任何其他过滤器,将其留空.这就是我们在 Jersey 过滤器中收到错误请求的原因;因为实体流是空的.这是 Javadoc 中的注释
注意:在多部分 POST 请求的情况下,此过滤器需要在多部分处理后运行,因为它固有地需要检查 POST 正文参数.
所以我们需要做的是配置 Jersey 过滤器在这个 Spring 过滤器之前调用2.基于 Spring Boot 文档,我们可以使用一个属性轻松配置此过滤器的顺序.
spring.jersey.filter.order
做一个在Spring Boot repo中搜索HiddenHttpMethodFilter
,我们可以看到使用的子类Ordered>HiddenHttpMethodFilter-10000.所以我们想将 Jersey 过滤器的顺序设置为小于(更高的优先级).所以我们可以设置如下值
spring.jersey.filter.order=-100000
如果你现在测试它,它现在应该可以工作了.
我们需要修复的另一件事是 Spring RequestContextFilter
的顺序.这最初被配置为在 Jersey 过滤器之前被命令调用.当我们为 Jersey 过滤器设置上面的顺序配置时,RequestContextFilter
保持在它原来的位置.所以我们需要改变这一点.我们可以通过添加一个 bean 来覆盖原来的 bean 并设置顺序.
@Bean公共 RequestContextFilter requestContextFilter() {OrderedRequestContextFilter filter = new OrderedRequestContextFilter();filter.setOrder(-100001);回油过滤器;}
现在,如果我们在启动时检查日志,我们应该会看到我们想要的文件管理器顺序.
映射过滤器:'characterEncodingFilter'到:[/*]映射过滤器:'requestContextFilter' 到:[/*]映射过滤器:'jerseyFilter' 到 urls:[/*]映射过滤器:'hiddenHttpMethodFilter' 到:[/*]映射过滤器:'httpPutFormContentFilter'到:[/*]
<小时>
旁白
您需要将 Jersey 配置为过滤器的原因是静态内容.如果您没有为 Jersey 应用程序配置根路径,则它默认为 /*
,它将收集所有请求,包括静态内容的请求.所以当请求静态内容时 Jersey 会抛出 404 错误.我们将 Jersey 配置为过滤器,并告诉它转发它找不到的请求.
如果我们只为 Jersey 配置根路径,那么我们就不需要担心静态内容的这个问题,我们可以让 Jersey 默认配置为 servlet.
要更改 Jersey 应用程序的基本路径,我们可以将 @ApplicatuonPath
注释添加到我们的 ResourceConfig
或我们可以使用属性 spring.jersey.application-path
@Component@ApplicationPath("/api")公共类 JerseyConfig 扩展 ResourceConfig {...}
或在您的 application.properties
spring.jersey.application-path=/api
<小时>
另见
- Jersey 宁静网络服务中的文件与其他对象一起上传.这有一些关于如何在多部分请求中接受 JSON 作为正文部分的信息.
脚注
1.一些问题看[1、2 ]
<子>2.参见在过滤器链中更改RequestContextFilter的顺序
I'm using Spring boot v1.5.10 + Jersey v2.25.1, configured jersey as filter to access static folder files. I'm getting HTTP response 400 Bad Request for a service consuming MULTIPART_FORM_DATA
.
Props to configure Jersey as filter.
spring.jersey.type=filter
If I remove above property i.e., using Jersey as Servlet, the service is working, but I'm not able to access static folder.
Here is the controller,
@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
}
EDIT:
GitHub link https://github.com/sundarabalajijk/boot-jersey
When you start the app, spring.jersey.type=filter
http://localhost:8080/ (works)
http://localhost:8080/hello.html (works)
http://localhost:8080/save (not working) - used postman.
When spring.jersey.type=servlet
http://localhost:8080/ (works)
http://localhost:8080/hello.html (not working)
http://localhost:8080/save (works)
After some research and finding related issues1, it seems that Spring's HiddenHttpMethodFilter
reads the input stream, which leaves it empty for any other filters further down the filter chain. This is why we are getting a Bad Request in the Jersey filter; because the entity stream is empty. Here is the note from the 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.
So what we need to do is configure the Jersey filter to be called before this Spring filter2. Based on the Spring Boot docs, there is a property we can use to easily configure the order of this filter.
spring.jersey.filter.order
Doing a Github search in the Spring Boot repo for the HiddenHttpMethodFilter
, we can see the subclass that is used OrderedHiddenHttpMethodFilter, where the order is set to -10000
. So we want to set the order of our Jersey filter to less than that (higher precedence). So we can set the following value
spring.jersey.filter.order=-100000
If you test it now, it should now work.
One more thing we need to fix is the order of the Spring RequestContextFilter
. This is originally configured to be ordered to be called right before the Jersey filter. When we set the order configuration above for the Jersey filter, the RequestContextFilter
stays where it was originally at. So we need to change this. We can do this just by adding a bean to override the original one and set the order.
@Bean
public RequestContextFilter requestContextFilter() {
OrderedRequestContextFilter filter = new OrderedRequestContextFilter();
filter.setOrder(-100001);
return filter;
}
Now if we check the logs on startup, we should see the filer ordering we want.
Mapping filter: 'characterEncodingFilter' to: [/*]
Mapping filter: 'requestContextFilter' to: [/*]
Mapping filter: 'jerseyFilter' to urls: [/*]
Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
Mapping filter: 'httpPutFormContentFilter' to: [/*]
Aside
The reason you need to configure Jersey as a filter in your case is because of the static content. If you don't configure the root path for the Jersey app, then it defaults to /*
, which will hog up all requests, included those to the static content. So Jersey will throw 404 errors when requests are made for the static content. We configure Jersey as a filter and tell it to forward request that it cannot find.
If we were to just configure the root path for Jersey, then we would not need to worry about this problem with the static content, and we would be able to just leave Jersey configured as a servlet by default.
To change the base path for the Jersey app, we can either add the @ApplicatuonPath
annotation to our ResourceConfig
or we can use the property spring.jersey.application-path
@Component
@ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {
...
}
or in your application.properties
spring.jersey.application-path=/api
See also
- File upload along with other object in Jersey restful web service. This has some information on how you can accept JSON as a body part in the multipart request.
Footnotes
1. Some issues to look at [ 1, 2 ]
2. See Change order of RequestContextFilter in the filter chain
这篇关于Spring Boot + Jersey 类型过滤器 - 服务消耗 MULTIPART_FORM_DATA 的错误请求 400的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!