【问题标题】:How can I prevent Spring MVC from doing a redirect?如何防止 Spring MVC 进行重定向?
【发布时间】:2014-06-06 16:04:35
【问题描述】:

我想处理更新实体的 AJAX 请求。我真的不需要它来返回任何东西。问题是 Spring MVC 坚持发送重定向到同一个 URL(显然是在做它的 post-redirect-get 事情),浏览器尽职尽责地遵循。

我怎样才能让 Spring MVC 控制器方法完成并返回一些东西而不发送重定向?在网络上搜索只会引发无数关于如何进行重定向的讨论,而不是如何避免重定向。

这是对http://localhost:9090/pex/api/testrun/f0a80b46-84b1-462a-af47-d1eadd779f59e 的 PUT 请求,带有以下标头:

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

响应的状态码为“302 Found”,没有正文内容和这些标头:

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: "*"

这是服务器端代码:

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

这是进行 AJAX 调用的 Javascript 代码:

$(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;
            }
    });
}

【问题讨论】:

  • @ResponseBody 默认不发送重定向;这里可能正在发生其他事情。
  • 使用@ResponseBoby注解方法应该只返回一个序列化的Testrun对象而不重定向。您使用哪个代码进行 AJAX 调用?
  • 请添加更多详细信息,例如带有重定向响应的网络控制台信息。您发布的代码本身不会导致重定向。
  • @beerbajay:当然——但是这个“其他东西”会是什么?
  • 一些配置会很好。

标签: java spring spring-mvc


【解决方案1】:

Spring MVC 构建在 Servlet API 之上。因此,任何有权访问HttpServletResponse 的组件理论上都可以将其用于sendRedirect(String) 或手动设置响应代码。 (我说理论上是因为在进行这些调用时,响应一定还没有提交。)

通常,在 Spring MVC 应用程序中,@Controller 可以在 @RequestMapping 方法中以 argument 的形式接收 HttpServletResponse(或 ServletResponse)。

HandlerInterceptorDispatcherServlet 的请求处理生命周期中收到三次。

任何注册的 Servlet Filter 实例也可以在 Spring 的 DispatcherServlet 之前(和之后)访问 ServletResponse,因为过滤器在 servlet 之前起作用。

Spring 尝试将所有这些依赖项隐藏到 Servlet API 以使编程 Web 服务器更容易。因此,它提供了导致重定向的其他方法。这些主要取决于supported handler method return types。更具体地说,我们关心StringViewModelAndViewResponseEntity

以下均为默认情况:

当您返回String 时,Spring 将使用ViewResolver 根据String 值解析View,该值标识视图的名称。 Spring 的UrlBasedViewResolver 将在String 视图名称中检测a redirect: prefix,并将其视为发送重定向响应的指示。它将创建一个RedirectView(其中一部分实际上是在ViewNameMethodReturnValueHandler 中完成的,但UrlBasedViewResolver 创建了View),它将负责使用HttpServletResponse 进行重定向。

这是一个实现细节,但 Spring 的大多数默认 ViewResolver 类都是这样做的。

使用View,您可以简单地自己创建并返回RedirectView。你也可以实现你自己的View 类来做同样的事情。 Spring 将使用适当的HandlerMethodReturnValueHandler 来处理它。

使用ModelAndView,它是前两个选项的混合,因为您可以提供视图名称或View 本身。

使用ResponseEntity 会变得更有趣,因为您可以控制整个响应。也就是说,你可以设置一个状态码,headers,body,一切。因此,您需要做的就是将状态代码设置为 302,并在 Location 标头中添加要重定向到的 URL。

最后,您在 @ExceptionHandler methods 中具有类似的行为(具有类似的返回类型),您也可以将其与 @ResponseStatus 混合并手动修改标题。

这些都是基本情况,但由于 Spring MVC 几乎是完全可定制的,因此还有其他组件需要注意。这些是HandlerMethodArgumentResolverHandlerMethodReturnValueHandlerHandlerAdapterHandlerExceptionResolverExceptionHandler 等等。请注意,您很少会使用这些,而 Spring 附带的那些几乎可以完成整个工作。

【讨论】:

  • @MichaelBorgwardt 或者编写非常棒的应用程序 :)
【解决方案2】:

所以,

我拿了你的代码并用它创建了一个应用程序,尝试使用浏览器插件 POSTMAN 发送一个 PUT 请求并得到响应但没有重定向。看看这是否有效。 我附上了完整的课程,您可以复制并直接在您的应用程序中使用。

以下是网络标头:

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

这里是代码: 配置启动spring

@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
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>

【讨论】:

    【解决方案3】:

    Spring 建议使用后重定向模式,但 id 默认情况下做任何事情。 在我的控制器中,我必须明确结束处理 return "redirect:/url"; 帖子的方法

    我怀疑重定向是在testrunService.updateOverview(testrunId, body); 调用中完成的。

    编辑:实际上,testrunService.updateOverview(testrunId, body); 中的任何内容都不会因为 @ResponseBody 注释而导致重定向。使用这样的代码,只有拦截器或过滤器可以进行重定向。

    【讨论】:

    • 您的怀疑是不正确的,但是您坚持默认情况下不执行重定向让我仔细检查了内容,直到我意识到我的 Maven 启动配置仍然在运行旧版本的代码.. . 呻吟
    • @SotiriosDelimanolis 你说得对,我没有对@ResponseBody 注释给予足够的重视。我相应地编辑了我的帖子。
    猜你喜欢
    • 2015-09-24
    • 2011-01-10
    • 2015-09-27
    • 2015-10-28
    • 1970-01-01
    • 2012-06-24
    • 2016-10-06
    • 2019-02-26
    • 1970-01-01
    相关资源
    最近更新 更多