如何从 HttpServerExchange 获取请求正文?



我创建了一个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();
}
}

我的日志记录处理程序类:

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()));
}
}
}

我的测试端点类:

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());
}
}

多谢!

正如我在下面的评论中所说,您的实现问题似乎在于将inputStream转换为字符串的方法。一旦你关闭了缓冲阅读器,它就会关闭位于交易所内部的输入流。看看这个问题:BufferedReader 和 InputStreamReader 应该显式关闭吗?

一个简单的解决方案应该是不要过度关闭 BufferedStream(或避免使用 try with 资源块(:

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()));
}

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

输入流存储在交换中,由处理程序共享。在一个处理程序中读取后,无法在下一个处理程序中重新读取它。
相反,您可以将已读到的内容作为附件放入交换中。因此,您可以直接获取它,而无需再次重读,这是低效的。

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

首先,你应该知道输入流不应该在java中被重复读取。例如,ByteArrayInputStream不能被读取可重复,我们都知道实现可重复的inputStream很容易,但是jdk没有实现。我想这遵循了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 方法,所以你必须通过反映来实现。

相关内容

  • 没有找到相关文章

最新更新