Java 如何防止 Spring MVC 进行重定向?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24086207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 10:05:09  来源:igfitidea点击:

How can I prevent Spring MVC from doing a redirect?

javaspringspring-mvc

提问by Michael Borgwardt

I want to handle an AJAX request that updates an entity. I don't really need it to return anything. The problem is that Spring MVC insists on sending a redirect to the same URL (apparently doing its post-redirect-get thing), which the browser dutifully follows.

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

How can I have a Spring MVC controller method just complete and return something without sending a redirect? Searching on the web only leads to countrless discussions of how to do a redirect, not how to avoid one.

如何让 Spring MVC 控制器方法在不发送重定向的情况下完成并返回某些内容?在网络上搜索只会导致关于如何进行重定向的无数讨论,而不是如何避免重定向。

It's a PUT request to http://localhost:9090/pex/api/testrun/f0a80b46-84b1-462a-af47-d1eadd779f59ewith these headers:

这是一个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

The response has status code "302 Found", no body content and these headers:

响应具有状态代码“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: "*"

Here's the Server-side code:

这是服务器端代码:

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

Here's the Javascript code that makes the AJAX call:

这是进行 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;
            }
    });
}

采纳答案by Sotirios Delimanolis

Spring MVC is built on top of the Servlet API. As such, any component that has access to the HttpServletResponsecan theoretically use it to sendRedirect(String)or set the response code manually. (I say theoreticallybecause the response must not have yet been committed when those calls are made.)

Spring MVC 建立在 Servlet API 之上。因此,任何HttpServletResponse可以访问 的组件理论上都可以使用它来sendRedirect(String)手动设置响应代码。(我说理论上是因为在进行这些调用时必须尚未提交响应。)

Typically, in a Spring MVC application, a @Controllercan receive the HttpServletResponse(or ServletResponse) in a @RequestMappingmethod as an argument.

通常,在 Spring MVC 应用程序中, a@Controller可以接收方法中的HttpServletResponse(或ServletResponse)@RequestMapping作为参数

A HandlerInterceptorreceives it three times as part of the DispatcherServlet's request handling lifecycle.

HandlerInterceptor作为DispatcherServlet的请求处理生命周期的一部分,A收到它三次。

Any registered Servlet Filterinstances also have access to the ServletResponsebefore (and after) Spring's DispatcherServletsince filters act before servlets.

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

Spring tries to hide all these dependencies to the Servlet API to make programming web server easier. It therefore provides other ways to cause a redirect. These mostly depend on the supported handler method return types. More specifically, we care about String, View, ModelAndView, and ResponseEntity.

Spring 尝试将所有这些依赖项隐藏到 Servlet API 以简化 Web 服务器编程。因此,它提供了导致重定向的其他方法。这些主要取决于支持的处理程序方法返回类型。更具体地讲,我们关心的StringViewModelAndView,和ResponseEntity

The following are all default cases:

以下是所有默认情况:

When you return a String, Spring will use a ViewResolverto resolve a Viewbased on the Stringvalue, which identifies the name of the view. Spring's UrlBasedViewResolverwill detect a redirect:prefixin Stringview names and consider it as an indication to send a redirect response. It will create a RedirectView(part of this is actually done in ViewNameMethodReturnValueHandler, but UrlBasedViewResolvercreates the View) which will be in charge of doing the redirect with the HttpServletResponse.

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

This is an implementation detail, but most of Spring's default ViewResolverclasses do this.

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

With View, you can simply create and return a RedirectViewyourself. You can also implement your own Viewclass that will do the same. Spring will use the appropriate HandlerMethodReturnValueHandlerto handle it.

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

With ModelAndView, it's a mix of the two previous options since you can provide either a view name or a Viewitself.

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

With ResponseEntityit gets more interesting since you control the whole response. That is, you can set a status code, headers, body, everything. All you need to do is therefore set the status code to 302 and put a Locationheader with the URL to redirect to.

随着ResponseEntity它变得更有趣,因为您可以控制整个响应。也就是说,您可以设置状态代码、标题、正文等所有内容。因此,您需要做的就是将状态代码设置为 302 并放置一个Location包含要重定向到的 URL的标头。

Finally, you have similar behavior in @ExceptionHandlermethods(with similar return types) which you can also mix with @ResponseStatusand modifying the headers manually.

最后,您在@ExceptionHandler方法(具有相似的返回类型)中有类似的行为,您也可以@ResponseStatus手动混合和修改标题。

These are all the basic cases, but since Spring MVC is almost completely customizable, there are other components to be aware of. These are HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler, HandlerAdapter, HandlerExceptionResolverand ExceptionHandler, and more. Note that you'll rarely play with these and those that come with Spring pretty much do the whole job.

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

回答by Serge Ballesta

Spring recommends using the post-redirect patterns but id does notdo anything by default. In my controllers I have to explicetely end the methods dealing with posts by return "redirect:/url";

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

I suspect the redirect to be done in the testrunService.updateOverview(testrunId, body);call.

我怀疑要在testrunService.updateOverview(testrunId, body);通话中完成重定向。

Edit : in reality, nothing in testrunService.updateOverview(testrunId, body);could cause a redirect because of the @ResponseBodyannotation. With code like that, only interceptors or filters could do a redirection.

编辑:实际上,testrunService.updateOverview(testrunId, body);由于@ResponseBody注释,没有任何内容可能导致重定向。使用这样的代码,只有拦截器或过滤器才能进行重定向。

回答by dharam

So,

所以,

I took your code and created an application with that, tried sending a PUT request using browser plugin POSTMAN and got a response but no redirection. See if this works. I am attaching the complete classes, you can copy and directly use it in ur application.

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

Here are the network headers:

以下是网络标头:

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

And here is the code: Configuration to boot up spring

这是代码: 启动 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

控制器

@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

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