Как получить тело запроса от HttpServerExchange? - PullRequest
0 голосов
/ 02 мая 2018

Я создал сервер 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());
    }

}

Большое спасибо!

Ответы [ 3 ]

0 голосов
/ 10 октября 2018

Сначала вы должны знать, что inputStream не должен читаться повторяемым в 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;  

Метод решения проблемы - сначала прочитать поток и кешировать данные, записать входной поток в HttpServerExchange после выполнения метода handleRequest. Но HttpServerExchange не имеет метода setInputStream, поэтому его необходимо реализовать с помощью поразмышлять.

0 голосов
/ 08 февраля 2019

InputStream хранится в обмене, который разделяется обработчиками. Когда вы читаете в одном обработчике, вы не можете перечитать его в следующем обработчике.
Вместо этого вы можете поместить то, что вы прочитали, как вложение, которое хранится на бирже. Таким образом, вы можете получить его напрямую, вам не нужно перечитывать его снова, что неэффективно.

    public static final AttachmentKey<Object> REQUEST_BODY = AttachmentKey.create(Object.class);
    exchange.putAttachment(REQUEST_BODY, toString(exchange.getInputStream()).trim());
0 голосов
/ 05 октября 2018

Как я уже сказал в своем комментарии ниже, похоже, что ваша проблема реализации в том, что ваш метод преобразует ваш inputStream в String. Как только вы закрываете BufferedReader, он закрывает ваш inputStream, который находится внутри вашего обмена. Посмотрите на этот вопрос: Должны ли BufferedReader и InputStreamReader быть закрыты явно?

Простое решение должно заключаться в том, чтобы не закрывать 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()));
}

Ожидается, что поток закроет тот элемент, который был создан, поэтому в этом случае вы можете использовать его для безопасного закрытия для откатки.

...