Spring Boot unified interface return and global exception handling

Get into the habit of writing together! This is the 12th day of my participation in the "Nuggets Daily New Plan·April Update Challenge", click to view the details of the event

foreword

I took over an old project some time ago, and now I need to add some new requirements to this project. My colleagues encountered some problems during the development process?

1. Whether the status of success is 200 or 0, the order system 200 represents success, while the membership system is 0 represents success.

2. Among the results returned by the interface, some are described by the msg field, and some are described by the desc field. It is troublesome to process the previous section. Can you unify it?

3. The error message needs to support internationalization.

In fact, these problems are, in the final analysis, the problem of code specification. We need to uniformly handle interface definitions and global exceptions. There are more than 10 projects in the history project. Is it possible to implement each project once? The answer is definitely impossible.

solution

picture.png

Define public modules, implement unified interface definition specifications and exception handling, and other systems can rely on and expand.

Implementation

2.1 Defining a unified interface for status codes

public interface BaseResultCode
{
    /**
     * 状态码
     * @return
     */
    int getCode();
    
    /**
     * 提示信息
     * @return
     */
    String getMsg();
}

复制代码

2.2 Public module status code enumeration class

public enum ResultCode implements BaseResultCode
{
    OK(200, "成功"),
    ERROR(300,"系统异常"), 
    NEED_AUTH(301, "非法请求,请重新登录"), 
    PARAMTER_ERROR(302, "参数错误");
    //省略其他定义错误码
   
    private int code;

    private String msg;

    private ResultCode(int code, String msg)
    {
        this.code = code;
        this.msg = msg;
    }

    public static ResultCode getValue(int code)
    {
        for (ResultCode errorCode : values())
        {
            if (errorCode.getCode() == code)
            {
                return errorCode;
            }
        }
        return null;
    }
   //省略Get、Set方法
 }

复制代码

2.3 Define global custom exceptions

public class SysException extends RuntimeException
{
    private static final long serialVersionUID = 5225171867523879342L;
    
    private int code;
    
    private String msg;
    
    private Object[] params;

    private BaseResultCode errorCode;

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

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

    public SysException(int code ,String message,  Object[] params)
    {
        this(code, message);
        this.params= params;
    }
    
    public SysException(String message, Throwable cause)
    {
        super(message, cause);
    }
    
    public SysException(BaseResultCode errorCode)
    {
        this.errorCode = errorCode;
    }

    public SysException(String message, Object[] params)
    {
        super(message);
        this.params = params;
    }
    
    public SysException(BaseResultCode errorCode, String message, Object[] params)
    {
        this(message, params);
        this.errorCode = errorCode;
    }
 
    

    /**
     * Construct by default
     * 
     * @param message
     *            message
     * @param parameters
     *            parameters
     * @param cause
     *            cause
     */
    public SysException(String message, Object[] params, Throwable cause)
    {
        super(message, cause);
        this.params = params;
    }
    

    public int getCode()
    {
        return code;
    }

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

    public String getMsg()
    {
        return msg;
    }

    public void setMsg(String msg)
    {
        this.msg = msg;
    }

    /**
     * @return the params
     */
    public Object[] getParams()
    {
        return params;
    }

    /**
     * @param params
     *            the params to set
     */
    public void setParams(Object[] params)
    {
        this.params = params;
    }

    public BaseResultCode getErrorCode()
    {
        return errorCode;
    }

    public void setErrorCode(BaseResultCode errorCode)
    {
        this.errorCode = errorCode;
    }
    
    
}
复制代码

2.3 Defining a unified interface format output class

public class Result implements Serializable
{
    private static final long serialVersionUID = -1773941471021475043L;

    private Object data;

    private int code;

    private String msg;

    public Result()
    {
    }

    public Result(int code, Object data, String msg)
    {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    public Result(int code, String desc)
    {
        this(code, null, desc);
    }

    public Result(BaseResultCode errorCode)
    {
        this(errorCode.getCode(), null, errorCode.getMsg());
    }

    public static Result success()
    {
        return success(null);
    }

    public static Result success(Object data)
    {
        Result result = new Result();
        result.setData(data);
        result.setCode(ResultCode.OK.getCode());
        return result;
    }

    public static Result error(String msg)
    {
        Result result = new Result();
        result.setCode(ResultCode.ERROR.getCode());
        result.setMsg(msg);
        return result;
    }
    
    public static Result error(BaseResultCode baseCode)
    {
        Result result = new Result();
        result.setCode(baseCode.getCode());
        result.setMsg(baseCode.getMsg());
        return result;
    }
}
复制代码

Personal suggestion: The unified interface output class should not be defined as a generic type

2.4 Define a unified interface format output class

@RestControllerAdvice
public class SysExceptionHandler
{
    public static Log logger = LogManager.getLogger(SysExceptionHandler.class);
    
    @ExceptionHandler(Exception.class)
    public Result handleException(HttpServletRequest request,
            Exception ex)
    {
        logger.error("Handle Exception Request Url:{},Exception:{}",request.getRequestURL(),ex);
        Result result = new Result();
        //系统异常
        if (ex instanceof SysException)
        {
            SysException se = (SysException) ex;
            BaseResultCode resultCode =se.getErrorCode();
            if(resultCode==null)
            {
                result = Result.error(se.getMessage());
            }
            else
            {
               result = new Result(resultCode.getCode(),
                                       StringUtil.isNotEmpty(se.getMessage())?se.getMessage():resultCode.getMsg());
            }
        }
        //参数错误
        else if (ex instanceof ConstraintViolationException)
        {
            ConstraintViolationException v = (ConstraintViolationException) ex;
            String message = v.getConstraintViolations().iterator().next()
                    .getMessage();
            result.setCode(ResultCode.PARAMTER_ERROR.getCode());
            result.setMsg(ResultCode.PARAMTER_ERROR.getMsg() + ":" + message);
        }
        //参数错误
        else if (ex instanceof BindException)
        {
            BindException v = (BindException) ex;
            String message = v.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(","));
            result.setCode(ResultCode.PARAMTER_ERROR.getCode());
            result.setMsg(ResultCode.PARAMTER_ERROR.getMsg() + ":" + message);
        }
        //参数错误
        else if (ex instanceof MethodArgumentNotValidException)
        {
            MethodArgumentNotValidException v = (MethodArgumentNotValidException) ex;
            String message = v.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(","));
            result.setCode(ResultCode.PARAMTER_ERROR.getCode());
            result.setMsg(ResultCode.PARAMTER_ERROR.getMsg() + ":" + message);
        }
        else
        {
           result = new Result(ResultCode.ERROR.getCode(),ExceptionUtil.getErrorMsg(ex));
        }
        logger.info("exception handle reuslt:" + result);
        return result;
    }
}
复制代码

The above definition can already achieve unified processing of global interfaces and exceptions, but the following problems exist

Each controller needs to return the Result type, and each method needs to return the result of Result.success() or Result.success(data), which is a bit repetitive and needs to be optimized.

    @GetMapping("addUser")
    public Result add()
    {
       for(int i=0;i<10;i++)
       {
           TUser user = new TUser();
           //user.setOid(IdWorker.getId());
           user.setName("shareing_"+i);
           user.setAge(i);
           userService.addUser(user);
       }
       return Result.success();
    }
复制代码

2.5 Interface unified output optimization

The implementation method only needs to implement the ResponseBodyAdvice interface and override the beforeBodyWrite method interface.

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object>
{
    private Logger logger = LoggerFactory.getLogger(ResponseAdvice.class);
    
    @Override
    public boolean supports(MethodParameter returnType,
            Class<? extends HttpMessageConverter<?>> converterType)
    {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter returnType,
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request, ServerHttpResponse response)
    {
        logger.info("before body write param:{}",o);
        
        if(o instanceof String)
        {
           //序列化结果输出
           return FastJsonUtil.toJSONString(Result.success(o));
        }
        else if (o instanceof Result)
        {
             return o;
        }
        return Result.success(o);
    }
}
复制代码

After optimization, the controller output can define output objects according to business requirements.

 @GetMapping("getUserByName")
    public TUser getUserByName1(@RequestParam String name)
    {
       logger.info("getUserByName paramter name:"+name);
       return userService.getUserByName(name); 
    }
复制代码

2.6 How the subsystem is implemented

1. The subsystem introduces the common jar package,

  <dependency>
	    <groupId>com.xx</groupId>
	    <artifactId>xx-common</artifactId>
	    <version>2.0</version>
	</dependency>
复制代码

2. The subsystem defines the status code and implements the BaseResultCode interface

 public enum OrderModelErrorCode implements BaseResultCode
{
    ORDER_STATUS_ERROR(1000, "订单状态不正确");
    
    private int code;

    private String msg;
    
    private UserModelErrorCode(int code, String msg)
    {
        this.code = code;
        this.msg = msg;
    }

    @Override
    public int getCode()
    {
        return code;
    }

    @Override
    public String getMsg()
    {
        return msg;
    }
}
复制代码

3. Define an exception handling class and inherit the public exception handling class SysExceptionHandler

@RestControllerAdvice
public class OrderModalExceptionHandle extends SysExceptionHandler
{
     @Override
    public Result handleException(HttpServletRequest request, Exception ex)
    {
        return super.handleException(request, ex);
        //子系统可以扩展异常处理
        
    }
}
复制代码

4. Subsystem usage example

@Override
public Order getOrder(String orderId)
{
	Order order =getOrder(orderId);
        //相关伪代码
	if(order.getStatus()>120)
	{
	   throw new SysException(OrderModelErrorCode.ORDER_STATUS_ERROR);    
	}
	return order;
}
复制代码

After the reconstruction of related projects, the first and second problems have been solved. The third internationalization problem will be explained in subsequent articles.

Summarize

In the development of the project, the project development manual is still very important. For example, <<Alibaba Java Development Manual>> mentioned many important
specifications, which are summed up by the predecessors through actual combat and stepping on the pit. We should be more like the predecessors. Learn to avoid making mistakes.

Guess you like

Origin juejin.im/post/7086031796755922981