4

我完全无法在 JaxRS ContainerRequestFilter 中获取请求负载/表单。

我的设置:

  • JDK 8
  • SpringBoot 1.3.0.RELEASE
  • Jersey 2.22.1(来自 SpringBoot)

这是我的 pom :(来自 Spring Initialzr)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jersey</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

这是我的应用程序类:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

这是我的 JerseyConfig:

@Configuration
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(HelloController.class);
        register(MyFilter.class);
    }
}

这是我的 HelloController:

@Component
@Path("/hello")
public class HelloController {

    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @POST
    public String onHelloRequest(@FormParam("foo") String foo) {
        System.out.println(foo);
        return foo + "bar";
    }
}

这是我遇到的问题的核心部分,ContainerRequestFilter:

public class MyFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext ctx) throws IOException {

        System.err.println("I'm in the filter");

        // Solution #1 doesn't work
        if (ctx instanceof ContainerRequest) {
            ContainerRequest request = (ContainerRequest) ctx;

            request.bufferEntity();
            Form f = request.readEntity(Form.class);
            System.err.println(f.asMap().toString());
        }

        // Solution #2 doesn't work either
        InputStream inputStream = ctx.getEntityStream();
        StringBuilder textBuilder = new StringBuilder();
        try (Reader reader = new BufferedReader(new InputStreamReader
                (inputStream, Charset.forName(StandardCharsets.UTF_8.name())))) {
            int c = 0;
            while ((c = reader.read()) != -1) {
                textBuilder.append((char) c);
            }
        }
        System.err.println(textBuilder.toString());
    }
}

如您所见,这是一个使用 Jersey 的非常精简的 SpringBoot 示例。但是,看起来 ContainerRequestFilter 中的 InputStream 已被使用。我用 Javax ServletFilter 试过了,我也遇到了同样的问题。

Spring-web 是否有可能在调用 Jersey 之前使用 InputStream?

请帮我。

编辑

为了测试它,我使用了 POSTMAN 并发送了:

发布http://localhost:8080/hello

标题:

内容类型 = 应用程序/x-www-form-urlencoded

有效载荷:

富=酒吧

我的回应是

野蛮人

我的控制台输出是

我在过滤器中

{} // 解决方案 #1 <- 应该类似于 foo=bar

// 解决方案 #2 <- 一个空字符串

酒吧

编辑2

我在控制台中也有该消息,即使没有任何自定义 Filter :

2015-11-21 20:14:05.438 WARN 4440 --- [nio-8080-exec-2] o.glassfish.jersey.servlet.WebComponent:对 URI http://localhost:8080/hello的 servlet 请求包含请求正文中的表单参数,但请求正文已被 servlet 或访问请求参数的 servlet 过滤器使用。只有使用 @FormParam 的资源方法才能按预期工作。通过其他方式消耗请求正文的资源方法将无法按预期工作。

在将 InputStream 传递给 Jersey 之前,它肯定看起来像其他东西消耗了 InputStream,使每个 Jax-RS Filters|Interceptors 都没有状态。

4

1 回答 1

1

这里是“解决方案”

在 SpringBoot 中,如果添加 starter-web 模块,它将添加 spring:webmvc,这似乎与 JaxRS 和 Servlet 过滤器不兼容。如果您使用 SpringBoot 和 Jersey,请确保 webmvc jar 不在类路径中。

Spring 堆栈将消耗 Servlet 的 Request InputStream 并且管道中的每个向下拦截器/过滤器都将一无所有。

这是在 Servlet 过滤器中获取请求有效负载的工作代码片段

public MyFilter implements Filter
{


    @Override
    public final void doFilter(
        final ServletRequest request,
        final ServletResponse response,
        final FilterChain chain)
        throws IOException,
        ServletException {


        getRawFormPayload((HttpServletRequest) request);
        chain.doFilter(request, response);
    }

     protected String getRawFormPayload(HttpServletRequest request) {

        final int contentLength = request.getContentLength();
        final StringBuilder payloadBuilder = new StringBuilder(contentLength);
        try {
            final InputStream inputStream = request.getInputStream();
            final BufferedReader reader =
                new BufferedReader(new InputStreamReader(inputStream, request.getCharacterEncoding()));

            // Buffer should not be higher than the content-length of the request
            reader.mark(contentLength + 1);
            final char[] charBuffer = new char[contentLength + 1];
            int bytesRead = -1;

            while ((bytesRead = reader.read(charBuffer)) > 0) {
                payloadBuilder.append(charBuffer, 0, bytesRead);
            }

            // Reset the buffer so the next Filter/Interceptor have unaltered InputStream
            reader.reset();

        } catch (final IOException e) {
            this.LOG.error(e.getMessage(), e);
        }
        return payloadBuilder.toString();
    }
}
于 2015-11-24T20:31:25.013 回答