Как установить код состояния http при ответе клиенту сервлета из метода класса Filter в tomcat

Я пишу веб-сервис с Spring (этот вопрос не о Spring...), который реализует (надеюсь) спокойный API. Насколько я понимаю, все ответы должны быть в формате xml или json. В большинстве случаев это не имеет большого значения. Но в одной ситуации это кажется невозможным. Я использую средство от tomcat, где задействован сервлет. По какой-то причине мне приходится использовать фильтр (и эта причина — аутентификация). Поскольку я новичок в сервлетах, мое понимание в конечном итоге не так хорошо, но для меня это выглядит так

Мой класс фильтра происходит от javax.servlet.filter, и я пишу свой код в методе doFilter:

@Override
public void doFilter(ServletRequest request, ServletResponse response,
                          FilterChain chain) throws IOException, ServletException { // ... }

И в какой-то момент я понимаю, что должен ответить клиенту с кодом состояния http 401, а также хочу предоставить ему информацию в формате xml или json о том, что произошло. Теперь мне кажется, что я тоже могу

1) Используйте ServletResponse: это позволяет мне получить OutputStream и записать мой xml/json. Однако я вообще не могу установить код состояния http. Окончательный ответ, поступающий клиенту, содержит некоторые заголовки http.

2) Приведите ServletResponse к HttpServletResponse: это позволяет мне установить код состояния, но я, похоже, не могу установить тело ответа, но пусть тело ответа обрабатывается из tomcat.

В любом случае кажется неполным. Если я использую ServletResponse для записи в OutputStream, а затем привожу к HttpServletResponse, а затем вызываю sendError(401) - надеясь, что все, что я написал в OutputStream, достигнет клиента - мой ответ не содержит http "строку состояния". Однако присутствуют заголовки http, такие как «Сервер: Apache-Coyote/1.1».

любая помощь приветствуется...


person Matthias    schedule 03.03.2016    source источник
comment
Реализация REST в сервлетах, на мой взгляд, является довольно низким уровнем. Если возможно, я бы попытался использовать JAX-RS, например, с Джерси, что более удобно.   -  person simdevmon    schedule 03.03.2016
comment
Я не очень хорошо описал свою ситуацию... Пожалуйста, посмотрите на обновленный вопрос. остальные API выполняются с помощью spring.   -  person Matthias    schedule 03.03.2016


Ответы (3)


Вскоре я реализовал фильтр для аутентификации. Я закодировал что-то похожее на это:

public void doFilter(ServletRequest req, ServletResponse resp,
                         FilterChain chain)
{
    HttpServletResponse response=(HttpServletResponse) resp;

    boolean authenticated=false;
    // perform authentication

    if (authenticated)
    {
         chain.doFilter(req, response);
    }
    else
    {
         // don't continue the chain
         response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
         response.setHeader("WWW-Authenticate", "BASIC realm=\"Your realm\"");

         response.setContentType("what you need");
         PrintWriter writer=response.getWriter();

         // don't set content length , don't close
    }
}
person JimHawkins    schedule 03.03.2016
comment
Можно ли отправить определенный пользователем код состояния, например 498, с пользовательским сообщением? - person Sanjeev; 18.09.2017
comment
@Sanjeev - я не пробовал, но думаю, что это возможно, потому что код представляет собой просто целое число. Возможно, другие компоненты в вашей инфраструктуре принимают только статус, охватываемый стандартами. - person JimHawkins; 18.09.2017
comment
Если я использую PrintWriter, в журналах появляется следующее исключение: java.lang.IllegalStateException: getWriter() уже был вызван для этого ответа. Когда я переключаюсь на outputStream(), я не могу установить код состояния http в ответе. - person Piyush Upadhyay; 02.08.2020

Это работает для меня:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
    ServletException {

    response.resetBuffer();
    response.getOutputStream().write("Your content".getBytes());
    HttpServletResponse hsr = (HttpServletResponse) response;
    hsr.setStatus(401);
    chain.doFilter(request, response);
}
person eztam    schedule 03.03.2016
comment
почему вы продолжаете цепочку фильтров после установки статуса 401? Фильтры перед аутентификацией выполняются в любом случае - person JimHawkins; 03.03.2016
comment
@Ulrich Может не потребоваться продолжение цепочки фильтров, но это зависит от конкретной настройки фильтров в файле web.xml. - person eztam; 03.03.2016
comment
Вы правы, конечно web.xml влияет на порядок выполнения фильтра. Но в целом нет смысла продолжать обработку неаутентифицированного запроса. Если есть задачи, которые необходимо выполнить (например, ведение журнала), они должны выполняться в фильтрах, которые объединены в цепочку перед аутентификацией. - person JimHawkins; 03.03.2016
comment
Что, если последующие фильтры захотят установить заголовки? Я иду сюда в поисках, почему мой фильтр, который устанавливает заголовки CORS, не срабатывает. - person Stijn de Witt; 26.04.2017

Метод HttpServletResponse::sendError работает для меня, чтобы вернуться из фильтра в приложении SpringBoot 2.0:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (authenticated)
    {
         chain.doFilter(req, response);
    }
    else
    {
    ((HttpServletResponse) response).sendError(HttpStatus.FORBIDDEN.value(), "Authorization shall be provided");
    }
    chain.doFilter(request, response);
}
person Shamil Guseinov    schedule 20.05.2019
comment
Это позволяет мне установить код состояния, но, похоже, я не могу установить собственное тело ответа, используя getOutputStream(). Невозможно добавить сюда фрагмент кода, так как это приводит к неформатированному коду. - person Piyush Upadhyay; 02.08.2020
comment
@PiyushUpadhyay - опубликуйте новый вопрос. Там вы можете добавить MCVE или что-то подобное. В ответ можно поместить пользовательское тело, но не после закрытия OutputStream. - person JimHawkins; 03.08.2020