【问题标题】:How do I get a request body from HttpServerExchange?如何从 HttpServerExchange 获取请求正文?
【发布时间】:2018-10-12 22:16:40
【问题描述】:

我创建了一个 Undertow 服务器和一个处理程序来记录请求。 我在检索HttpServerExchange 的请求正文时遇到问题。

LoggingHandler 课堂上,我的身体没有问题。但是在TestEndpoint,身体是空的。

如果我删除在LoggingHandler 处检索请求正文的行,则正文将填充到TestEndpoint

有人知道怎么做吗?

我的服务器类:

package com.undertow.server;

import com.undertow.server.endpoints.TestEndpoint;

import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;

import io.undertow.Undertow;
import io.undertow.Undertow.Builder;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.BlockingHandler;
import io.undertow.servlet.api.DeploymentInfo;

public class UndertowServer {

    private UndertowJaxrsServer server;

    public UndertowServer() {
        this.server = new UndertowJaxrsServer();
    }

    public void start() {
        Builder builder = Undertow.builder().addHttpListener(8000, "0.0.0.0");
        this.server.start(builder);
        this.configureEndpoints();
    }

    private void configureEndpoints() {
        ResteasyDeployment deployment = new ResteasyDeployment();
        deployment.getActualResourceClasses().add(TestEndpoint.class);

        DeploymentInfo deploymentInfo = this.server.undertowDeployment(deployment) //
                .setClassLoader(ClassLoader.getSystemClassLoader()).setContextPath("/gateway/") //
                .setDeploymentName("gateway.war");

        deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() {
            @Override
            public HttpHandler wrap(HttpHandler handler) {
                return new BlockingHandler(new LoggingHandler(handler));
            }
        });

        this.server.deploy(deploymentInfo);
    }

    public static void main(String[] args) {
        new UndertowServer().start();
    }

}

我的 LoggingHandler 类:

package com.undertow.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

import org.apache.log4j.Logger;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

public class LoggingHandler implements HttpHandler {

    private static Logger LOGGER = Logger.getLogger(LoggingHandler.class);

    private final HttpHandler next;

    public LoggingHandler(final HttpHandler next) {
        this.next = next;
    }

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        LOGGER.info(toString(exchange.getInputStream()).trim());
        this.next.handleRequest(exchange);
    }

    private String toString(InputStream is) throws IOException {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
            return br.lines().collect(Collectors.joining(System.lineSeparator()));
        }
    }

}

我的 TestEndpoint 类:

package com.undertow.server.endpoints;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.log4j.Logger;

@Path("/person")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class TestEndpoint {

    private static Logger LOGGER = Logger.getLogger(TestEndpoint.class);

    @POST
    @Path("/add")
    public void listar(@Suspended AsyncResponse response, String body) {
        LOGGER.info(body.trim());
        response.resume(Response.ok().build());
    }

}

非常感谢!

【问题讨论】:

  • 所以你的意思是String body in TestEndoint listar 方法是null 还是empty? @FDB
  • @Vishrant 空字符串
  • 我不确定,但HttpHandler 中有请求转发器吗?如果是这样试试。检查handleRequest 是否还有其他重载方法,您可以在其中添加标题。
  • 尝试调试并查看请求正文的具体设置位置。
  • 我在这里分享了我的测试项目......github.com/fabiodelabruna/undertow-server-test

标签: java rest resteasy httphandler undertow


【解决方案1】:

正如我在下面的评论中所说,您的实现问题似乎在于您将 inputStream 转换为字符串的方法。关闭 BufferedReader 后,它会关闭位于交换器内的 inputStream。看看这个问题:Should BufferedReader and InputStreamReader be closed explicitly?

一个简单的解决方案应该是不要显式关闭 BufferedStream(或避免尝试使用资源块):

private String toString(InputStream is) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
    return br.lines().collect(Collectors.joining(System.lineSeparator()));
}

预期关闭流的元素是创建它的元素,因此在这种情况下,您可以利用它来安全地关闭流。

【讨论】:

    【解决方案2】:

    InputStream 存储在交换中,由处理程序共享。一旦您在一个处理程序中读取,您将无法在下一个处理程序中重新读取它。
    相反,您可以将阅读的内容作为附件存储在交换中。这样就可以直接拿到了,不需要重读,效率低。

        public static final AttachmentKey<Object> REQUEST_BODY = AttachmentKey.create(Object.class);
        exchange.putAttachment(REQUEST_BODY, toString(exchange.getInputStream()).trim());
    

    【讨论】:

      【解决方案3】:

      首先你要知道java中inputStream不应该是readrepeatable,比如ByteArrayInputStream是readrepeatable的猜猜遵循InputStream的统一标准。

      /** 
       * Reads the next byte of data from the input stream. The value byte is 
       * returned as an <code>int</code> in the range <code>0</code> to 
       * <code>255</code>. If no byte is available because the end of the stream 
       * has been reached, the value <code>-1</code> is returned. This method 
       * blocks until input data is available, the end of the stream is detected, 
       * or an exception is thrown. 
       * 
       * <p> A subclass must provide an implementation of this method. 
       * 
       * @return     the next byte of data, or <code>-1</code> if the end of the 
       *             stream is reached. 
       * @exception  IOException  if an I/O error occurs. 
       */  
      public abstract int read() throws IOException;  
      

      解决问题的方法是先读取流并缓存数据,执行handleRequest方法后将输入流写入HttpServerExchange。但是HttpServerExchange没有setInputStream方法,所以必须通过反映。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-16
        • 2016-03-13
        • 2021-03-07
        • 2019-11-28
        • 2011-12-27
        • 2014-06-14
        • 2018-04-21
        • 1970-01-01
        相关资源
        最近更新 更多