2014-06-06 17 views
7

Chcę obsłużyć żądanie AJAX, które aktualizuje jednostkę. Naprawdę nie potrzebuję tego, aby zwrócić wszystko. Problem polega na tym, że Spring MVC nalega na wysłanie przekierowania do tego samego adresu URL (najwyraźniej robi to po przekierowaniu), co przeglądarka posłusznie przestrzega.Jak mogę zapobiec przekierowaniu Spring MVC?

Jak mogę mieć metodę kontrolera Spring MVC po prostu zakończyć i zwrócić coś bez wysyłania przekierowania? Wyszukiwanie w sieci prowadzi tylko do niezliczonych dyskusji o tym, jak wykonać przekierowanie, a nie jak tego uniknąć.

To żądanie PUT do http://localhost:9090/pex/api/testrun/f0a80b46-84b1-462a-af47-d1eadd779f59e z tych nagłówków:

Host: localhost:9090 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0 
Accept: */* 
Accept-Language: de,en-US;q=0.7,en;q=0.3 
Accept-Encoding: gzip, deflate 
Content-Length: 20 
Content-Type: application/json 
Referer: http://localhost:9090/pex/api/testrun/f0a80b46-84b1-462a-af47-d1eadd779f59e/visualizations/common-api?slas=lp,internal,external 
X-Requested-With: XMLHttpRequest 
Connection: keep-alive 
Authorization: Basic xxxx 

Odpowiedź jest kod stanu "302 Found", bez zawartości ciała i te nagłówki:

Content-Language: "de" 
Content-Length: "0" 
Location: "http://localhost:9090/pex/api/testrun/f0a80b46-84b1-462a-af47-d1eadd779f59e" 
Server: "Jetty(6.1.10)" 
access-control-allow-origin: "*" 

Oto po stronie serwera kod:

@RequestMapping(value = "/api/testrun/{testrunId}", method = RequestMethod.PUT, consumes = "application/json") 
@ResponseBody 
public Testrun updateOverview(@PathVariable("testrunId") final String testrunId, @RequestBody final String body) { 
    return testrunService.updateOverview(testrunId, body); 
} 

Oto kod Javascript wywołujący połączenie AJAX:

$(document).ready(function() { 
    $("#update_name_form").submit(function (e) { 
     update_testrun($("#name")); 
     return false; 
    }); 
} 
function update_testrun(element) { 
    var name = element.attr('name'); 
    var new_value = element.val().trim(); 
    var data = {}; 
    data[name] = new_value; 
    $.ajax({url: config.urls.api.testrun + testrun.id, 
      type: "PUT", 
      contentType: "application/json", 
      data: JSON.stringify(data), 
      error: function(jqXHR, textStatus, errorThrown) { 
        alert(errorThrown); 
      }, 
      success: function (data, textStatus, jqXHR) { 
        testrun.overview[name] = new_value; 
      } 
    }); 
} 
+3

'@ ResponseBody' nie wysyła domyślnie przekierowania; prawdopodobnie dzieje się coś innego. – beerbajay

+2

przy użyciu metody adnotacji '@ ResponseBoby' powinien zwracać tylko serializowany obiekt' Testrun' bez przekierowania. Z którego kodu korzystasz podczas połączenia AJAX? – davioooh

+1

Należy podać więcej szczegółów, np. Informacje o konsoli sieciowej z odpowiedzią przekierowania. Wysłany przez Ciebie kod nie spowodowałby przekierowania. –

Odpowiedz

11

wiosny MVC jest zbudowany na szczycie Servlet API. Jako taki, każdy komponent, który ma dostęp do HttpServletResponse, może teoretycznie użyć go do sendRedirect(String) lub ręcznie ustawić kod odpowiedzi. (Powiedziałem, teoretycznie, ponieważ odpowiedź nie została jeszcze zatwierdzona, gdy te połączenia są wykonane.)

Typowo w zastosowaniu MVC wiosny, A @Controller można otrzymać HttpServletResponse (lub ServletResponse) w metodzie @RequestMapping jako argument.

A HandlerInterceptor odbiera go trzy razy w ramach cyklu obsługi żądań żądania DispatcherServlet.

Każda zarejestrowana wersja serwletu ma również dostęp do ServletResponse przed (i po) wiosennym DispatcherServlet, ponieważ filtry działają przed serwletami.

Spring próbuje ukryć wszystkie te zależności w Servlet API, aby ułatwić programowanie serwera WWW. Zapewnia to inne sposoby powodowania przekierowania. Te w większości zależą od supported handler method return types. Dokładniej rzecz biorąc, zależy nam na: String, View, ModelAndView i ResponseEntity.

Poniżej wymienione są wszystkie przypadki domyślne:

Kiedy zwróci String, Wiosna użyje ViewResolver rozwiązać View na podstawie wartości String, który identyfikuje nazwę widoku. Wiosna UrlBasedViewResolver wykrywa a redirect: prefix w nazwach widoków i uważa to za wskazanie do wysłania odpowiedzi przekierowania. Stworzy on RedirectView (część z tego jest faktycznie wykonywana w ViewNameMethodReturnValueHandler, ale UrlBasedViewResolver tworzy View), która będzie odpowiedzialna za wykonanie przekierowania z HttpServletResponse.

To jest szczegół implementacji, ale większość domyślnych klas Springa ViewResolver to robi.

Dzięki View możesz samodzielnie utworzyć i odesłać plik RedirectView. Możesz także zaimplementować własną klasę View, która zrobi to samo. Wiosna użyje odpowiedniego HandlerMethodReturnValueHandler do obsługi.

Jest to mieszanka dwóch poprzednich opcji, ponieważ można podać nazwę widoku lub samą nazwę.

Z ResponseEntity robi się ciekawiej, ponieważ kontrolujesz całą odpowiedź. Oznacza to, że możesz ustawić kod statusu, nagłówki, treść, wszystko. Wszystko, co musisz zrobić, to ustawić kod statusu na 302 i umieścić nagłówek Location z adresem URL do przekierowania.

Wreszcie, masz podobne zachowanie w @ExceptionHandler methods (z podobnymi typami zwrotów), które możesz również wymieszać z @ResponseStatus i ręcznie zmodyfikować nagłówki.

Są to wszystkie podstawowe przypadki, ale ponieważ Spring MVC jest prawie całkowicie konfigurowalny, istnieją inne elementy, o których należy pamiętać. Są to: HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler, HandlerAdapter, i ExceptionHandler i więcej. Zauważ, że rzadko będziesz się z nimi bawić, a te, które pochodzą ze Spring, wykonują całą pracę.

+1

Cholera, oni * naprawdę * chcą dać ci mnóstwo sznurka, żeby powiesić się z ... –

+1

@MichaelBorgwardt Albo napisać naprawdę świetne aplikacje :) –

1

Wiosna zaleca stosowanie po przekierowanie wzorców, ale nic nie robi id domyślnie nie zrobić. W moich kontrolerami muszę explicetely zakończyć metod zajmujących się postow return "redirect:/url";

podejrzewam przekierowanie do zrobienia w zaproszeniu testrunService.updateOverview(testrunId, body);.

Edytuj: w rzeczywistości nic w numerze testrunService.updateOverview(testrunId, body); nie może spowodować przekierowania z powodu adnotacji @ResponseBody. Z takim kodem tylko przekłuwacze lub filtry mogą przekierować.

+1

Twoje podejrzenie jest niepoprawne, ale twoje naleganie, że przekierowanie nie jest wykonywane domyślnie, sprawiło, że sprawdziłem dokładnie, dopóki nie zorientowałem się, że moja konfiguracja uruchamiania Maven po prostu wciąż działa stara wersja kodu ... * jęk * –

+0

@SotiriosDelimanolis Masz rację, nie zwracałem wystarczającej uwagi na adnotację "@ ResponseBody". Odpowiednio zredagowałem swój post. –

3

Więc

Wziąłem swój kod i stworzył aplikację, z tym, próbował wysłanie żądania PUT za pomocą wtyczki przeglądarki listonosz i dostał odpowiedź, ale nie przekierowanie. Sprawdź, czy to działa. Załączam kompletne zajęcia, możesz je skopiować i użyć bezpośrednio w aplikacji.

Oto nagłówki sieciowe:

Remote Address:::1:8080 
Request URL:http://localhost:8080/test/api/testrun/hello 
Request Method:PUT 
Status Code:200 OK 

**Request Headers** 
Accept:*/* 
Accept-Encoding:gzip,deflate,sdch 
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6,pl;q=0.4 
Authorization:Basic ZWFzeWwtbWFpbi1pbnQ6cHJnc3VzZXI= 
Cache-Control:no-cache 
Connection:keep-alive 
Content-Length:0 
Content-Type:application/json 
Host:localhost:8080 
Origin:chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm 
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36 

**Response Headers** 
Cache-Control:private 
Content-Length:5 
Content-Type:text/plain;charset=ISO-8859-1 
Date:Tue, 10 Jun 2014 10:58:50 GMT 
Expires:Thu, 01 Jan 1970 05:30:00 IST 
Server:Apache-Coyote/1.1 
ConsoleSearchEmulationRendering 

A oto kod: Konfiguracja rozruchu sprężynę

@Configuration 
@EnableWebMvc 
@Profile("production") 
@ComponentScan(basePackages = "com", excludeFilters = {  @ComponentScan.Filter(Configuration.class) }) 
public class WebConfig extends WebMvcConfigurationSupport { 

@Override 
protected void configureContentNegotiation(
     ContentNegotiationConfigurer configurer) { 
    configurer.favorPathExtension(false).favorParameter(true) 
      .parameterName("mediaType").ignoreAcceptHeader(true) 
      .useJaf(false).defaultContentType(MediaType.APPLICATION_JSON) 
      .mediaType("xml", MediaType.APPLICATION_XML) 
      .mediaType("json", MediaType.APPLICATION_JSON); 
} 

@Bean(name = "viewResolver") 
public InternalResourceViewResolver viewResolver() throws Exception { 
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 

    viewResolver.setPrefix("/jsp/"); 
    viewResolver.setSuffix(".jsp"); 

    return viewResolver; 
} 
} 

Controller

@Controller 
public class TestController { 
@RequestMapping(value = "/api/testrun/{testrunId}", method = RequestMethod.PUT, consumes = "application/json") 
@ResponseBody 
public String updateOverview(@PathVariable("testrunId") final String testrunId) { 
    System.out.println(testrunId); 

    return "hello"; 
} 
} 

web.xml

<?xml version="1.0" encoding="UTF-8"?> 
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> 
<display-name>test</display-name> 

<context-param> 
    <param-name>contextClass</param-name> 
    <param-value> 
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext 
    </param-value> 
</context-param> 
<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>com.WebConfig</param-value> 
</context-param> 
<context-param> 
    <param-name>spring.profiles.active</param-name> 
    <param-value>production</param-value> 
</context-param> 
<listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
</listener> 

<servlet> 
    <servlet-name>ui</servlet-name> 
    <servlet-class> 
    org.springframework.web.servlet.DispatcherServlet 
    </servlet-class> 
    <init-param> 
    <param-name>contextClass</param-name> 
    <param-value> 
     org.springframework.web.context.support.AnnotationConfigWebApplicationContext 
    </param-value> 
    </init-param> 
    <load-on-startup>1</load-on-startup> 
</servlet> 
<servlet-mapping> 
    <servlet-name>ui</servlet-name> 
    <url-pattern>/</url-pattern> 
</servlet-mapping> 
    </web-app> 
+0

+1 za wysiłek, dzięki! –