Как правильно установить заголовок Location для ответа HTTP 201 в приложении сервлета Java - PullRequest
34 голосов
/ 18 февраля 2011

Рассмотрим следующий код, отправляющий клиенту HTTP-ответ «201»:

    String url = "/app/things?id=42"; // example
    response.setStatus(HttpServletResponse.SC_CREATED);
    response.setContentType("text/plain");
    response.setHeader("Location", url);
    response.getWriter().print(url);

Он информирует клиента о том, что была создана новая «вещь» и что ее можно найти по URL-адресу /app/things?id=42.Проблема в том, что этот URL является относительным.Это было бы идеально для JSP, который можно записать следующим образом:

<img src="<c:url value="/things?id=42" />" />

, который будет производить следующий HTML:

<img src="/app/things?id=42" />

Что мы хотим для веб-приложений.

Но я не верю, что это то, что мы хотим для заголовка местоположения с ответом 201.Спецификация HTTP сообщает :

14.30 Местоположение

Поле заголовка ответа Location используется для перенаправления получателя в местоположение, отличное от Request-URI длязавершение запроса или идентификация нового ресурса.Для 201 (Создано) ответов, Местоположение - это то, что нового ресурса, который был создан запросом.Для ответов 3xx местоположение ДОЛЖНО указывать предпочитаемый URI сервера для автоматического перенаправления на ресурс.Значение поля состоит из одного абсолютного URI.

       Location = "Location" ":" absoluteURI

Например:

       Location: http://www.w3.org/pub/WWW/People.html

Мой вопрос заключается в том, как преобразовать этот относительный URL-адрес в абсолютный URL-адрес дляРасположение заголовка в правильном порядке для сервлетов.

Я НЕ верю, что использование:

request.getServerName() + ":" + request.getServerPort() + url;

является правильным решением.Должен быть стандартный метод, обеспечивающий корректный вывод (чтобы можно было переписать URL и т. Д.).Я не хочу создавать хак.

Ответы [ 5 ]

22 голосов
/ 18 февраля 2011

Просто отправьте абсолютный путь. Ограничение на абсолютный URI является известным дефектом в RFC 2616 и будет исправлено в HTTPbis (см. http://trac.tools.ietf.org/wg/httpbis/trac/ticket/185).

Обратите внимание, что RFC 7231 теперь включает относительные URI в спецификации . См. Другие ответы, чтобы узнать, как обрабатывать относительные URI.

4 голосов
/ 27 августа 2014

Если вы используете JAX RS, в javax.ws.rs.core.Response есть метод, который автоматически преобразует относительные URL :

public static Response.ResponseBuilder created(java.net.URI location)

СоздатьДля нового ResponseBuilder для созданного ресурса установите заголовок местоположения, используя предоставленное значение.

Параметры:

  • location - URI нового ресурса.Если указан относительный URI, он будет преобразован в абсолютный URI путем его разрешения относительно URI запроса.

Обратите внимание, что в реализации JAX RS есть ошибка, которая приводит к неправильным абсолютным URL .

4 голосов
/ 18 февраля 2011

Решили пойти по совету Джулиана Решке и нарушить спецификацию! По крайней мере, я добавил следующий комментарий:

        /* Note: strictly speaking (per section 14.30 of RFC 2616), the Location header 
         * requires an *absolute URI*. However, in practice, many web 
         * applications send an *absolute path* instead. This is interoperable, 
         * that is, works in popular web browsers  (according to 
         * http://en.wikipedia.org/wiki/HTTP_location).
         * 
         * As the information required to set the Location header to an absolute URI
         * is not generally available to a Servlet, we go with the flow and send
         * an absolute path instead (in violation of RFC 2616).
         * 
         * There is an issue filed with Hypertext Transfer Protocol Bis (httpbis) 
         * working group to resolve this problem here:
         * http://trac.tools.ietf.org/wg/httpbis/trac/ticket/185
         */
        response.setHeader("Location", url);

Причина, по которой я не хочу сам отправлять абсолютный URI, заключается в том, что я столкнулся с проблемами, связанными с балансировкой нагрузки и другой производственной инфраструктурой. Хотя в режиме разработки "http://localhost:8080/foo" работает нормально :))

Примет ответ Джулиана сейчас ...

4 голосов
/ 18 февраля 2011

К сожалению, API сервлета не предоставляет метод, который напрямую возвращает абсолютный URL-адрес вплоть до корневого контекста.Для этого мне несколько раз приходилось использовать комбинацию getRequestURL(), getRequestURI() и getContextPath().

String absoluteContextRootURL = request.getRequestURL().toString().replace(request.getRequestURI().substring(1), request.getContextPath());
2 голосов
/ 18 февраля 2011

Вы можете попробовать

new URL(new URL(request.getRequestURL().toString()), url).toString();

Это, по крайней мере, будет разумно при канонизации любых .. или других странностей. Кроме этого, я не думаю, что это намного лучше, чем манипуляции со строками.

...