2016-10-08 11 views
1

Ich arbeite an einem Spring-Projekt. Hier ist mein Grund Controller:Redirect nach einem POST vs Redirect nach einem 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); 
} 

Von web.xml:

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

Ich habe Anlegesteg eingebettet und ich versuche, einen Integrationstest:

@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()); 

} 

Also, wenn ich die oben ausführen es funktioniert gut und ich bekomme ein grünes OK-Zeichen.

Nun akzeptiert der Controller POST- und GET-Methoden. Wenn ich den Test mit GET-Methode ausführen (als Ersatz für HttpMethod.POST mit HttpMethod.GET), wird das Ergebnis sein, um ein 404.

Die Protokolle zeigen:

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

Ich habe versucht, die Anwendung zu debuggen up zum DispatcherServlet und seltsame Sache ist, dass mit GET, nach der 302/Redirect Antwort der Dispatcher wieder aufgerufen wird und dies zu einem 200 - keine Ahnung wie und warum.

+0

Keine Antwort, aber die vernünftige Sache zu tun wäre für Bearbeitung immer ein POST sein. –

+0

@NathanHughes Wenn Sie es als eine tatsächliche Dokumentbearbeitung betrachten, haben Sie recht - vielleicht ist die Benennung nicht die beste. Allerdings wird "/ editor" nach einer Umleitung von einem anderen Dienst aufgerufen (und es werden keine Änderungen/Bearbeitungen angewendet), also muss es ein GET sein. – Michael

Antwort

1

Ich werde versuchen zu erklären, was vor sich geht, und dann eine Lösung bereitstellen.

Zuerst vergessen wir, dass Sie einen Ruhefall ausführen und davon ausgehen, dass die Anfrage von einem Browser kommt.

Szenario 1: Der Browser gibt eine GET-Anfrage aus und der Server antwortet mit einer Weiterleitung.

In diesem Fall liest der Browser den Antwortstatuscode als 302 und stellt eine weitere Anfrage unter Verwendung des Antwortkopfs Location. Der Benutzer sieht ein schnelles Neuladen, merkt aber nichts falsch.

Szenario 2: Der Browser gibt eine POST-Anfrage aus und der Server antwortet mit einer Weiterleitung.

In diesem Fall folgt der Browser dem Antwortcode und gibt eine Umleitung aus, aber die zweite Anfrage ist eine GET-Anfrage und der ursprüngliche Anfragetext geht in der zweiten Anfrage verloren. Dies liegt daran, dass der Browser streng nach HTTP-Standards keine Daten auf den Server "re-posten" kann, ohne dass der Benutzer dies ausdrücklich verlangt. (Einige Browser den Benutzer auffordern, und sie fragen, ob sie wollen Post erneut)

Jetzt in Ihrem Code, RestTemplate verwendet, was nehme ich an eine Standard-HttpClientFactory zu sein, wahrscheinlich diese: https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java. Diese

ist, wie RestTemplate ist, um die beiden oben genannten Szenarien Handhabung:

Szenario 1: Rest Vorlage gibt eine GET-Anforderung und der Server antwortet mit einer Umleitung. Hier funktioniert die Rest Template-Instanz genauso wie ein Browser.Das ist der Grund, warum zwei Anfragen gemacht werden, und der zweite sucht /edm/editor/pad.html

Scenario 2 : Rest Template issues a POST request, and the server responds with a redirect. In diesem Fall, Rest Vorlage wird nach dem ersten Aufruf zu stoppen, weil es nicht automatisch Ihre Anfrage Methode überschreiben und ändern Sie es in GET und es kann Sie nicht zur Erlaubnis auffordern, wie es ein Browser tun würde.

Lösung: Wenn eine Instanz von RestTemplate erstellen, gibt ihnen eine überschriebene Version der Client-Fabrik, so etwas wie

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

Diese Ruhe Vorlage anweisen wird nach der ersten Anfrage zu stoppen.

Sorry für die lange Antwort, aber ich hoffe, dies klärt die Dinge.