Announcement Announcement Module
Collapse
No announcement yet.
Setting response status in HandlerInterceptor.afterCompletion does not work Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Setting response status in HandlerInterceptor.afterCompletion does not work

    I'm sorry if this has already been answered but I spent over an hour perusing forum messages and didn't find anything specific to this topic.

    First, the specifics... Spring 3.0.6, configured mostly thru annotations, but with the additional <appname>-servlet.xml in /WEB-INF.

    I have a very simple app, let's call it web02, that has a few controllers. I wanted to have a specific set of operations applied to the HttpServletResponse object returning from all of the controller methods.

    I looked around and saw that HandlerInterceptors is the way to go with Spring MVC. I wrote mine to do what I want in .afterCompletion(). I also placed the following in WEB-INF/web02-servlet.xml:

    Code:
    	<mvc:interceptors>
    		<bean class="app.main.web.ResponseStatusInterceptor" />
    	</mvc:interceptors>
    This is the afterCompletion method of my interceptor:

    Code:
    	public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
    		throws Exception
    	{
    		int respCode = 404;  // this is just for a test - real value isn't set like this.
    		LOGGER.info ("setting response code: {} ...", respCode);
    		response.setStatus (respCode);
    	}
    In my log output, I see my logging line print "setting response code: 404 ..." so I know this method is getting ran, but when I issue a request from any client, the httpstatus that gets back to the client is always 200! Regardless of what I set this to.

    It's almost as if something else is setting the httpstatus after this method. Any light anyone can shed on this would be greatly appreciated.

    Thanks,

    -Jac

  • #2
    Well the method is called afterCompletion for a reason... That method is called after the response has been send to the client. So sending a response changing it isn't going to work in the afterCompletion method, you might have some luck in the postHandle method.

    Comment


    • #3
      Yes, I actually did try putting the same code in postHandle at first. It had the same results. That's why I tried moving it to afterCompletion. Still, same thing.

      I just need to know how to affect the response status in a interceptor. If not in there, then where? Do I have to write a filter? or is there some other class I can register in Spring MVC that allows me to do the same thing?

      Thanks in advance.

      -Jac

      Comment


      • #4
        The problem is that this only might work if the controller does nothing with the request and no view is to be rendered. If there is a view to be rendered it might depend on the view technology (concrete view implementation) used. Also when using annotation (@controller and @RequestMapping) the response code might already have been set by the HandlerAdapter (not sure if setting it again is allowed).

        If using annotations you are best to use @ResponseStatus also what is the use case here, why do you want to change the response code?

        Comment


        • #5
          Marten, first off, thanks very much for your replies. I really appreciate you taking time.

          Ok, the use case is this... No views being rendered from any of the @Controller methods. Pure REST-style web services returning only data objects using @ResponseBody.

          I have an abstract base class (WebApiResponse) for all my response objects. This base class contains a couple of common fields. All other subclasses (UserResponse below) add their own specific typed objects that will be in the method returns.

          All I wanted to do is for this object to get serialized in the response body, and I wanted the HttpResponse status code to be set to the same code as the one in my serialized return object.

          This is my controller method in question:

          Code:
          @RequestMapping (value="/{userId}", method = RequestMethod.GET)
          public @ResponseBody WebApiResponse getUser (@PathVariable String userId)
          {
          	UserResponse resp = null;
          	User u = realmSrv.getUser (userId);
          
          	if (u == null) {
          		String msg = "Unable to locate user by ID: " + userId;
          		resp = new UserResponse (HttpStatus.NOT_FOUND, msg);
          	} else {
          		resp = new UserResponse (u);
          	}
          
          	return resp;
          }
          My problem is that with the above mechanism, whether or not the user object is found, the HttpStatus is set to 200 since I'm not throwing any exceptions of any kind. I simply wanted the above UserResponse object to get serialized regardless, then I'd handle the setting the response status in an interceptor.

          BTW, just to update you further... Based on your hint about @ResponseStatus, it led me to the docs for @ExceptionHandler that one can register in any controller. I gave it a try, and it achieves the effect I want, but not as clean as I would have liked it.

          I wrote the following method in the same controller:

          Code:
          @ExceptionHandler
          public String handleNotFoundException (NotFoundException ex, HttpServletResponse resp)
          {
          	WebApiResponse webResp = ex.getWebResp ();
          	if (webResp != null) {
          		try {
          			resp.getWriter ().print (objMapper.writeValueAsString (webResp));
          			resp.setStatus (webResp.getHttpStatusCode ());
          			resp.setHeader ("Content-Type", "application/json");
          		} catch (Exception e) {
          			e.printStackTrace ();
          		}
          	}
          	return null;
          }
          then added the throwing of that exception to the previous getUser method:

          Code:
          	if (u == null) {
          		String msg = "Unable to locate user by ID: " + userId;
          		resp = new UserResponse (HttpStatus.NOT_FOUND, msg);
          		throw new NotFoundException (resp);
          	} else {
          		resp = new UserResponse (u);
          	}
          By doing this I've achieved the desired effect, but as you can see, the @ExceptionHandler method assumes Content-Type="application/json". I suppose I could inject the HttpRequest in there as well and examine the "Accept" header and use the proper object serializer to do the job.

          So, my question is... Is this the correct Spring way of achieving the effect I'm looking for?

          BTW, my NotFoundException is annotated with "@ResponseStatus (value = HttpStatus.NOT_FOUND)". But if I simply throw that exception WITHOUT declaring my own @ExceptionHandler, Tomcat writes to the response stream its standard 404/NOT FOUND page HTML page contents, which is very ugly and unusable as a standard data return.

          Thanks again in advance for your help on this.

          -Jac

          Comment


          • #6
            I'm actually trying to do the same thing, and I'm stuck in that I don't know how to have spring convert an exception into a json-ready Status object (so Jackson will serialize) at the app level. But if you defined an @ExceptionHandler in the controller and annotate it with @ResponseBody and return a pojo, you should get the effect you are looking for.

            See my post for my sample: http://forum.springsource.org/showth...SON-Spring-3-1

            Comment


            • #7
              The problem is when using @ResponseBody the return is handled by the RequestResponseBodyMethodProcessor which in turn delegates it to a selected HttpMessageConverter. That converter is probably a subclass of AbstractHttpMessageConverter which flushes the output stream meaning the response is already written and as such cannot be changed anymore. You might get it to work with a custom HttpMessageConverter (or modified existing one).

              Comment


              • #8
                You can declare an HttpServletResponse argument and set the response status. Or alternatively, remove @ResponseBody and return ResponseEntity<WebApiResponse>:

                Code:
                new ResponseEntity<WebApiResponse>(new UserResponse (u), HttpStatus.NOT_FOUND)

                Comment


                • #9
                  Originally posted by Rossen Stoyanchev View Post
                  You can declare an HttpServletResponse argument and set the response status. Or alternatively, remove @ResponseBody and return ResponseEntity<WebApiResponse>:
                  Is using ResponseEntity<T> getData(...) preferred over using @ResponseBody T getData(...) [throws some exception for resource not found, and lets an exception handler set the response code] ?

                  I'm genuinely curious about what Spring offers and which option is recommended.

                  Comment


                  • #10
                    I don't think there is a preferred style in this regard. To me returning ResponseEntity<T> seems simple and appropriate for your case.

                    Comment

                    Working...
                    X