Java 如何在请求过滤器中获取表单参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18138876/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get form parameters in request filter
提问by Paul Bellora
I'm trying to get the form parameters of a request in a request filter:
我试图在请求过滤器中获取请求的表单参数:
@Override
public ContainerRequest filter(final ContainerRequest request) {
final Form formParameters = request.getFormParameters();
//logic
return request;
}
However, the form always seems to be empty. The HttpRequestContext.getFormParameters()
documentation says:
然而,表格似乎总是空的。该HttpRequestContext.getFormParameters()
文件说:
Get the form parameters of the request entity.
This method will ensure that the request entity is buffered such that it may be consumed by the applicaton.
Returns:the form parameters, if there is a request entity and the content type is "application/x-www-form-urlencoded", otherwise an instance containing no parameters will be returned.
获取请求实体的表单参数。
此方法将确保请求实体被缓冲,以便应用程序可以使用它。
返回:表单参数,如果有请求实体,内容类型为“application/x-www-form-urlencoded”,否则返回一个不包含参数的实例。
My resource is annotated with @Consumes("application/x-www-form-urlencoded")
, although it won't have been matched until after the request filter - is that why this isn't working?
我的资源用 注释@Consumes("application/x-www-form-urlencoded")
,尽管它在请求过滤器之后才会匹配 - 这就是为什么这不起作用?
I tried doing some research but couldn't find any conclusive evidence of whether this is possible. There was this 4-year old discussion, in which Paul Sandoz says:
我尝试做一些研究,但找不到任何确凿的证据证明这是否可行。有一个 4 年前的讨论,其中 Paul Sandoz 说:
If you are working in Jersey filters or with the
HttpRequestContext
you can get the form parameters as follows: [broken link to Jersey 1.1.1HttpRequestContext.getFormParameters
]
如果您正在使用 Jersey 过滤器或使用 ,
HttpRequestContext
您可以获得如下表单参数:[断开的 Jersey 1.1.1 链接HttpRequestContext.getFormParameters
]
I also found this 3-year-old discussionabout how to get multipart/form-data form fields in a request filter. In it, Paul Sandoz uses the following code:
我还发现了这个关于如何在请求过滤器中获取多部分/表单数据表单字段的3 年前的讨论。在其中,Paul Sandoz 使用了以下代码:
// Buffer
InputStream in = request.getEntityInputStream();
if (in.getClass() != ByteArrayInputStream.class) {
// Buffer input
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ReaderWriter.writeTo(in, baos);
} catch (IOException ex) {
throw new ContainerException(ex);
}
in = new ByteArrayInputStream(baos.toByteArray());
request.setEntityInputStream(in);
}
// Read entity
FormDataMultiPart multiPart = request.getEntity(FormDataMultiPart.class);
I tried emulating that approach for Form
instead, but the result of request.getEntityInputStream()
is always an empty stream. And looking at the source of getFormParameters
, that method is in fact doing the same thing already:
我尝试模拟这种方法Form
,但结果request.getEntityInputStream()
总是一个空流。查看的来源getFormParameters
,该方法实际上已经在做同样的事情:
@Override
public Form getFormParameters() {
if (MediaTypes.typeEquals(MediaType.APPLICATION_FORM_URLENCODED_TYPE, getMediaType())) {
InputStream in = getEntityInputStream();
if (in.getClass() != ByteArrayInputStream.class) {
// Buffer input
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ReaderWriter.writeTo(in, byteArrayOutputStream);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
in = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
setEntityInputStream(in);
}
ByteArrayInputStream byteArrayInputStream = (ByteArrayInputStream) in;
Form f = getEntity(Form.class);
byteArrayInputStream.reset();
return f;
} else {
return new Form();
}
}
I can't figure out what's slurping up the entity input stream before I get to it. Something in Jersey must be consuming it because the form params are later passed into the resource method. What am I doing wrong here, or is this impossible (and why)?
在我开始之前,我无法弄清楚是什么导致了实体输入流。Jersey 中的某些东西必须使用它,因为表单参数稍后会传递到资源方法中。我在这里做错了什么,或者这是不可能的(为什么)?
EDIT:Here's an example of a request being sent:
编辑:这是发送请求的示例:
POST /test/post-stuff HTTP/1.1
Host: local.my.application.com:8443
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
form_param_1=foo&form_param_2=bar
Here's the (somewhat redundant) request logging:
这是(有些多余的)请求日志记录:
INFO: 1 * Server in-bound request
1 > POST https://local.my.application.com:8443/test/post-stuff
1 > host: local.my.application.com:8443
1 > connection: keep-alive
1 > content-length: 33
1 > cache-control: no-cache
1 > origin: chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
1 > user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
1 > content-type: application/x-www-form-urlencoded
1 > accept: */*
1 > accept-encoding: gzip,deflate,sdch
1 > accept-language: en-US,en;q=0.8
1 > cookie: [omitted]
1 >
Here are the response headers of that request, including the Jersey Trace:
以下是该请求的响应标头,包括Jersey Trace:
Content-Type →application/json;charset=UTF-8
Date →Fri, 09 Aug 2013 18:00:17 GMT
Location →https://local.my.application.com:8443/test/post-stuff/
Server →Apache-Coyote/1.1
Transfer-Encoding →chunked
X-Jersey-Trace-000 →accept root resource classes: "/post-stuff"
X-Jersey-Trace-001 →match path "/post-stuff" -> "/post\-stuff(/.*)?", [...], "(/.*)?"
X-Jersey-Trace-002 →accept right hand path java.util.regex.Matcher[pattern=/post\-stuff(/.*)? region=0,11 lastmatch=/post-stuff]: "/post-stuff" -> "/post-stuff" : ""
X-Jersey-Trace-003 →accept resource: "post-stuff" -> @Path("/post-stuff") com.application.my.jersey.resource.TestResource@7612e9d2
X-Jersey-Trace-004 →match path "" -> ""
X-Jersey-Trace-005 →accept resource methods: "post-stuff", POST -> com.application.my.jersey.resource.TestResource@7612e9d2
X-Jersey-Trace-006 →matched resource method: public javax.ws.rs.core.Response com.application.my.jersey.resource.TestResource.execute(java.lang.String,java.lang.String)
X-Jersey-Trace-007 →matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@b98df1f
X-Jersey-Trace-008 →matched message body writer: java.lang.String@f62, "application/json" -> com.sun.jersey.core.impl.provider.entity.StringProvider@1c5ddffa
Here is the (unremarkable) servlet config:
这是(不起眼的)servlet 配置:
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.application.my.jersey</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.application.my.jersey.MyFilterFactory</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.feature.Trace</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Here's the example resource:
这是示例资源:
@Path("/post-stuff")
@Produces(MediaType.APPLICATION_JSON)
public final class TestResource {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response execute(
@FormParam("form_param_1") final String formParam1,
@FormParam("form_param_2") final String formParam2
) {
return Response.created(URI.create("/")).entity("{}").build();
}
}
I'm using Jersey 1.17.
我正在使用泽西 1.17。
For those interested, I'm trying to roll my own required parameter validation, as described in JERSEY-351. My solution hereworked for query, cookie, and header params - form params are holding out on me.
对于那些感兴趣的人,我正在尝试推出自己所需的参数验证,如JERSEY-351 中所述。我的解决方案在这里工作了查询,饼干,和头PARAMS -形式PARAMS都瞒着我。
采纳答案by Paul Bellora
This was a tricky one. I'd removed other Jersey filters to eliminate them from the problem, but missed a plain servlet filter hiding at the bottom of web.xml
:
这是一个棘手的问题。我删除了其他 Jersey 过滤器以消除它们的问题,但错过了隐藏在底部的普通 servlet 过滤器web.xml
:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.application.my.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Removing this filter fixed the issue - form params showed up in the Jersey filter. But why? I dug deeper, narrowing down the problem to a single statement in MyFilter
:
删除这个过滤器解决了这个问题——表单参数出现在 Jersey 过滤器中。但为什么?我深入挖掘,将问题缩小到以下语句中的单个语句MyFilter
:
request.getParameter("some_param")
I tried to simplify the problem even more by removing MyFilter
and making the same call in the Jersey filter (by injecting HttpServletRequest
) - but the form parameters still showed up. The issue appears to happen specifically when calling getParameter
on the org.apache.catalina.connector.RequestFacade
instance that gets passed into javax.servlet.Filter.doFilter
. So is this in fact a Tomcat bug?
我试图通过MyFilter
在 Jersey 过滤器中删除和进行相同的调用(通过注入HttpServletRequest
)来进一步简化问题- 但表单参数仍然出现。这个问题似乎是在调用传入getParameter
的org.apache.catalina.connector.RequestFacade
实例时特别发生的javax.servlet.Filter.doFilter
。那么这实际上是 Tomcat 的错误吗?
The documentation of ServletRequest.getParameter
says:
的文档ServletRequest.getParameter
说:
If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via
getInputStream()
orgetReader()
can interfere with the execution of this method.
如果参数数据是在请求正文中发送的,例如发生在 HTTP POST 请求中,则直接通过
getInputStream()
或读取正文getReader()
可能会干扰此方法的执行。
So maybe the reverse is true too - that calling getParameter
might be allowed to interfere with the entity input stream? It's unclear to me whether the method's contract allows for this behavior, and whether it indicates a bug in Tomcat, Jersey, or neither.
所以也许反过来也是如此 -getParameter
可能允许调用干扰实体输入流?我不清楚该方法的契约是否允许这种行为,以及它是否表明 Tomcat、Jersey 中存在错误,或者两者都没有。
Anyway, that old filter wasn't actually needed so my issue is solved but just removing it.
无论如何,实际上不需要那个旧过滤器,所以我的问题解决了,只是将其删除。
Here's a full reproduction of the problem (Tomcat 7.0):
这是问题的完整再现(Tomcat 7.0):
web.xml
:
web.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>test</display-name>
<servlet>
<servlet-name>jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.application.my</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.application.my.TestFilterFactory</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.feature.Trace</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>servletFilter</filter-name>
<filter-class>com.application.my.TestServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>servletFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
TestServletFilter.java
:
TestServletFilter.java
:
package com.application.my;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public final class TestServletFilter implements Filter {
@Override
public void init(FilterConfig config) { }
@Override
public void doFilter(
final ServletRequest request,
final ServletResponse response,
final FilterChain chain
) throws IOException, ServletException {
System.out.println("calling getParameter on " + request.getClass().getName());
request.getParameter("blah");
chain.doFilter(request, response);
}
@Override
public void destroy() { }
}
TestFilterFactory.java
:
TestFilterFactory.java
:
package com.application.my;
import java.util.Collections;
import java.util.List;
import com.sun.jersey.api.model.AbstractMethod;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import com.sun.jersey.spi.container.ResourceFilter;
import com.sun.jersey.spi.container.ResourceFilterFactory;
public final class TestFilterFactory implements ResourceFilterFactory {
@Override
public List<ResourceFilter> create(final AbstractMethod method) {
return Collections.<ResourceFilter>singletonList(new ResourceFilter() {
@Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
@Override
public ContainerRequest filter(final ContainerRequest request) {
System.out.println("form: " + request.getFormParameters());
return request;
}
};
}
@Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
});
}
}
TestResource.java
:
TestResource.java
:
package com.application.my;
import java.net.URI;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/post-stuff")
@Produces(MediaType.APPLICATION_JSON)
public final class TestResource {
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response execute(
@FormParam("form_param_1") final String formParam1,
@FormParam("form_param_2") final String formParam2
) {
System.out.println("form param_1: " + formParam1);
System.out.println("form param_2: " + formParam2);
return Response.created(URI.create("/")).entity("{}").build();
}
}
回答by Michal Gajdos
Make sure your ResourceFilterFactory
creates an instance of ResourceFilter
for the TestResource#execute
method, which then creates a ContainerRequestFilter
instance:
确保您ResourceFilterFactory
创建的实例ResourceFilter
的TestResource#execute
方法,然后创建一个ContainerRequestFilter
实例:
public class MyFilterFactory implements ResourceFilterFactory {
@Override
public List<ResourceFilter> create(final AbstractMethod am) {
return new ArrayList<ResourceFilter>() {{
add(new ResourceFilter() {
@Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
@Override
public ContainerRequest filter(final ContainerRequest request) {
System.out.println(request.getFormParameters());
return request;
}
};
}
@Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
});
}};
}
}
From the trace
you have provided I am not sure whether your ContainerRequestFilter
is called. There should be one more trace header containing something like this:
从trace
您提供的信息来看,我不确定您是否ContainerRequestFilter
被调用。应该还有一个包含如下内容的跟踪标头:
→matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@b98df1f
The whole trace from my test:
我的测试的整个跟踪:
HTTP/1.1 201 Created
Location: http://localhost:8080/helloworld-webapp/helloworld/
Content-Type: text/plain
X-Jersey-Trace-000: accept root resource classes: "/helloworld"
X-Jersey-Trace-001: match path "/helloworld" -> "/application\.wadl(/.*)?", "/helloworld(/.*)?"
X-Jersey-Trace-002: accept right hand path java.util.regex.Matcher[pattern=/helloworld(/.*)? region=0,11 lastmatch=/helloworld]: "/helloworld" -> "/helloworld" : ""
X-Jersey-Trace-003: accept resource: "helloworld" -> @Path("/helloworld") com.sun.jersey.samples.helloworld.resources.HelloWorldResource@7449df0f
X-Jersey-Trace-004: match path "" -> ""
X-Jersey-Trace-005: accept resource methods: "helloworld", POST -> com.sun.jersey.samples.helloworld.resources.HelloWorldResource@7449df0f
X-Jersey-Trace-006: matched resource method: public javax.ws.rs.core.Response com.sun.jersey.samples.helloworld.resources.HelloWorldResource.execute(java.lang.String,java.lang.String)
X-Jersey-Trace-007: matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@6bc1b916
X-Jersey-Trace-008: matched message body reader: class com.sun.jersey.api.representation.Form, "application/x-www-form-urlencoded" -> com.sun.jersey.core.impl.provider.entity.FormProvider@6bc1b916
X-Jersey-Trace-009: matched message body writer: java.lang.String@f62, "text/plain" -> com.sun.jersey.core.impl.provider.entity.StringProvider@4aae6c4e
Transfer-Encoding: chunked
Server: Jetty(6.1.24)
EDIT 1:
编辑 1:
Enable request LoggingFilter
:
启用请求LoggingFilter
:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
</init-param>
EDIT 2:
编辑2:
Also make sure no other Servlet or Jersey filter has read the InputStream
before. In such a case the entity input stream may no longer be available (but you can still inject @FormParam
into your resource method - as in this case).
还要确保没有其他 Servlet 或 Jersey 过滤器已经阅读了InputStream
之前的内容。在这种情况下,实体输入流可能不再可用(但您仍然可以注入@FormParam
您的资源方法 - 在这种情况下)。