web全局异常捕获

问题:为什么要在数据处理层controller层做全局异常捕获?

分析:如果一个异常,在service层没有被catch到,那么,这个异常就传递到了controller层,如果controller不做处理,页面看到的就是500了。全局异常处理,针对的是这种情况:当异常不能被catch的时候,避免给用户展示500界面,在controller层做的统一处理,跳转到我们自定义的错误页面,或者返回自定义的error信息。

方案:@ControllerAdvice+@ExceptionHandler

package com.powersi.handler;

import cn.hsa.hsaf.core.framework.web.exception.AppException;
import com.alibaba.fastjson.JSON;
import com.powersi.common.exception.BaseExcCodesEnum;
import com.powersi.common.exception.BizRtException;
import com.powersi.common.utils.ResultBuilder;
import com.powersi.common.utils.ResultDto;
import com.powersi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.RpcException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName HSAFExceptionHandler
 * @Description HSAF异常处理器
 * @Author **** 
 * @Date 2021/1/12 11:09
 * @Version 1.0
 **/

@ControllerAdvice
@Slf4j
public class WebExceptionHandler {

    /**
     * 处理自定义运行时的业务异常
     * @param request
     * @param ex
     * @return
     */
    @ExceptionHandler(BizRtException.class)
    public Object exceptionHandler(BizRtException ex, HttpServletRequest request, HttpServletResponse response) {
        log.error("BizRtException:" + ex.getLocalizedMessage(), ex);
        return getObject(response, ResultBuilder.genExpResult(ex));
    }

    /**
     * 处理空指针的异常
     * @param request
     * @param ex
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    public Object exceptionHandler(NullPointerException ex, HttpServletRequest request, HttpServletResponse response) {
        log.error("NullPointerException:" + ex.getLocalizedMessage(), ex);
        return getObject(response, ResultBuilder.genExpResult(ex));
    }

    /**
     * Handle exceptions thrown by handlers.
     */
    @ExceptionHandler(AppException.class)
    public Object exceptionHandler(Exception ex, HttpServletRequest request, HttpServletResponse response) {
        log.error("exception:" + ex.getLocalizedMessage(), ex);
        return getObject(response, ResultBuilder.genExpResult(ex));
    }

    @ExceptionHandler({RpcException.class})
    public Object RpcException(Exception ex,HttpServletRequest request, HttpServletResponse response) {
        log.error("rpc接口参数校验异常。{}", ex.getMessage());
        log.error("exception:" + ex.getLocalizedMessage(), ex);
        String errMsg = ((ConstraintViolationException) ex.getCause()).getConstraintViolations()
                .iterator().next().getMessage();
        //ResponseEntity 为自定义的返回类,非springframework.http.ResponseEntity,当然此处可以自己定义返回类,只需将 异常信息 errMsg 返回即可
        return getObject(response, ResultBuilder.buildResult(BaseExcCodesEnum.PARAM_EXCEPTION.getCode(), null,errMsg));
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object validExceptionHandler(MethodArgumentNotValidException e, HttpServletResponse response) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        StringBuffer stringBuffer = new StringBuffer(16);
        if (!CollectionUtils.isEmpty(fieldErrors)) {
            List<String> fieldList = new ArrayList<>();
            // 遍历所有错误信息,如果一个字段有多个校验注解,只取第一个错误信息
            fieldErrors.forEach(fieldError -> {
                if(fieldList.contains(fieldError.getField())){
                    return;
                }
                fieldList.add(fieldError.getField());
                stringBuffer.append(fieldError.getDefaultMessage()).append(",");
            });
        }
        String message = "";
        if(StringUtils.isNotEmpty(stringBuffer.toString())){
            message = stringBuffer.toString().substring(0,stringBuffer.toString().length()-1);
        }
        return getObject(response, ResultBuilder.buildResult(BaseExcCodesEnum.PARAM_EXCEPTION.getCode(), null, message));
    }

    private Object getObject(HttpServletResponse response, ResultDto resultDto) {
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setHeader("Access-control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setStatus(HttpStatus.OK.value());
        try {
            response.getWriter().write(JSON.toJSONString((resultDto)));
        } catch (IOException e) {
            log.error("异常返回出错" + e.getMessage(),e);
        }
        return new ModelAndView();
    }
}

异常都可以自己定义,基本就是错误信息+错误码


public class AppException extends RuntimeException {
    private static final long serialVersionUID = -7611643172712984323L;
    int code = -1;

    public AppException() {
    }

    public AppException(int code) {
        this.code = code;
    }

    public AppException(String message) {
        super(message);
    }

    public AppException(int code, String message) {
        super(message);
        this.code = code;
    }

    public AppException(Throwable cause) {
        super(cause);
    }

    public AppException(String message, Throwable cause) {
        super(message, cause);
    }

    public AppException(int code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    public int getCode() {
        return this.code;
    }

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

这样我们service层throw或者throws抛出到controller层的异常,在controller层都不需要另外自己写代码处理了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值