Как добавить собственное сообщение после кода состояния HTTP

Я использую Spring Boot в качестве веб-сервера и иногда хочу отвечать [HTTP / 1.1 403 XXX].

Я пробовал ResponseEntity с @ResponseStatus и response.sendError(403, "XXX"), но я обнаружил, что пакет сниффера нашел только ответ веб-сервера [HTTP / 1.1 403]

Пример CURL:

[root@localhost ~]# curl -v 192.168.12.36:8080/test
* About to connect() to 192.168.12.36 port 8080 (#0) 
*   Trying 192.168.12.36...
* Connected to 192.168.12.36 (192.168.12.36) port 8080 (#0)
> GET /test HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.12.36:8080
> Accept: */*
> 
< HTTP/1.1 403 
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Thu, 09 Aug 2018 08:26:37 GMT
< 
* Connection #0 to host 192.168.12.36 left intact

Мне нужен ответ:

< HTTP/1.1 403 XXX
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
...

Как я могу добавить собственное сообщение после кода состояния HTTP?

Спасибо @john за исправление моего вопроса.

ajianzheng 09.08.2018 12:25
0
1
5 472
3

Ответы 3

Вы можете указать причину в аннотации @ResponseStatus:

@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "You custom reason")

не работает, ответное сообщение выглядит следующим образом: <HTTP / 1.1 403 <X-Content-Type-Options: nosniff <X-XSS-Protection: 1; mode = block <Cache-Control: no-cache, no-store, max-age = 0, must-revalidate <Pragma: no-cache <Expires: 0 <X-Frame-Options: DENY <Content-Type: application / json; charset = UTF-8 <Transfer-Encoding: chunked <Date: Thu, 09 Aug 2018 09:06:29 GMT <* Соединение № 0 с хостом 192.168.12.36 осталось неизменным {"timestamp": 1533805589599, "status": 403, "error": "Forbidden" ‌, "message": "XXX", "pa‌ th": "/ test"}

ajianzheng 09.08.2018 11:04

Причина вашего XXX отображается в свойстве message. Получить его сразу после < HTTP/1.1 403 не получится.

Thoomas 09.08.2018 11:09

о (╥﹏╥) о ……………………

ajianzheng 09.08.2018 11:12

У вас может быть тело HTTP-ответа, содержащее сообщение, которое вы можете проанализировать с любой дополнительной информацией.

Сообщение о состоянии HTTP в коде состояния (в ответе) может быть любым, и оно не повлияет на клиентов. HTTP-клиенты обычно игнорируют текст сообщения.

Вот альтернатива. Создайте универсальное исключение, которое принимает код состояния и сообщение. Затем создайте обработчик исключений. Используйте обработчик исключений, чтобы извлечь информацию из исключения и вернуться к вызывающей стороне службы.

http://javaninja.net/2016/06/throwing-exceptions-messages-spring-mvc-controller/

public class ResourceException extends RuntimeException {

    private HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

    public HttpStatus getHttpStatus() {
        return httpStatus;
    }

    /**
     * Constructs a new runtime exception with the specified detail message.
     * The cause is not initialized, and may subsequently be initialized by a
     * call to {@link #initCause}.
     * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()}
     *                method.
     */
    public ResourceException(HttpStatus httpStatus, String message) {
        super(message);
        this.httpStatus = httpStatus;
    }
}

Затем используйте обработчик исключений, чтобы получить информацию и вернуть ее вызывающей стороне службы.

@ControllerAdvice
public class ExceptionHandlerAdvice { 

    @ExceptionHandler(ResourceException.class)
    public ResponseEntity handleException(ResourceException e) {
        // log exception 
        return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
    }         
} 

Затем создайте исключение, когда вам нужно.

throw new ResourceException(HttpStatus.NOT_FOUND, "We were unable to find the specified resource.");

сообщение будет найдено в теле ответа

ajianzheng 09.08.2018 12:21

Вы не сказали этого в своем вопросе, но я предполагаю, что вы используете Tomcat. По умолчанию Tomcat отправляет только код состояния и не отправляет фразу причины. Фраза причины является необязательной частью спецификации HTTP, поэтому я бы не рекомендовал полагаться на ее отправку.

Если вы действительно хотите полагаться на настраиваемую фразу причины, а не использовать тело ответа, как рекомендовали другие ответы, вам придется либо настроить Tomcat для отправки фразы причины (что поддерживается только в Tomcat 8.5), либо переключиться на другую контейнер. Что касается встроенных контейнеров сервлетов, поддерживаемых Spring Boot, как Jetty, так и Undertow отправят фразу причины.

Вот небольшой пример, который настраивает Tomcat как для отправки фразы причины, так и для включения использования настраиваемых фраз:

package sample.tomcat;

import javax.servlet.http.HttpServletResponse;

import org.apache.coyote.AbstractProtocol;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@SpringBootApplication
public class SampleTomcatApplication {

    @Bean
    public EmbeddedServletContainerCustomizer tomcatCustomizer() {
        return (container) -> {
            if (container instanceof TomcatEmbeddedServletContainerFactory) {
                ((TomcatEmbeddedServletContainerFactory) container)
                        .addConnectorCustomizers((connector) -> {
                            ((AbstractProtocol<?>) connector.getProtocolHandler())
                                    .setSendReasonPhrase(true);
                        });
            }
        };
    }

    public static void main(String[] args) throws Exception {
        System.setProperty("org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER", "true");
        SpringApplication.run(SampleTomcatApplication.class, args);
    }

}

@Controller
class SampleController {

    @GetMapping("/")
    public void customReasonPhrase(HttpServletResponse response) throws Exception {
        response.setStatus(503, "Custom");
    }

}

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

Интеграция Spring: асинхронный перехват каналов
Я получаю сообщение об ошибке в Spring Dao при вставке данных: Предупреждение: пружина сервлета вызвала исключение java.lang.NullPointerException
Ожидается по крайней мере 1 bean-компонент, который квалифицируется как кандидат autowire для этой зависимости. Для зависимости не найдено подходящего bean-компонента типа
PayPal не возвращает параметры успеха для LIVE-аккаунта
Запрос PUT приложения Spring Mvc с @ModelAttribute с формой многостраничного запроса без привязки
Недопустимое возвращаемое значение метода обработчика: ModelAndView: ссылка на просмотр с именем в Spring MVC
Мой AJAX не запускает функцию УСПЕХ, но выполняет мой контроллер JAVA
Как сгенерировать проверки на стороне клиента с помощью проверок на стороне сервера?
Декларативная производительность API Jdbi поражает веб-службу Spring MVC
SpringMVC, свойство driverClassName вызвало исключение java.lang.IllegalStateException: не удалось загрузить класс драйвера oracle.jdbc.driver.OracleDriver

Похожие вопросы

Дата усечения QueryDSL
Реализовать фоновый процесс в Spring
Я получаю сообщение об ошибке в Spring Dao при вставке данных: Предупреждение: пружина сервлета вызвала исключение java.lang.NullPointerException
В чем разница использования Spring Cache и хранения данных в переменной?
Как объединить две таблицы и использовать их как репозиторий jpa
Ожидается по крайней мере 1 bean-компонент, который квалифицируется как кандидат autowire для этой зависимости. Для зависимости не найдено подходящего bean-компонента типа
Интеграция Spring Boot + Hibernate (не Spring Data JPA) в Spring Boot 2.0 или выше
Ошибка при создании bean-компонента с именем 'ModelAccess' Не удалось ввести автоматически подключенные зависимости; Не удалось выполнить автоматическое подключение поля
Могу ли я поймать уже пойманное исключение?
Интеграция Spring с DSL: может ли адаптер исходящего канала File создать файл, скажем, через 10 минут интервала