Как получить тело запроса от 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();
    }

}

Мой класс 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 в методе TestEndointlistar, его null или empty? @FDB

Vishrant 02.05.2018 20:29

@Vishrant пустая строка

FDB 02.05.2018 21:27

Я не уверен, но есть ли в HttpHandler перенаправитель запросов? если да, то попробуйте это. Проверьте, есть ли другие перегруженные методы для handleRequest, куда вы можете добавить свой заголовок.

Vishrant 02.05.2018 22:24

Попробуйте отладить и посмотреть, где именно установлено тело запроса.

Vishrant 02.05.2018 22:25

Я поделился своим тестовым проектом здесь .... github.com/fabiodelabruna/undertow-server-test

FDB 03.05.2018 01:47

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

LuisFMorales 05.10.2018 07:51
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
9
6
3 772
3

Ответы 3

Как я сказал в своем комментарии ниже, похоже, что ваша проблема с реализацией связана с вашим методом, который преобразует ваш 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()));
}

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

Во-первых, вы должны знать, что 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, поэтому вы должны реализовать его с помощью отражения。

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

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

Другие вопросы по теме