4

需求:小程序登录时,后台需要将异常抛出,并将错误信息交给前台。

一、后台异常处理

1.自定义微信登录异常

    public class WechatLoginException extends RuntimeException {
    /**
     * @Description 自定义状态码
     * 510: 未找到该学号或该学号的状态已冻结
     * 509: 该学号已被其他微信帐号绑定,请联系老师或者管理员解除绑定
     * 508: 该微信帐号已绑定学号,请解除绑定后重新登录
     **/
    private HttpStatus status = null;
    public WechatLoginException(String message, HttpStatus httpStatus) {
        super(message);
        status = httpStatus;
    }

     public HttpStatus getStatus() {
        return status;
    }
}

2.userService抛出异常

     if (student.getWechat() != null && !student.getWechat().getOpenid().equals(openId)) {
         throw new WechatLoginException("该学号已被其他微信帐号绑定,请联系老师或者管理员解除绑定", HttpStatus.valueOf(509));
    }

3. 全局异常处理

// 处理微信登录的异常
@ExceptionHandler(WechatLoginException.class)
public ResponseEntity<JsonErrorResult> wechatLoginHandler(HttpServletRequest request, WechatLoginException e){
    logger.error("微信登录异常:---Host {} invokes url {} ERROR: {}", request.getRemoteHost(), request.getRequestURL(), e.getMessage());
    return new ResponseEntity<>(new JsonErrorResult(request, e), e.getStatus());
}

存在的问题

后来,潘老师和张喜硕学长评论说这样写不好,因为开放异常信息,异常的状态码只靠注释约束,当返回未知状态码前台不知道如何处理。

二、改进

根据潘老师的建议,做出如下修改:

1. 添加异常状态枚举

public enum  HttpStatusEnum {
    StudentNotFound("未找到该学生", 601),
    StudentIsForzen("该学生的状态已冻结", 602),
    StudentISBinded("该学号已被其他微信帐号绑定,请联系老师或者管理员解除绑定", 603),
    WecahtIsBinded("该微信帐号已绑定学号,请解除绑定后重新登录", 604);

    private String description;
    private int code;

    private HttpStatusEnum (String description, int code) {
        this.description = description;
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

2. 异常类修改,构造时传入枚举体

public class WechatLoginException extends RuntimeException {
    private int code;   // 异常状态码
    public WechatLoginException(HttpStatusEnum httpStatusEnum) {
        super(httpStatusEnum.getDescription());
        code = httpStatusEnum.getCode();
    }

    public int getCode() {
        return code;
    }
}

3. service抛出异常

throw new WechatLoginException(HttpStatusEnum.StudentISBinded);

4. 全局异常处理

//处理微信登录的异常
@ExceptionHandler(WechatLoginException.class)
public ResponseEntity<JsonErrorResult> wechatLoginHandler(HttpServletRequest request, WechatLoginException e){
    logger.error("微信登录异常:---Host {} invokes url {} ERROR: {}", request.getRemoteHost(), request.getRequestURL(), e.getMessage());
    return new ResponseEntity<>(new JsonErrorResult(request, e), e.getStatus());
}

然后这一步就出现了问题,使用ResponseEntity返回异常信息时,第二个参数需要传入HttpStatus,如图:
clipboard.png

而张喜硕学长指出,spring-boot的HttpStatus枚举是不可扩展的,即我无法通过继承来增加自定义的http状态码
clipboard.png

所以,此时陷入一个僵局,要么放弃使用自定义状态码,要么放弃使用ResponseEntity,这时,张喜硕学长说了一下他的解决方案,即不返回ResponseEntity了,而是注入HttpServletResponse,调取setStatus()直接改返回的状态码:

// 处理微信登录的异常
@ExceptionHandler(value = WechatLoginException.class)
public String WechatLoginExceptionHandler(HttpServletRequest request, HttpServletResponse response, WechatLoginException e) throws IOException {
    logger.error("微信登录异常:---Host {} invokes url {} ERROR: {}", request.getRemoteHost(), request.getRequestURL(), e.getMessage());
    response.setStatus(e.getCode());
    return e.getMessage();
}

然后在小程序端测试,发现果然状态码可以变成了自定义的了
clipboard.png
接着,就是在小程序端添加异常处理:

    public static handleHttpException(response: RequestSuccessCallbackResult) {
    let result = '';
    switch (response.statusCode) {
        case 601:
            result = '未找到该学生';
            break;
        case 602:
            result = '该学生的状态已冻结';
            break;
        case 603:
            result = '该学号已被其他微信帐号绑定,请联系老师或者管理员解除绑定';
            break;
        case 604:
            result = '该微信帐号已绑定学号,请解除绑定后重新登录';
    }
    // 如果异常信息不为空,则显示提示信息,并且抛出异常
    if (result != '') {
        // 显示异常
        wx.showToast({
            title: result,
            icon: "none",
            duration: Request.duration
        });
        throw response;
    }
 }

最后的效果:

clipboard.png

总结

总得来说,这次收获还是挺多的,遇到了不少问题,也对一些知识的盲区进行了探索和挖掘。虽然实现了需求,但觉得小程序的异常处理还是有些生硬,还有改进的余地。


陈杰
167 声望332 粉丝

为API生,为框架死;