RedirectView не использует относительный путь для Location - PullRequest
0 голосов
/ 29 июня 2018

У меня есть настройка веб-службы для перенаправления из корня ('/') на страницу ('my / page.html').

Итак, http://localhost:8080/ следует перенаправить на http://localhost:8080/my/page.html.

Вот код:

@RequestMapping(method = RequestMethod.GET, value = "/")
public RedirectView localRedirect()
{
    final RedirectView redirectView = new RedirectView();
    redirectView.setContextRelative(true);
    redirectView.setUrl("/my/page.html");

    return redirectView;
}

Я ожидаю, что в ответе на перенаправление заголовок location будет иметь относительный путь: /my/page.html. Тем не менее, на самом деле это полный путь: http://localhost/my/page.html.

Это вызывает некоторые проблемы, потому что это приложение работает в контейнере Docker, который считает, что он обслуживает порт 80; Вы, возможно, заметили, что полный путь отбросил спецификатор порта :8080 в URL. Контейнер Docker работает за обратным прокси-сервером, который отображает запросы на 8080 для контейнера. Если заголовок location был относительным и установлен на /my/page.html, ожидается, что клиент браузера будет использовать правильное имя хоста (localhost:8080), поэтому он будет перенаправлен обратным прокси на правильную страницу.

Как видно из моего кода, я попытался установить для параметра ContextRelative в объекте RedirectView значение true. Что-то еще мне здесь не хватает?

EDIT

@RequestMapping(method = RequestMethod.GET, value = "/")
public void localRedirect(HttpServletResponse response) {
    response.setStatus(HttpServletResponse.SC_FOUND);
    response.setHeader("Location", "/my/page.html");
}

Я получил перенаправление на работу с использованием приведенного выше кода. Тем не менее, мне все еще любопытно, если кто-нибудь знает способ достижения вышеуказанного с помощью RedirectView и RedirectStrategy от Spring, я был бы рад принять это решение.

1 Ответ

0 голосов
/ 30 июня 2018

Поведение вашего кода правильное, потому что вы говорите, что ваш URL-адрес редиректа относителен к контексту, но это должен быть действительный URL-адрес, и по этой причине Spring построил его как URL-адрес, /my/page.html не является действительный URL. сказал, что проблема заключается в том, что вы должны настроить перезапись URL-адреса в полнодуплексном режиме и устранить проблему в обратном прокси-сервере, фактически для предполагаемого кода, если он выполняется на сервере, который обслуживает порт 80, код отобразит ваш URL-адрес. на 80 в противном случае вы должны написать свой URL вручную, как показано ниже:

@RequestMapping(method = RequestMethod.GET, value = "/")
public RedirectView localRedirect()
{
    final RedirectView redirectView = new RedirectView();

    redirectView.setUrl("http://localhost:8080/my/index.html");
    redirectView.setHosts();
    return redirectView;
}

Я попытался запустить два экземпляра приложения на моем компьютере, один на 80, а другой на 8080, и перенаправления работают правильно

Обновление:

когда вы используете RedirectView, перенаправление происходит на стороне сервера с помощью вызова HttpServletResponse.sendRedirect, конечно, если ваш сервер остается за обратным прокси, ваше приложение на стороне сервера может этого не знать. Когда вы используете в заголовке Location Location заголовок, это обычная строка, не переходящая из среды вашего сервлета. В вашем браузере происходит перенаправление, которое в этом случае получает относительный URL, потому что в вашем контроллере вы задаете простую строку, и ваш браузер уже знает сервер, который уже проксирован.

Update2 Основная логика класса RedirectView - это защищенный метод, называемый sendRedirect (...).

/**
     * Send a redirect back to the HTTP client
     * @param request current HTTP request (allows for reacting to request method)
     * @param response current HTTP response (for sending response headers)
     * @param targetUrl the target URL to redirect to
     * @param http10Compatible whether to stay compatible with HTTP 1.0 clients
     * @throws IOException if thrown by response methods
     */
    protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,
            String targetUrl, boolean http10Compatible) throws IOException {

        String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl));
        if (http10Compatible) {
            HttpStatus attributeStatusCode = (HttpStatus) request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE);
            if (this.statusCode != null) {
                response.setStatus(this.statusCode.value());
                response.setHeader("Location", encodedURL);
            }
            else if (attributeStatusCode != null) {
                response.setStatus(attributeStatusCode.value());
                response.setHeader("Location", encodedURL);
            }
            else {
                // Send status code 302 by default.
                response.sendRedirect(encodedURL);
            }
        }
        else {
            HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
            response.setStatus(statusCode.value());
            response.setHeader("Location", encodedURL);
        }
    }

метод сначала извлекает URL, а затем, если http10Compatible имеет значение true, в конечном итоге будет использовать response.sendRedirect (encodedURL); в противном случае просто поместите ваш относительный URL в заголовок местоположения без пропуска для API сервлета. В вашем коде вы не предоставляете данные для активного условия if, которое предотвращает sendRedirect Servlet api. Это может объяснить, почему в вашем коде у вас есть проблемы. Конечно, в любой другой ветви кода: http10Compatible в false и т. Д. В вашем коде просто поместите строку в заголовок Location, и это работает, потому что в вашем браузере, который будет выполнять перенаправление, поступает относительный URL.

На вопрос, является ли это ошибкой Servlet API, я могу поставить официальный код интерфейса:

HttpServletResponse.java:

 /**
     * Sends a temporary redirect response to the client using the specified
     * redirect location URL. This method can accept relative URLs; the servlet
     * container must convert the relative URL to an absolute URL before sending
     * the response to the client. If the location is relative without a leading
     * '/' the container interprets it as relative to the current request URI.
     * If the location is relative with a leading '/' the container interprets
     * it as relative to the servlet container root.
     * <p>
     * If the response has already been committed, this method throws an
     * IllegalStateException. After using this method, the response should be
     * considered to be committed and should not be written to.
     *
     * @param location
     *            the redirect location URL
     * @exception IOException
     *                If an input or output exception occurs
     * @exception IllegalStateException
     *                If the response was committed or if a partial URL is given
     *                and cannot be converted into a valid URL
     */
    public void sendRedirect(String location) throws IOException;

Вы можете прочитать фрагмент комментария:

This method can accept relative URLs; the servlet
     * container must convert the relative URL to an absolute URL before sending
     * the response to the client

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

Я надеюсь, что это может хорошо объяснить проблему.

...