【问题标题】:Spring @ExceptionHandler does not work with @ResponseBodySpring @ExceptionHandler 不适用于 @ResponseBody
【发布时间】:2011-07-03 02:23:11
【问题描述】:

我尝试为休息控制器配置一个弹簧异常处理程序,该处理程序能够根据传入的接受标头将映射呈现到 xml 和 json。它现在抛出一个 500 servlet 异常。

这行得通,它拿起了 home.jsp:

@ExceptionHandler(IllegalArgumentException.class)
public String handleException(final Exception e, final HttpServletRequest request, Writer writer)
{
    return "home";
}

这不起作用:

@ExceptionHandler(IllegalArgumentException.class)
public @ResponseBody Map<String, Object> handleException(final Exception e, final HttpServletRequest request, Writer writer)
{
    final Map<String, Object> map = new HashMap<String, Object>();
    map.put("errorCode", 1234);
    map.put("errorMessage", "Some error message");
    return map;
}

在同一控制器中,通过相应的转换器将响应映射到 xml 或 json:

@RequestMapping(method = RequestMethod.GET, value = "/book/{id}", headers = "Accept=application/json,application/xml")
public @ResponseBody
Book getBook(@PathVariable final String id)
{
    logger.warn("id=" + id);
    return new Book("12345", new Date(), "Sven Haiges");
}

【问题讨论】:

标签: rest spring-mvc


【解决方案1】:

我遇到了类似的问题,当您的 Controller 方法返回类型和 ExceptionHandler 返回类型不相同时会出现此问题。确保您具有完全相同的返回类型。

控制器方法:

@RequestMapping(value = "/{id}", produces = "application/json", method = RequestMethod.POST)
public ResponseEntity<?> getUserById(@PathVariable String id) throws NotFoundException {
    String response = userService.getUser(id);
    return new ResponseEntity(response, HttpStatus.OK);
}

建议方法:

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<?> notFoundException(HttpServletRequest request, NotFoundException e) {
    ExceptionResponse response = new ExceptionResponse();
    response.setSuccess(false);
    response.setMessage(e.getMessage());
    return new ResponseEntity(response, HttpStatus.NOT_FOUND);
}

如您所见,两个类中的返回类型相同 ResponseEntity&lt;?&gt;

【讨论】:

    【解决方案2】:

    我使用的是 Spring 3.2.4。我对这个问题的解决方案是确保我从异常处理程序返回的对象具有 getter。

    没有 getter,Jackson 无法将对象序列化为 JSON。

    在我的代码中,对于以下 ExceptionHandler:

    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public List<ErrorInfo> exceptionHandler(Exception exception){
        return ((ConversionException) exception).getErrorInfos();
    }
    

    我需要确保我的 ErrorInfo 对象有 getter:

    package com.pelletier.valuelist.exception;
    
    public class ErrorInfo {
    private int code;
    private String field;
    private RuntimeException exception;
    
    public ErrorInfo(){}
    
    public ErrorInfo(int code, String field, RuntimeException exception){
        this.code = code;
        this.field = field;
        this.exception = exception;
    }
    
    public int getCode() {
        return code;
    }
    
    public String getField() {
        return field;
    }
    
    public String getException() {
        return exception.getMessage();
    }
    }
    

    【讨论】:

      【解决方案3】:

      AnnotationMethodHandlerExceptionResolver 也需要 MappingJacksonHttpMessageConverter

      <bean
          class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
          <property name="messageConverters">
              <list>
                  <bean
                      class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                      <property name="objectMapper" ref="jacksonObjectMapper" />
                  </bean>
              </list>
          </property>
      </bean>
      
      <bean id="jacksonObjectMapper"
          class="iacm.cemetery.framework.web.servlet.rest.JacksonObjectMapper" />
      

      【讨论】:

      • 我没有意识到 org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter 的 messageConverters 需要复制到 org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver。我想你知道的越多。
      【解决方案4】:

      谢谢,看来我读多了。这很糟糕......任何想法如何提供 自动以 xml/json 格式出现异常?

      Spring 3.0 中的新功能 MappingJacksonJsonView 可以用来实现:

      private MappingJacksonJsonView  jsonView = new MappingJacksonJsonView();
      
      @ExceptionHandler(Exception.class)
      public ModelAndView handleAnyException( Exception ex )
      {
          return new ModelAndView( jsonView, "error", new ErrorMessage( ex ) );
      }
      

      【讨论】:

      【解决方案5】:

      如果您使用消息转换器将错误对象编组为响应内容,则以下可能是一种解决方法

      @ExceptionHandler(IllegalArgumentException.class)
      public String handleException(final Exception e, final HttpServletRequest request)
      {
          final Map<String, Object> map = new HashMap<String, Object>();
          map.put("errorCode", 1234);
          map.put("errorMessage", "Some error message");
          request.setAttribute("error", map);
          return "forward:/book/errors"; //forward to url for generic errors
      }
      
      //set the response status and return the error object to be marshalled
      @SuppressWarnings("unchecked")
      @RequestMapping(value = {"/book/errors"}, method = {RequestMethod.POST, RequestMethod.GET})
      public @ResponseBody Map<String, Object> showError(HttpServletRequest request, HttpServletResponse response){
      
          Map<String, Object> map = new HashMap<String, Object>();
          if(request.getAttribute("error") != null)
              map = (Map<String, Object>) request.getAttribute("error");
      
          response.setStatus(Integer.parseInt(map.get("errorCode").toString()));
      
          return map;
      }
      

      【讨论】:

      • 这绝对应该被选为这个话题的答案!谢谢你,伙计!
      【解决方案6】:

      这似乎是一个已确认的错误 (SPR-6902 @ResponseBody 不适用于 @ExceptionHandler)

      https://jira.springsource.org/browse/SPR-6902

      虽然已在 3.1 M1 中修复...

      【讨论】:

        【解决方案7】:

        你的方法

        @ExceptionHandler(IllegalArgumentException.class)
        public @ResponseBody Map<String, Object> handleException(final Exception e, final HttpServletRequest request, Writer writer)
        

        不起作用,因为它具有错误的返回类型。 @ExceptionHandler 方法只有两种有效的返回类型:

        • 字符串
        • 模型和视图。

        请参阅http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html 了解更多信息。以下是链接中的具体文字:

        返回类型可以是字符串, 被解释为视图名称或 ModelAndView 对象。

        回应评论

        谢谢,看来我读多了。那是 坏...任何想法如何提供 在 xml/json 中自动出现异常 格式? – Sven Haiges 7 小时前

        这是我所做的(我实际上是在 Scala 中完成的,所以我不确定语法是否完全正确,但你应该明白要点)。

        @ExceptionHandler(Throwable.class)
        @ResponseBody
        public void handleException(final Exception e, final HttpServletRequest request,
                Writer writer)
        {
            writer.write(String.format(
                    "{\"error\":{\"java.class\":\"%s\", \"message\":\"%s\"}}",
                    e.getClass(), e.getMessage()));
        }
        

        【讨论】:

        • 谢谢,看来我读多了。这很糟糕......有什么想法如何以 xml/json 格式自动提供异常?
        • 您好,只是为了评论@ExceptionHandler 方法的返回类型非常广泛。根据文档,它可以是 ModelAndView、Model 对象、java.util.Map、org.springframework.web.servlet.View、String 或 void
        • 请注意,由于您没有返回类型 (void),因此 @ResponseBody 没有意义,可以省略。
        • 是的,从 Spring 3.1 开始,@ResponseBody 方法可以返回一个对象,这将被传递给一个 HttpMessageConverter 以转换为一个表示。
        猜你喜欢
        • 2011-09-10
        • 1970-01-01
        • 2016-12-25
        • 2015-01-14
        • 1970-01-01
        • 2012-07-23
        • 1970-01-01
        • 2013-07-23
        • 2018-07-26
        相关资源
        最近更新 更多