Restfull unified service exception handling

1 Introduction

When building a service, there will always throw various exceptions, then we need to be consistent exception handling, so we can guarantee the return of foreign consistent.
And avoid the return value is determined by the result of this function call code cumbersome.

2.Controller layer approach, unified exception handling

Two different solutions as follows:

  • Scenario 1: Using @ControllerAdvice (或@RestControllerAdvice), @ExceptionHandlerannotations to achieve;
  • Scheme 2: Use AOP technology;

3. Use @ControllerAdvice annotation style

3.1 to create a global exception handler class

The following code will throw an exception to all the Controller for processing, where we introduced the custom exception ServiceException our project
abnormality can be declared to be captured, for example, @ExceptionHandler(NoHandlerFoundException.class)
you can specify intercept package,@RestControllerAdvice(basePackages = "com.demo.springboot_helloword.webrest")

/**
 * Created on 2019/1/15 19:09.
 *
 * @author caogu
 */
@RestControllerAdvice
public class ControllerExceptionHandleAdvice {
    private final static Logger logger = LoggerFactory.getLogger(ControllerExceptionHandleAdvice.class);

    @ExceptionHandler
    public BaseResult handler(HttpServletRequest request, HttpServletResponse response, Exception e) {
        logger.error("Restful Http请求发生异常, Path=" + request.getServletPath(), e);
        if (response.getStatus() == HttpStatus.BAD_REQUEST.value()) {
            logger.info("修改返回状态值,oldStatus={} newStatus={}", response.getStatus(), HttpStatus.BAD_REQUEST.value());
            response.setStatus(HttpStatus.OK.value());
        }

        BaseResult baseResult = new BaseResult();
        if (e instanceof ServiceException) {
            ServiceException exception = (ServiceException) e;
            baseResult.setCode(exception.getCode());
            baseResult.setMessage(exception.getMessage());
        } else {
            baseResult.setCode(PlatformErrorCode.SERVICE_INTERNAL_ERROR.getCode());
            baseResult.setMessage(PlatformErrorCode.SERVICE_INTERNAL_ERROR.getMessage());
        }

        return baseResult;
    }
}

3.2 Creating a custom exception

A business exception, generally requires an error code and error messages. Help us to locate the problem and unified external interface behavior.

/**
 * Created on 2019/1/14 11:37.
 *
 * @author caogu
 */
public class ServiceException extends RuntimeException {
    /**
     * 错误码
     */
    private String code;

    /*public ServiceException(String message, String code) {
        super(message);
        this.code = code;
    }*/

    public ServiceException(BusinessErrorCode businessErrorCode) {
        super(businessErrorCode.getMessage());
        this.code = businessErrorCode.getCode();
    }

    public ServiceException(BusinessErrorCode businessErrorCode, Throwable cause) {
        super(businessErrorCode.getMessage(), cause);
        this.code = businessErrorCode.getCode();
    }

    public ServiceException(PlatformErrorCode platformErrorCode) {
        super(platformErrorCode.getMessage());
        this.code = platformErrorCode.getCode();
    }

    public ServiceException(PlatformErrorCode businessErrorCode, Throwable cause) {
        super(businessErrorCode.getMessage(), cause);
        this.code = businessErrorCode.getCode();
    }

    /*public ServiceException(String message, String code, Throwable cause) {
        super(message, cause);
        this.code = code;
    }*/

    public String getCode() {
        return code;
    }

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

At this time, we found that there is a problem, and if so to write the code to multiply in the future, is difficult to manage the correspondence between these business exceptions and error codes. So optimize it.
The error code and error messages, assembled into enumeration unified management.
Define a business enumeration exception.

3.3 Business abnormal enumeration

package com.sinosun.starter.enums;

/**
 * Created on 2019/1/14 12:10.
 *
 * @author caogu
 */
public enum PlatformErrorCode {
    SERVICE_INTERNAL_ERROR(1, "服务器内部错误")
    ;
    private static final int BASE_CODE_VALUE = 10000;
    private static final String BASE_CODE_PREFIX = "P";
    private String code;
    private String message;

    PlatformErrorCode(int code, String message) {
        this.code = buildCode(code);
        this.message = message;
    }

    private String buildCode(int code) {
        int codeValue = BASE_CODE_VALUE + code;
        return BASE_CODE_PREFIX + codeValue;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

3.4 Test results

After an exception is thrown in the code, uniform exception handling will take effect as follows:

public StationResult getAllCity(NoneRequest requestBody) {
    throw new ServiceException(PlatformErrorCode.SERVICE_INTERNAL_ERROR);
    //return new StationResult(new StationList(PreloadData.getTrainAllCity()));
}

Here Insert Picture Description

4. Use AOP technology

The main idea: to intercept all the Controller method @Around AOP, and at the call unified handle exceptions. After the call before the call can also print logs and other operations.
E.g:

package com.sinosun.starter.config;

/**
 * Created on 2019/1/16 8:44.
 *
 * @author caogu
 */
@Component
@Aspect
public class ControllerAspect {
    private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class);

    @Pointcut("execution(public * com.sinosun.starter.controller..*.*(..))")
    public void allControllerFunction() {
    }


    @Around("allControllerFunction()")
    public Object handleControllerMethod(ProceedingJoinPoint pjp) {
        Stopwatch stopwatch = Stopwatch.createStarted();

        BaseResult baseResult;
        try {
            logger.info("执行Controller方法{}开始,参数:{}", pjp.getSignature().getName(), Arrays.toString(pjp.getArgs()));
            baseResult = (BaseResult) pjp.proceed(pjp.getArgs());
            logger.info("执行Controller方法{}结束,返回值:{}", pjp.getSignature().getName(), baseResult);
            logger.info("执行Controller方法{}耗时:{}(ms).", pjp.getSignature().getName(), stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));
        } catch (Throwable throwable) {
            baseResult = handlerException(pjp, throwable);
        }
        return baseResult;
    }

    private BaseResult handlerException(ProceedingJoinPoint pjp, Throwable e) {
        BaseResult baseResult = new BaseResult();
        logger.error("方法:" + pjp.getSignature().getName() + ", 参数:" + Arrays.toString(pjp.getArgs()) + ",异常:" + e.getMessage() + "}", e);
        if (e instanceof ServiceException) {
            ServiceException exception = (ServiceException) e;
            baseResult.setCode(exception.getCode());
            baseResult.setMessage(exception.getMessage());
        } else {
            baseResult.setCode(PlatformErrorCode.SERVICE_INTERNAL_ERROR.getCode());
            baseResult.setMessage(PlatformErrorCode.SERVICE_INTERNAL_ERROR.getMessage());
        }

        return baseResult;
    }

}

result:
Here Insert Picture Description

5 Conclusion

  • Exception handling is recommended to use @ControllerAdvice (或@RestControllerAdvice), @ExceptionHandlerannotation program.
  • No exception handling does not use AOP technology, the use of AOP technology as a doorway to print the log, exception handling is too much trouble, all the return values ​​need to manually set the response right.
  • Both should be used in combination, @ ControllerAdvice handle exceptions, AOP unified gateway log record.
Published 418 original articles · won praise 745 · Views 1.26 million +

Guess you like

Origin blog.csdn.net/u013467442/article/details/89193430