【问题标题】:Spring Boot Exception Handler: User Does not existSpring Boot 异常处理程序:用户不存在
【发布时间】:2020-10-13 17:42:41
【问题描述】:

我正在为用户开发一个 Spring Boot Web 应用程序,其中一项功能是让用户能够重置他们的密码。为此,他们必须提供他们的电子邮件地址,并向他们发送验证消息,以便他们能够重置密码。不过,我的问题是如何针对用户提供的电子邮件在数据库中不存在的情况发出错误消息?到目前为止,我的 GlobalExceptionHandler.java 如下:

package bcoreHW.controllers;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class GlobalExceptionHandler {

    @Value("${message.error.exception}")
    private String exceptionMessage;
    
    @Value("${message.user.nonexistent}")
    private String userNonexistentMessage;

    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    @ExceptionHandler(value=Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.getModel().put("message", exceptionMessage);
        modelAndView.getModel().put("url", req.getRequestURL());
        modelAndView.getModel().put("exception", e);
        modelAndView.setViewName("app.exception");
        return modelAndView;
    }
}

所以我现在得到的错误是基本的 404 HttpStatus.NOT_FOUND 错误,我只是说“发生错误”。但当然我希望这个更具体,以便用户了解更多问题,并且 userNonexistentMessage 会说“此用户不存在。”

那么,如何为这种情况添加异常处理程序?谢谢!

【问题讨论】:

    标签: java spring-boot spring-mvc exception


    【解决方案1】:

    以灵活的方式处理错误的解决方案可能很复杂,但您可以从创建一个枚举开始,以便在一个地方定义您的业务逻辑可能引发的所有错误。

    Java 枚举支持字段,因此您可以将每个文字与错误消息和状态代码相关联。

    public enum ErrorCode {
    
        USER_NOT_FOUND(HttpStatus.NOT_FOUND, "This user does not exist"),
        USER_IS_LOCKED(HttpStatus.UNPROCESSABLE_ENTITY, "This user is locked");
        
        private HttpStatus httpStatus;
        private String message;
        
        private ErrorCode(HttpStatus httpStatus, String message) {
            this.httpStatus = httpStatus;
            this.message = message;
        }
    
        public HttpStatus getHttpStatus() {
            return httpStatus;
        }
    
        public String getMessage() {
            return message;
        }
    }
    

    然后您可以创建一个具有 ErrorCode 字段的自定义 API 异常,因此您可以在代码中抛出异常,并在 Exception 构造函数中传递特定错误。

    最后,您可以在 GlobalExceptionHandler 中添加一个方法来处理您的自定义异常,获取包含在异常中的 ErrorCode 值,并从该 ErrorCode 中获取错误消息、http 状态或构建您的响应所需的任何其他错误相关信息.这样您就可以在 GlobalExceptionHandler 中以单一方式处理所有错误,而不是注入特定的错误消息。

    如开头所述,错误处理可以更加复杂。您的异常也可以接受动态参数来插入错误消息(“使用电子邮件 {} 的用户不存在”),您可以为每个状态代码设置单独的业务异常,您可以将 i18N 支持添加到在每种语言的属性文件中都有错误消息,而不是在错误枚举等中硬编码。但我认为实现 ErrorCode 枚举和自定义异常是一个很好的起点。

    【讨论】:

    • 我从堆栈跟踪中确定它是 NullPointerException,所以我的解决方案如下。您的方法对于额外的错误处理也很有用。谢谢!
    【解决方案2】:

    所以我查看了跟踪,发现以下几行有助于确定手头的错误:

    2020-10-13 22:04:53.555 DEBUG 63687 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet        : POST "/forgotPassword", parameters={masked}
    2020-10-13 22:04:53.556 DEBUG 63687 --- [nio-8080-exec-8] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to bcoreHW.controllers.AuthController#forgotUserPassword(ModelAndView, SiteUser)
    2020-10-13 22:04:53.654 DEBUG 63687 --- [nio-8080-exec-8] org.hibernate.SQL                        : select siteuser0_.user_id as user_id1_3_, siteuser0_.email as email2_3_, siteuser0_.enabled as enabled3_3_, siteuser0_.password as password4_3_, siteuser0_.role as role5_3_ from users siteuser0_ where siteuser0_.email=?
    Hibernate: select siteuser0_.user_id as user_id1_3_, siteuser0_.email as email2_3_, siteuser0_.enabled as enabled3_3_, siteuser0_.password as password4_3_, siteuser0_.role as role5_3_ from users siteuser0_ where siteuser0_.email=?
    2020-10-13 22:04:53.662 DEBUG 63687 --- [nio-8080-exec-8] .m.m.a.ExceptionHandlerExceptionResolver : Using @ExceptionHandler bcoreHW.controllers.GlobalExceptionHandler#invalidUserMessage(HttpServletRequest, Exception)
    2020-10-13 22:04:53.662 DEBUG 63687 --- [nio-8080-exec-8] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [java.lang.NullPointerException] to ModelAndView [view="app.exception"; model={message=Invalid user., url=http://localhost:8080/forgotPassword, exception=java.lang.NullPointerException}]
    2020-10-13 22:04:53.662 DEBUG 63687 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet        : Using resolved error view: ModelAndView [view="app.exception"; model={message=Invalid user., url=http://localhost:8080/forgotPassword, exception=java.lang.NullPointerException}]
    2020-10-13 22:04:53.663 DEBUG 63687 --- [nio-8080-exec-8] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
    2020-10-13 22:04:53.663 DEBUG 63687 --- [nio-8080-exec-8] o.s.web.servlet.view.tiles3.TilesView    : View name 'app.exception', model {message=Invalid user., url=http://localhost:8080/forgotPassword, exception=java.lang.NullPointerException}
    2020-10-13 22:04:53.768 DEBUG 63687 --- [nio-8080-exec-8] o.s.web.servlet.DispatcherServlet        : Completed 200 OK
    

    你可以看到最后有一个 NullPointerException,所以我在上面的问题中显示的控制器中添加了以下内容:

        @ExceptionHandler(value=NullPointerException.class)
        public ModelAndView invalidUserMessage(HttpServletRequest req, Exception e) {
            ModelAndView modelAndView = new ModelAndView();
            modelAndView.getModel().put("message", invalidUserMessage);
            modelAndView.getModel().put("url", req.getRequestURL());
            modelAndView.getModel().put("exception", e);
            modelAndView.setViewName("app.exception");
            return modelAndView;
        }
    

    所以现在我避免了总体 404 错误状态,并为无效用户提供了特定的错误消息。

    【讨论】:

      猜你喜欢
      • 2023-03-04
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-25
      • 2018-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多