【问题标题】:How to throw an exception back in JSON in Spring Boot如何在 Spring Boot 中以 JSON 形式抛出异常
【发布时间】:2018-06-02 14:46:37
【问题描述】:

我有一个请求映射 -

  @RequestMapping("/fetchErrorMessages")
  public @ResponseBody int fetchErrorMessages(@RequestParam("startTime") String startTime,@RequestParam("endTime") String endTime) throws Exception
  {
      if(SanityChecker.checkDateSanity(startTime)&&SanityChecker.checkDateSanity(endTime))
      {
          return 0;
      }
      else
      {
          throw new NotFoundException("Datetime is invalid");
      }
  }

如果 startTime 和 endTime 无效,我想抛出 500 错误但返回 JSON 中的异常字符串。但是,我得到一个 HTML 页面,而不是说

白标错误页面

此应用程序没有显式映射 /error,因此您将其视为后备。

2017 年 12 月 20 日星期三 10:49:37 IST
出现意外错误(类型=内部服务器错误,状态=500)。
日期时间无效

我想用 JSON 返回 500

{"error":"Date time format is invalid"}

我该怎么做?

【问题讨论】:

    标签: java json spring spring-mvc spring-boot


    【解决方案1】:

    假设您有一个自定义的异常类 NotFoundException 及其实现如下:

    public class NotFoundException extends Exception {
    
        private int errorCode;
        private String errorMessage;
    
        public NotFoundException(Throwable throwable) {
            super(throwable);
        }
    
        public NotFoundException(String msg, Throwable throwable) {
            super(msg, throwable);
        }
    
        public NotFoundException(String msg) {
            super(msg);
        }
    
        public NotFoundException(String message, int errorCode) {
            super();
            this.errorCode = errorCode;
            this.errorMessage = message;
        }
    
    
        public void setErrorCode(int errorCode) {
            this.errorCode = errorCode;
        }
    
        public int getErrorCode() {
            return errorCode;
        }
    
        public void setErrorMessage(String errorMessage) {
            this.errorMessage = errorMessage;
        }
    
        public String getErrorMessage() {
            return errorMessage;
        }
    
        @Override
        public String toString() {
            return this.errorCode + " : " + this.getErrorMessage();
        }
    }
    

    现在你想从控制器抛出一些异常。如果你抛出一个异常,那么你必须从标准的错误处理程序类中捕获它,例如在春天他们提供@ControllerAdvice 注释来申请创建一个标准错误处理程序类。当它应用于一个类时,这个弹簧组件(我的意思是你注释的类)可以捕获从控制器抛出的任何异常。但是我们需要用适当的方法映射异常类。所以我们用你的异常 NotFoundException 处理程序定义了一个方法,如下所示。

    @ControllerAdvice
    public class RestErrorHandler {
    
        @ExceptionHandler(NotFoundException.class)
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        @ResponseBody
        public Object processValidationError(NotFoundException ex) {
            String result = ex.getErrorMessage();
            System.out.println("###########"+result);
            return ex;
        }
    }
    

    你想发送http状态到内部服务器错误(500),所以这里我们使用@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)。由于您使用了 Spring-boot,所以您不需要创建一个 json 字符串,除了一个简单的注释 @ResponseBody 可以自动为您完成。

    【讨论】:

    • 我遵循了您为我的项目解释的所有内容,并将其作为输出 - NotFoundException{statusCode=400, message='Invalid mode of payment'},但是我只想要它的 JSON 部分.. 你如何实现这一点?
    • @a11apurva,在 NotFoundException 类中你会看到 toString() 方法,你应该有一个像 toJSON() 这样的方法并返回一个 json 对象。
    【解决方案2】:

    创建自定义异常。

    public class SecurityException extends RuntimeException {
    
        private static final long serialVersionUID = -7806029002430564887L;
    
        private String message;
    
        public SecurityException() {
        }
    
        public SecurityException(String message) {
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
    }
    

    创建自定义响应实体。

    public class SecurityResponse {
    
        private String error;
    
        public SecurityResponse() {
    
        }
    
        public SecurityResponse(String error) {
            this.error = error;
        }
    
        public String getError() {
            return error;
        }
    
        public void setError(String error) {
            this.error = error;
        }
    
    }
    

    为自定义异常创建一个带有ExceptionHandler的ControllerAdvice,它将处理自定义异常,填充并返回自定义响应,如下所示。

    @ControllerAdvice
    public class SecurityControllerAdvice {
    
        @ExceptionHandler(SecurityException.class)
        @ResponseBody
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        public SecurityResponse handleSecurityException(SecurityException se) {
            SecurityResponse response = new SecurityResponse(se.getMessage());
            return response;
        }
    }
    

    根据您的情况抛出自定义异常。

    throw new SecurityException("Date time format is invalid");
    

    现在运行并测试您的应用。例如。 :

    【讨论】:

    • 你为什么还要使用securityException?这也可以在没有 securityException 类的情况下执行
    【解决方案3】:

    您可以使用@ResponseStatus 注释创建NotFoundException 类,如下所示:

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public class NotFoundException extends RuntimeException {
       public NotFoundException() {
       }
    
       public NotFoundException(String message) {
        super(message);
       }
    
    }
    

    【讨论】:

    • 这确实有效,但我不推荐这种方法。它不提供显示自定义消息以防万一您想要的可能性,而且它也不安全,因为它仍然会显示类似“Apache Tomcat/8.0.18”的内容,因此有恶意的人现在可以搜索更具体的内容用于您的系统中的违规行为。
    • @GlennVanSchil:可以通过异常的“message”参数显示错误信息;此外,异常的名称本身就是一条消息
    【解决方案4】:

    Javax 的接口名称为 ExceptionMapper。请参考以下代码 sn-p,对于您应用程序中的每个 RuntimeException,它会将其映射到 Json 响应实体。

    public class RuntimeExceptionMapper implements ExceptionMapper <RuntimeException> {
    
    @Override
    public Response toResponse(RuntimeException exception) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setMessage(exception.getMessage);
        if (exception== null) {
            logger.error("Exception Details Not found");            
        } else {
            return Response.status(Status.INTERNAL_SERVER_ERROR)
                .entity(errorResponse )
                    .type(MediaType.APPLICATION_JSON)
                        .header("trace-id", "1234").build();
        }
    
    
    }
    

    }

    【讨论】:

      【解决方案5】:

      这就是我在应用程序中的做法:

      import org.springframework.web.bind.annotation.ControllerAdvice;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      
      @ControllerAdvice
      public class ExceptionHandlingControllerAdvice {
      
         @ExceptionHandler(ExecutionRestrictionViolationException.class)
         public ResponseEntity<String> handleExecutionRestrictionViolationException(ExecutionRestrictionViolationException ex) {
           return response("Invalid Query", ex.getMessage(), HttpStatus.UNPROCESSABLE_ENTITY);
         }
      
         private static String createJson(String message, String reason) {
          return "{\"error\" : \"" + message + "\"," +
                  "\"reason\" : \"" + reason  + "\"}";
         }
      
         private static ResponseEntity<String> response(String message,
                                                     String reason,
                                                     HttpStatus httpStatus) {
          String json = createJson(message, reason);
          return new ResponseEntity<>(json, httpStatus);
         }
      
      }
      

      解释:

      1. 您创建一个控制器 Advice,用特殊注释标记它并像任何其他 bean 一样定义(在我的例子中,它是一个 java 配置,但这并不重要)

      2. 对于您希望这样处理的每个异常 - 定义一个处理程序,该处理程序将以您想要的格式生成响应

      3. 有一个静态方法 createJson - 你可以使用不同的方法,这也无所谓。

      现在这只是一种工作方式(它在最新的 Spring Boot 版本中可用) - 但还有其他方式:

      我知道的所有方法(甚至更多)都列出了here

      【讨论】:

      • 如果他抛出NotFoundException 那么你的@ExceptionHandler 类不应该是@ExceptionHandler(NotFoundException.class) 吗?
      • 是的,你完全正确。我刚刚从我的应用程序中展示了一个代码 sn-p(这个想法)。我相信它应该是每种异常类型的一种方法。
      • 看起来我可以从客户端发送接受标头,并且响应将以 JSON 格式(感谢 Jackson)
      【解决方案6】:

      Spring 提供了几种方法来做到这一点,根据您的情况,有些方法比其他方法更明智。

      (这里有几个选项的很棒的教程。https://www.baeldung.com/spring-exceptions-json

      我最喜欢的是这个,因为我想发回适当的错误消息和适当的 http 响应,而无需创建超类或在实用程序类中创建辅助方法或到处复制样板。

      如果您想通知调用者该事件导致错误(并且在正确的 JSON 中),请使用 Spring 的 ResponseStatusException。它使您可以访问 httpReponse 对象,因此您还可以返回“ok”以外的响应。

      它想要一个异常作为它的参数之一。对于我的一个场景,我想通知调用者他们正在尝试注册一个已经存在的用户。通常,查找用户不应该引发异常,但在这种情况下,我创建了自己的异常,并在 ResponseStatusException 中将其返回给调用者,如下所示:

        @PostMapping("/register")
        public ResponseEntity register(@RequestBody AccountUserDto user) {
          UserDetails userExists = userDetailsService.loadUserByEmail(user.getEmail());
      
        if (userExists != null) {
            UserExistsException exc = new UserExistsException("Error: Email address " + user.getEmail() +  " is already in use.");
              throw new ResponseStatusException(
              HttpStatus.BAD_REQUEST, "User Exists", exc);
        }
      ....(fall through and create user)
      

      【讨论】:

        猜你喜欢
        • 2020-02-05
        • 1970-01-01
        • 2016-09-26
        • 2019-09-19
        • 1970-01-01
        • 1970-01-01
        • 2019-01-24
        • 2019-05-20
        • 2014-07-16
        相关资源
        最近更新 更多