我正在尝试在请求过滤器中获取请求的表单参数:
@Override
public ContainerRequest filter(final ContainerRequest request) {
final Form formParameters = request.getFormParameters();
//logic
return request;
}
然而,表格似乎总是空的。 HttpRequestContext.getFormParameters()
文档说:
获取请求实体的表单参数。
此方法将确保缓冲请求实体,以便应用程序可以使用它。
返回: 表单参数,如果有请求实体且内容类型为“application / x-www-form-urlencoded”,否则将返回不包含参数的实例。
我的资源使用@Consumes("application/x-www-form-urlencoded")
注释,虽然在请求过滤器之后才会匹配 - 这就是为什么这不起作用?
我尝试做一些研究,但未能找到任何确凿的证据证明这是否可行。有this 4-year old discussion,Paul Sandoz说:
如果您正在使用泽西过滤器或使用
HttpRequestContext
,您可以获得以下表单参数:[到Jersey 1.1.1HttpRequestContext.getFormParameters
的断开链接]
我还发现this 3-year-old discussion有关如何在请求过滤器中获取multipart / form-data表单字段。其中,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);
我尝试使用Form
模仿该方法,但request.getEntityInputStream()
的结果始终为空流。看看the source of 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();
}
}
在我达到它之前,我无法弄清楚实体输入流是什么。泽西岛的某些东西必须消耗它,因为形式参数后来被传递到资源方法中。我在这里做错了什么,或者这是不可能的(以及为什么)?
编辑:以下是发送请求的示例:
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
这是(有点多余)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 >
以下是该请求的响应标头,包括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
这是(不起眼的)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>
以下是示例资源:
@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();
}
}
我正在使用Jersey 1.17。
对于那些感兴趣的人,我正在尝试推出自己的必需参数验证,如JERSEY-351中所述。我的解决方案here适用于查询,cookie和标题参数 - 形式参数对我很有用。
答案 0 :(得分:2)
这是一个棘手的问题。我删除了其他泽西过滤器以消除它们的问题,但错过了隐藏在web.xml
底部的普通servlet过滤器:
<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>
删除此过滤器修复了问题 - 表格params出现在Jersey过滤器中。但为什么?我深入挖掘,将问题缩小到MyFilter
中的单个陈述:
request.getParameter("some_param")
我尝试通过删除MyFilter
并在Jersey过滤器中进行相同的调用(通过注入HttpServletRequest
)来进一步简化问题 - 但表单参数仍然显示出来。在调用传递到javax.servlet.Filter.doFilter
的getParameter
实例上调用org.apache.catalina.connector.RequestFacade
时,问题似乎就会发生。这实际上是Tomcat的错误吗?
ServletRequest.getParameter
的文档说:
如果参数数据是在请求正文中发送的,例如HTTP POST请求,则直接通过
getInputStream()
或getReader()
读取正文会干扰此方法的执行。< / p>
所以也许反过来也是这样 - 调用getParameter
可能会被允许干扰实体输入流?我不清楚方法的契约是否允许这种行为,以及它是否表示Tomcat,Jersey或其中的错误。
无论如何,那个旧的过滤器实际上并不需要,所以我的问题解决了,但只是删除它。
这是问题的完整复制品(Tomcat 7.0):
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
:
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
:
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
:
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();
}
}
答案 1 :(得分:1)
确保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;
}
});
}};
}
}
从您提供的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
我测试的全部痕迹:
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)
启用请求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>
还要确保之前没有其他Servlet或Jersey过滤器已读取InputStream
。在这种情况下,实体输入流可能不再可用(但您仍然可以将@FormParam
注入资源方法 - 如本例所示。)