3

I've got the following class in my Spring Boot application:

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.google.common.net.HttpHeaders;

@Controller
public class TestController {
  @RequestMapping("foo")
  public ResponseEntity<?> redirectOnGet() {
    return ResponseEntity.status(HttpStatus.FOUND).header(HttpHeaders.LOCATION, "http://www.bbc.co.uk").build();
  }
}

When I call that via a GET request, I get a 200 OK back containing the HTML source of BBC.co.uk. So it looks like the redirect is being followed.

According to the HTTP spec section 10.3.3:

If the 302 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user

Also it's specific for non-GET responses, it doesn't dictate that GET requests should redirect. So although it could be helpful in some cases, here it isn't so I'd like to avoid that redirect.

So does anyone have any idea how I can Spring not to follow a 302 redirect when returning a response to a GET request? What I am returning the response to will follow the 302 itself.

Looking at SimpleClientHttpRequestFactory, that could be somewhere I can potentially fix the problem. But after overriding the class & exposing it via a @Bean, my overridden methods aren't being called. I'm guessing it's not being picked up.

There's a project illustrating the issue here: https://github.com/LTheobald/StackOverflow31266409

The test case is probably the most interesting thing to look at. The MockMVC based method does what I expected, the RestTemplate based one doesn't. This is because RestTemplate is set to follow redirects, while TestRestTemplate (which I'm assuming MockMVC uses) doesn't follow redirects. So how can I get my application to act more like TestRestTemplate when the controllers are called?

Note Regarding the possible duplication of: How can I prevent Spring MVC from doing a redirect? . This question doesn't contain enough information to say why it's redirecting. The original poster could have simply been returning "redirect:something" and Spring would have been doing the right thing.

Community
  • 1
  • 1
Lee Theobald
  • 8,461
  • 12
  • 49
  • 58
  • Is it my thinking, or does the status code depend on what the server of bbc.co.uk sending u? – We are Borg Jul 07 '15 at 12:19
  • The status code that comes back from the BBC will indeed depend on what they are sending me. The point is that I shouldn't even know what they are sending me - I don't want their server to be visited. I want to get a 302 request back that tells me what to visit via the Location header - so that I can do that visit myself – Lee Theobald Jul 07 '15 at 12:48
  • Oh, ok, now I get you... You want the header for their redirection instead of directly touching the server. Hmm..no idea..sorry....I was just curious.... – We are Borg Jul 07 '15 at 12:50
  • possible duplicate of [How can I prevent Spring MVC from doing a redirect?](http://stackoverflow.com/questions/24086207/how-can-i-prevent-spring-mvc-from-doing-a-redirect) – Shekhar Khairnar Jul 09 '15 at 08:51
  • @ShekharKhairnar I disagree with it being marked a duplicate. The other question doesn't contain enough information to know what's doing the redirect so the questions are explanitory of how Spring works & doesn't answer any problem. I've tried to reword my question to highlight this. – Lee Theobald Jul 09 '15 at 10:19

1 Answers1

0

Just tried to reproduce this problem in a Boot sample project and couldn't (using Spring Boot 1.2.4 and 1.3.0.M1).

➜  curl -v http://localhost:8080/foo
> GET /foo HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 302 Found
< Server: Apache-Coyote/1.1
< Location: http://www.bbc.co.uk
< Content-Length: 0
< Date: Wed, 08 Jul 2015 20:27:41 GMT
<

Could you share a bit more about your Spring MVC Web configuration? Which servlet container are you using?

Note: there's a typo in your sample, you're importing the wrong implementation of HttpHeaders.

Edit

Your controller is returning a valid HTTP 302 response. Now it's up to the HTTP client to do what it wants with that response. If you take a look at the SimpleClientHttpRequestFactory, which is the default one if you don't configure RestTemplate with another one (Apache httpComponents, Netty 4, OkHttp). This RequestFactory follows redirects for GET requests by default.

So two solutions:

  • you configure your RestTemplate to use another RequestFactory that does not follow redirects.
  • or you stick with mockMvc, which is probably better for those cases, as you can use it without starting the Servlet Container, depending on your use case of course (= faster tests!)
Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
  • Gave it a test just incase but it's not changed anything. The `@RestController` annotation combines the `@Controller` & `@ResponseBody` annotations. As there is no return object to bind to the response body, there's no need for the `@ResponseBody` annotation so I don't think it makes a different. – Lee Theobald Jul 07 '15 at 15:37
  • And I've updated the question with a full project to illustrate the problem :) Run the ApplicationLauncher class, curl localhost:8080/foo, you get a 200 response – Lee Theobald Jul 09 '15 at 08:45
  • I'm only using RestTemplate in my test to illustrate the point. The problem is how I can fix this in my Controller. If Spring uses RestTemplate internally, I can't see how I can provide my own RestTemplate implementation that doesn't use `SimpleClientHttpRequestFactory` & therefore redirects. – Lee Theobald Jul 09 '15 at 10:32
  • I don't get your comment. Your controller is behaving just fine, it returns a valid HTTP response. Spring does not use RestTemplate internally. This is just an HTTP client provided by the framework. So what's the actual problem? If you don't want HTTP clients to follow the link provided by the server, then don't respond with a 302. If you don't want your HTTP client to follow redirects, then configure/change your HTTP client. – Brian Clozel Jul 09 '15 at 11:45
  • Agree with @BrianClozel, your controller code looks fine and will correctly return a 302 redirect. Whether your client follows it or not is up to your client. If you do use RestTemplate, you need to configure it specifically to avoid following redirects, as in a comment on [this response](http://stackoverflow.com/a/29421192/334942): `new RestTemplate(new SimpleClientHttpRequestFactory() { protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { super.prepareConnection(connection, httpMethod); connection.setInstanceFollowRedirects(false); } });` – palimpsestor Nov 04 '16 at 17:59