【问题标题】:Handling exception on Spring MVC Controller处理 Spring MVC 控制器上的异常
【发布时间】:2017-12-11 13:59:29
【问题描述】:

你们能否分享一些关于如何设计一个好的实现来处理 Spring MVC 中的异常的建议?以下是我在网上花了一些时间试图找出处理异常的合适或更好的实现后的一些想法。

以下是我正在从事的项目的一些背景:

  • 使用的 MVC 框架是 Spring Boot Web。与使用 Spring MVC 的其他项目类似,我将代码分为几层:控制器、服务和模型。
  • 在控制器中,主要是验证来自视图(前端)层的表单输入并执行一些业务逻辑。对于表单验证,我使用的是 Spring 验证注释 (JSR 303),它与控制器完美集成。
  • 但是,当涉及到分布在控制器和服务层的业务逻辑实现时,我想处理一些检查,如果检查失败,可能会终止控制器/服务方法块的执行。李>
  • 所以,我认为最好使用异常来处理这个问题。
  • 我在网上找到了一些实现,分享了如何使用 Spring 现有的异常处理机制来实现更好的异常处理框架。例如。控制器建议和基于控制器的异常处理程序等。

所以,这是我的困惑。我想到了两种类型的异常处理机制。两者都是使用 @ControllerAdvice 实现的,它将异常处理分隔在一个单独的 Java 类中。

方法 A

  • 在 mvc 控制器中为特定方法重用相同的异常。例如,

    公共字符串方法 A() 抛出 AException{ // 呜呜呜 }

  • 然后会有唯一的 AExceptionHandler 在单独的 java 文件中处理这个异常。在 methodA() 中可能有其他方法调用会引发不同的异常。但是,将实现一个 try catch 块来包装抛出其他类型异常的方法并用 AException 重新抛出。

  • 因此,对于该方法A,它有点重用AException。当然,BaseException 类还有一些附加属性来存储返回消息(显示在视图上)和返回视图名称的详细信息。
  • 但是,这会导致控制器或服务层被返回消息或返回视图名弄乱。

方法 B

  • 另一个在控制器或服务层使用独特的异常。但是,将为每个异常实现一个指定的异常处理程序。
  • 由于异常是唯一的,细节(返回消息和视图名称)可以放在异常处理程序中,而不会污染控制器层。他们会以这种方式清洁。但是,将有更多的样板代码仅用于异常处理程序和许多针对不同独特情况的异常,我希望终止特定方法块的代码执行。

所以,这些是我现在面临的问题。我希望我在处理 Spring web 异常方面朝着正确的方向前进。请让我知道你对此的想法。谢谢。

示例:

我们以账户注册方式为例:

//In the controller class
public String accountSignUp(@Valid AccountSignUpForm form){
    if(bindingResult.hasErrors()){
       return "signupview";
    }
    accountSignUpSvc.validateEmail(form.getEmail);
    accountSignUpSvc.createAccount(form);
}

// In the accountSignUpSvc class;
public interface AccountSignUpSvc {
    // Check if email has been used for sign up.
    void validateEmail(String email) throws DuplicateAccountException;
    // Create the account based on the form.
    void createAccount(AccountSignUpForm form) throws AccountCreationException;
}

所以使用方法A:

//In the controller class
public String accountSignUp(@Valid AccountSignUpForm form){
    if(bindingResult.hasErrors()){
       return "signupview";
    }
    try{
       accountSignUpSvc.validateEmail(form.getEmail);
    } catch ( DuplicateAccountException e){
        // Rethrow with a controller method specific exception
        throw new AccountSignUpException("Returned error message"," Internal error message.",e);
    }
    // Then implement the similar try catch block for the accountSignUpSvc.createAccount method
}

@ControllerAdvice
public class AccountSignUpExceptionHandler{
  @ExceptionHandler(AccountSignUpException.class)
  public ModelAndView handleAccountSignUpException(HttpServletRequest request, AccountSignUpException ex){
    log.error(ex.getInternalError);

    ModelAndView mav = new ModelAndView("signup");
    mav.addObject("returnError", ex.getReturnError());

    return mav;
}

然后使用方法B:

//In the controller class
public String accountSignUp(@Valid AccountSignUpForm form){
    if(bindingResult.hasErrors()){
       return "signupview";
    }

    accountSignUpSvc.validateEmail(form.getEmail);

    // Then implement the similar try catch block for the accountSignUpSvc.createAccount method
}

@ControllerAdvice
public class AccountSignUpExceptionHandler{
  @ExceptionHandler(DuplicateAccountException.class)
  public ModelAndView handleDuplicateAccountException(HttpServletRequest request, DuplicateAccountException ex){
    log.error("Error due to error");

    ModelAndView mav = new ModelAndView("signup");
    mav.addObject("returnError", "Return error message: a long error message.");

    return mav;
}

我提到的好处是异常处理程序可以是特定的,因为只有特定的条件才会触发它。因此,任何应该在 html 页面上显示的长返回消息都可以在这个特定的异常处理程序中进行编码。

【问题讨论】:

    标签: java spring-mvc exception-handling


    【解决方案1】:

    对于异常唯一字段:

    try{
    
    }
    catch(DataIntegrityViolationException e)
    {
    
    }
    

    【讨论】:

      【解决方案2】:

      在我看来,这取决于您所说的这些异常是否是已检查异常。如果它们都是在 methodA() 中抛出的已检查异常,那么我倾向于使用方法 A 并捕获这些异常并将它们作为您自己的应用程序特定异常返回,并在您的 @ControllerAdvice 类中使用单个处理程序。

      但是,如果您想要处理您可能希望在应用程序中发生的未经检查的异常的错误响应,那么我将使用方法 B。

      【讨论】:

      • 感谢 Plog 的回复。我相信它们主要是检查异常。我用一个具体的例子更新了我的问题。你能帮我再看看吗?谢谢。
      • 当然,在我看来,您提到的方法 B 能够在特定异常处理程序中定义长错误消息的好处同样可以在方法 A 中完成。只需将这条长错误消息包含在AccountSignUpException 的构造函数。我确实认为在这种情况下最好使用单个异常处理程序。
      • 是的。我看到了在异常构造函数中包含长​​错误消息的方法。但是,我担心的是长错误消息应该显示在用户的视图层上。我不确定它是否会因返回错误消息过多而污染控制器层。除此之外,假设如果从服务类抛出异常,是否可以在从服务类抛出的异常构造函数中定义长错误消息?
      • 最好设置一个错误代码枚举并将长描述作为枚举的属性包含在内。然后你只需要在你的服务类中引用枚举对象,这会更整洁。
      • 听起来也不错。我会试试看。感谢您的帮助:)
      猜你喜欢
      • 2013-06-07
      • 1970-01-01
      • 1970-01-01
      • 2014-07-04
      • 1970-01-01
      • 2015-07-12
      • 2018-11-11
      • 1970-01-01
      • 2013-08-11
      相关资源
      最近更新 更多