Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-7637

ExceptinHandler method behaves differently than request handler

    Details

    • Type: Improvement
    • Status: Closed
    • Priority: Major
    • Resolution: Complete
    • Affects Version/s: 3.0.4
    • Fix Version/s: 3.1 M2
    • Component/s: Web
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      I am new to Spring Framework. I am using @ExceptionHandler to handle exceptions in my controller. I thought the process of returning result from exception handler would be similar to a request handler but that is not the case. Below is my code.

      @RequestMapping(value = "/

      {userid}

      ", method = RequestMethod.GET)
      public @ResponseBody
      User getUser(@PathVariable(value = "userid") int userId) throws SpringTestException {
      logger.trace("Getting user: " + userId);
      User user = getUserDAO().getUser(userId);
      if (user == null)

      { throw new SpringTestException(1000, "User does not exists"); }

      logger.trace("Userid: " + user.getId() + ", Name: " + user.getName() + ", Email: "
      + user.getEmailId());
      return user;
      }

      This code fetches user from the database and returns it to the user in json. But when the user is not found in db I through an exception which is handled by an exception class and supposed to return error message in json format ("code": 10000, "message": "user does not exists"}). Below is my exception handler version 1.

      @ExceptionHandler(SpringTestException.class)
      public @ResponseBody
      HttpResponse handleSpringTestException(SpringTestException e)

      { logger.trace("Exception caught"); return new HttpResponse(e.getCode(), e.getMessage()); }

      HttpResponse has two members "code" and "message".

      When I make a curl call, I didn't get the response, instead got an exception with http-status 500.

      < HTTP/1.1 500 Internal Server Error
      < Server: Apache-Coyote/1.1
      < Content-Type: text/html;charset=utf-8
      < Content-Length: 2960
      < Date: Sun, 10 Oct 2010 12:35:33 GMT
      < Connection: close
      <
      <html><head><title>Apache Tomcat/6.0.20 - Error report</title><style><!--H1

      {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;}

      H2

      {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;}

      H3

      {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;}

      BODY

      {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;}

      B

      {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;}

      P

      {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}

      A

      {color : black;}

      A.name

      {color : black;}

      HR

      {color : #525D76;}

      --></style> </head><body><h1>HTTP Status 500 - </h1><HR size="1" noshade="noshade"><p><b>type</b> Exception report</p><p><b>message</b> <u></u></p><p><b>description</b> <u>The server encountered an internal error () that prevented it from fulfilling this request.</u></p><p><b>exception</b> <pre>org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.ivasoft.springtest.common.SpringTestException: User does not exists
      org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:656)
      org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
      </pre></p><p><b>root cause</b> <pre>com.ivasoft.springtest.common.SpringTestException: User does not exists
      com.ivasoft.springtest.controllers.UserControllerIoC.getUser(UserControllerIoC.java:43)
      sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      java.lang.reflect.Method.invoke(Method.java:616)
      org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
      org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:427)
      org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:415)
      org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:788)
      org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:717)
      org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
      org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

      • Closing connection #0
        </pre></p><p><b>note</b> <u>The full stack trace of the root cause is available in the Apache Tomcat/6.0.20 logs.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/6.0.20</h3></body></html>

      I googled around for hours and found that ExceptionHandlers are handled by different Adaptor than that of request handlers so I need to configure something in my xml file. I did this in my xml config file.

      <bean id="stringHttpMessageConverter"
      class="org.springframework.http.converter.StringHttpMessageConverter" />

      <bean id="jsonHttpMessageConverter"
      class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />

      <bean id="methodHandlerExceptionResolver"
      class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
      <property name="messageConverters">
      <list>
      <ref bean="stringHttpMessageConverter" />
      <ref bean="jsonHttpMessageConverter" />
      </list>
      </property>
      </bean>

      I got this response after configurations change (This is what I expected):

      < HTTP/1.1 200 OK
      < Server: Apache-Coyote/1.1
      < Content-Type: application/json
      < Transfer-Encoding: chunked
      < Date: Sun, 10 Oct 2010 12:37:43 GMT
      <

      • Connection #0 to host localhost left intact
      • Closing connection #0 {"message":"User does not exists","code":1000}

      I don't know if the above configuration is correct (This is working but could be a hack found by some developer) as I found it in some forum.

      Second level problem came when I used ResposeEntity as return value for my exception handler. I changed the exception handler to this

      @ExceptionHandler(SpringTestException.class)
      public @ResponseBody
      ResponseEntity<HttpResponse> handleSpringTestException(SpringTestException e)

      { return new ResponseEntity<HttpResponse>(new HttpResponse(e.getCode(), e.getMessage()), HttpStatus.NOT_FOUND); }

      And I was exception the same response with status code 404, but got this response.

      < HTTP/1.1 200 OK
      < Server: Apache-Coyote/1.1
      < Content-Type: application/json
      < Transfer-Encoding: chunked
      < Date: Sun, 10 Oct 2010 12:39:13 GMT
      <

      • Connection #0 to host localhost left intact
      • Closing connection #0
        {"statusCode":"NOT_FOUND","headers":{},"body":
        Unknown macro: {"message"}

        }

      Looks like the exception handlers behave differently that request handlers. I fail to understand why is it that way. I used following code to get required results.

      @ExceptionHandler(SpringTestException.class)
      public @ResponseBody
      HttpResponse handleSpringTestException(SpringTestException e, HttpServletResponse response)

      { logger.trace("Exception caught"); response.setStatus(HttpServletResponse.SC_NOT_FOUND); return new HttpResponse(e.getCode(), e.getMessage()); }

      This makes my code dependent on HttpServletResponse object and this is not the same as what I would do in a request handler. I could use @ResponseBody ResponseEntity as return value to get the same result and that seems correct to me. If exception handlers and request handlers behave the same way it would be much easier and consistent for the framework to work with.

        Attachments

          Activity

            People

            • Assignee:
              rstoya05-aop Rossen Stoyanchev
              Reporter:
              rajeev1982 Rajeev Sharma
              Last updater:
              Trevor Marshall
            • Votes:
              2 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                6 years, 24 weeks, 3 days ago