2016-10-08 3 views
1

Я работаю над проектом Spring. Вот мой основной контроллер:Перенаправление после POST против перенаправления после GET

@Controller 
public class Editor { 

private static final String EDITOR_URL = "/editor"; 

@RequestMapping(value = EDITOR_URL, method = {POST, GET}) 
public ModelAndView edit(HttpServletResponse response, 
     HttpServletRequest request, 
     RedirectAttributes redirectAttributes, 
     @RequestParam Map<String, String> allRequestParams) { 

    // The code is trimmed to keep it short 
    // It doesn't really matter where it gets the URL, it works fine 
    String redirectURL = getRedirectUrl(); 
    // redirectURL is going to be /editor/pad.html 
    return new ModelAndView("redirect:" + redirectUrl); 
} 

От web.xml:

<servlet-mapping> 
    <servlet-name>edm</servlet-name> 
    <url-pattern>/</url-pattern> 
</servlet-mapping> 

Я молы встроенные и я пытаюсь тест интеграции:

@Test 
public void redirectToEditPadSuccess() throws Exception { 

    HttpHeaders requestHeaders = new HttpHeaders(); 

    UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(END_POINT + "/edm/editor") 
      .queryParam("param1", "val1") 
      .queryParam("param2", "val2"); 

    HttpEntity<?> entity = new HttpEntity<>(requestHeaders); 

    HttpEntity<String> response = restTemplate.exchange(
      builder.build().encode().toUri(), 
      HttpMethod.POST, 
      entity, 
      String.class); 

    HttpHeaders httpResponseHeaders = response.getHeaders(); 

    List<String> httpReponseLocationHeader = httpResponseHeaders.get("Location"); 
    assertTrue(httpReponseLocationHeader.size() == 1); 

    String redirectLocation = httpReponseLocationHeader.get(0); 
    URL redirectURL = new URL(redirectLocation); 

    assertEquals("/edm/editor/pad.html", redirectURL.getPath()); 

} 

Так что, когда я исполняю выше он отлично работает, и я получаю зеленый знак OK.

Теперь контроллер принимает методы POST и GET. Если я выполнить тест с помощью метода GET (заменяющий HttpMethod.POST с HttpMethod.GET), то результат будет 404.

Журналы показывают:

WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/edm/editor/pad.html] in DispatcherServlet with name 'edm' 

Я пытался отладить приложение вверх к DispatcherServlet и странно, что с GET после ответа 302/redirect Диспетчер снова вызывается и превращает это в 200 - не знаю, как и почему.

+0

Не ответ, но разумным было бы для редактирования всегда быть POST. –

+0

@NathanHughes Если вы думаете об этом как о фактическом редактировании документа, вы правы - возможно, именование не самое лучшее. Однако «/ editor» будет вызван после перенаправления из другой службы (и на самом деле он не будет применять какие-либо изменения/редактировать в любом месте), поэтому он должен быть GET. – Michael

ответ

1

Я собираюсь попытаться объяснить, что происходит, а затем предоставить решение.

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

Сценарий 1: Браузер выдает запрос GET, и сервер отвечает перенаправлением.

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

Сценарий 2: браузер выдает запрос POST, а сервер отвечает перенаправлением.

В этом случае браузер выполняет код ответа и выполняет переадресацию, но второй запрос представляет собой запрос GET, а исходный тело запроса теряется во втором запросе. Это связано с тем, что строго по HTTP-стандартам браузер не может «повторно отправлять» данные на сервер без явного запроса пользователя. (Некоторые браузеры предложит пользователю и попросить их, если они хотят, чтобы повторно пост)

Теперь в вашем коде, RestTemplate использует то, что я полагаю, что по умолчанию HttpClientFactory, скорее всего, это один: https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java.

Это как RestTemplate обрабатывает вышеуказанные два сценария:

Сценарий 1: Rest Шаблон выдает запрос GET, а сервер отвечает редирект. Здесь экземпляр Rest Template будет работать точно так же, как браузер.Вот причина, почему два запрос делается, а второй один ищет /edm/editor/pad.html

Scenario 2 : Rest Template issues a POST request, and the server responds with a redirect. В этом случае, Rest Шаблон остановится после первого вызова, поскольку он не может автоматически переопределить метод запроса и изменить его на GET , и он не может запросить разрешение, как браузер.

Решение: При создании экземпляра RestTemplate, передать его перекрытую версию клиента фабрики, что-то вроде

new RestTemplate(new SimpleClientHttpRequestFactory() { 
    protected void prepareConnection(HttpURLConnection conn, String httpMethod) throws IOException { 
     super.prepareConnection(conn, httpMethod);  
     conn.setInstanceFollowRedirects(false); 
    } 
}); 

Это будет инструктировать шаблон покоя, чтобы остановить после первого запроса.

Извините за длинный ответ, но я надеюсь, что это прояснит ситуацию.