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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:40:34  来源:igfitidea点击:

How to get form parameters in request filter

javajerseyhttp-posthttp-requestjersey-1.0

提问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 HttpRequestContextyou can get the form parameters as follows: [broken link to Jersey 1.1.1 HttpRequestContext.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 Forminstead, 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 MyFilterand 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 getParameteron the org.apache.catalina.connector.RequestFacadeinstance that gets passed into javax.servlet.Filter.doFilter. So is this in fact a Tomcat bug?

我试图通过MyFilter在 Jersey 过滤器中删除和进行相同的调用(通过注入HttpServletRequest)来进一步简化问题- 但表单参数仍然出现。这个问题似乎是在调用传入getParameterorg.apache.catalina.connector.RequestFacade实例时特别发生的javax.servlet.Filter.doFilter。那么这实际上是 Tomcat 的错误吗?

The documentation of ServletRequest.getParametersays:

的文档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()or getReader()can interfere with the execution of this method.

如果参数数据是在请求正文中发送的,例如发生在 HTTP POST 请求中,则直接通过getInputStream()或读取正文getReader()可能会干扰此方法的执行。

So maybe the reverse is true too - that calling getParametermight 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 ResourceFilterFactorycreates an instance of ResourceFilterfor the TestResource#executemethod, which then creates a ContainerRequestFilterinstance:

确保您ResourceFilterFactory创建的实例ResourceFilterTestResource#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 traceyou have provided I am not sure whether your ContainerRequestFilteris 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 InputStreambefore. In such a case the entity input stream may no longer be available (but you can still inject @FormParaminto your resource method - as in this case).

还要确保没有其他 Servlet 或 Jersey 过滤器已经阅读了InputStream之前的内容。在这种情况下,实体输入流可能不再可用(但您仍然可以注入@FormParam您的资源方法 - 在这种情况下)。