Spring boot 无法再从HttpServletRequest SpringBoot 2.2、Jersey 2.29获取表单数据
我们有一个SpringBoot应用程序,正在使用Jersey来审核传入的HTTP请求 我们实现了一个ContainerRequestFilter来检索传入的HttpServletRequest 并使用HttpServletRequest的getParameterMap()方法提取查询和表单数据,并将其放在审计中 这与getParameterMap()的javadoc一致: “请求参数是随请求发送的额外信息。对于 HTTP servlet,参数包含在查询字符串中或已发布 表单数据。” 以下是有关过滤器的文档: 在更新SpringBoot时,我们发现getParameterMap()不再返回表单数据,但仍然返回查询数据 我们发现SpringBoot 2.1是支持我们代码的最后一个版本。在SpringBoot 2.2中,Jersey的版本更新为2.29,但在查看发行说明时,我们没有看到与此相关的任何内容 发生了什么变化?为了支持SpringBoot 2.2/Jersey 2.29,我们需要做哪些更改? 以下是我们代码的简化版本: 球衣请求过滤器-我们的过滤器Spring boot 无法再从HttpServletRequest SpringBoot 2.2、Jersey 2.29获取表单数据,spring-boot,jersey,jax-rs,jersey-2.0,spring-jersey,Spring Boot,Jersey,Jax Rs,Jersey 2.0,Spring Jersey,我们有一个SpringBoot应用程序,正在使用Jersey来审核传入的HTTP请求 我们实现了一个ContainerRequestFilter来检索传入的HttpServletRequest 并使用HttpServletRequest的getParameterMap()方法提取查询和表单数据,并将其放在审计中 这与getParameterMap()的javadoc一致: “请求参数是随请求发送的额外信息。对于 HTTP servlet,参数包含在查询字符串中或已发布 表单数据。” 以下是有关过
import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
...
@Provider
@Priority(Priorities.AUTHORIZATION)
public class JerseyRequestFilter implements ContainerRequestFilter {
@Context
private ResourceInfo resourceInfo;
@Context
private HttpServletRequest httpRequest;
...
public void filter(ContainerRequestContext context) throws IOException {
...
requestData = new RequestInterceptorModel(context, httpRequest, resourceInfo);
...
}
...
}
RequestInterceptorModel-地图中没有填充表单数据,只有查询数据
import lombok.Data;
import org.glassfish.jersey.server.ContainerRequest;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceInfo;
...
@Data
public class RequestInterceptorModel {
private Map<String, String[]> parameterMap;
...
public RequestInterceptorModel(ContainerRequestContext context, HttpServletRequest httpRequest, ResourceInfo resourceInfo) throws AuthorizationException, IOException {
...
setParameterMap(httpRequest.getParameterMap());
...
}
...
}
好的,经过大量的代码调试和github repos的挖掘,我发现了以下内容: 有一个过滤器,如果请求是
POST请求
,它将读取请求的body inputstream,使其无法进一步使用。这是隐藏的HttpMethodFilter
。但是,如果正文内容是application/x-www-form-urlencoded
,则此筛选器将正文内容放入请求parameterMap
请参阅此github问题:
此筛选器在spring boot 2.1.X中默认处于活动状态
因为这种行为在大多数情况下是不需要的,所以创建了一个属性来启用/禁用它,并且在SpringBoot2.2.X中,默认情况下它被停用
由于您的代码依赖于此筛选器,因此可以通过以下属性启用它:
spring.mvc.hiddenmethod.filter.enabled=true
我在本地测试了它,它对我有效
编辑:
以下是使此解决方案起作用的原因:
电话
protectedvoidDoFilterInternal(HttpServletRequest请求、HttpServletResponse响应、FilterChain FilterChain)
抛出ServletException、IOException{
HttpServletRequest-requestToUse=请求;
if(“POST.equals(request.getMethod())&&request.getAttribute(WebUtils.ERROR\u EXCEPTION\u ATTRIBUTE)==null){
字符串paramValue=request.getParameter(this.methodParam);
...
request.getParameter
检查参数是否已被解析,如果不是,则执行此操作。此时,尚未调用请求正文输入流,因此请求也需要解析正文:
protectedvoid parseParameters(){
参数sparsed=true;
Parameters=coyoteRequest.getParameters();
布尔成功=假;
试一试{
...
//这是解析实际查询参数的位
parameters.handleQueryParameters();
//此处usingInputStream为false,因此也将解析主体
if(使用输入流| |使用读取器){
成功=真实;
返回;
}
…//实际的正文解析在这里完成
问题是,在这个场景中,usingInputStream
是false,因此该方法在解析查询参数后不会返回。
只有在第一次检索请求正文的输入流时,usingInputStream
才设置为true。这只有在我们从过滤器链的末尾退出并为请求提供服务后才能完成。当jersey在中初始化ContainerRequest
时,才会调用inputStream
private void initContainerRequest(
最终ContainerRequestContext,
最终HttpServletRequest servletRequest,
最终HttpServletResponse servletResponse,
最终响应编写器(ResponseWriter)引发IOException{
requestContext.setEntityStream(servletRequest.getInputStream());
...
由于
hiddenhttmpmethodfilter
是访问参数的唯一筛选器,因此没有此筛选器,在调用request.getParameterMap()之前,永远不会解析参数RequestInterceptorModel
中的
。但当时,请求主体的inputStream已经被访问,因此它我将发布此答案,即使如此。原因是我不太清楚该解决方案为什么有效。当然,我更希望有一个只需要向属性文件添加属性的解决方案,即它反对像我的解决方案那样修改代码。但是我不确定我是否适合使用一个与我的逻辑认为它应该如何工作相反的解决方案。我的意思是。在您当前的代码(SBv 2.1.15)中,如果您提出请求,请查看日志,您将看到一个Jersey日志
2020-12-15 11:43:04.858警告5045---[nio-8012-exec-1]o.g.j.s.WebComponent:对URI的servlet请求http://localhost:8012/api/jerseyBody/ping
在请求正文中包含表单参数,但请求正文已被访问请求的servlet或servlet过滤器使用
git clone https://github.com/fei0x/so-jerseyBodyIssue
mvn -Prun
curl -X POST \
http://localhost:8012/api/jerseyBody/ping \
-H 'content-type: application/x-www-form-urlencoded' \
-d param=Test%20String
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.15.RELEASE</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.9.RELEASE</version>
mvn -Prun
curl -X POST \
http://localhost:8012/api/jerseyBody/ping \
-H 'content-type: application/x-www-form-urlencoded' \
-d param=Test%20String
spring.mvc.hiddenmethod.filter.enabled=true
public ServletInputStream getInputStream() throws IOException {
...
usingInputStream = true;
...