5. spring-boot REST 全局异常处理

讲点实用的小技巧,学习前端之后才发现以前写的代码真是给前端儿搞了不少事,在此诚恳道歉

单页应用越来越多以及移动化之后,服务化已经是老生常谈了,在前文代码的基础上做些简单的通用模块的处理,后端返回结果的不一致性真的会给前端带来很大的麻烦,故此为止:

  1. 全局异常捕捉及处理
  2. REST FULL基本常见规范

直接贴核心代码。统一返回结果RestResult

public class RestResult {

    private boolean result;
    private String message;
    private T data;

    private RestResult() {}

    public static  RestResult newInstance() {
        return new RestResult<>();
    }

    // ...setter and getter

    @Override
    public String toString() {
        return "RestResult{" +
                "result=" + result +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

result工具类RestResultGenerator

/**
 * Created by kaenry on 2016/9/20.
 * RestResultGenerator
 */
public class RestResultGenerator {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestResultGenerator.class);

    /**
     * normal
     * @param success
     * @param data
     * @param message
     * @param 
     * @return
     */
    public static  RestResult genResult(boolean success, T data, String message) {
        RestResult result = RestResult.newInstance();
        result.setResult(success);
        result.setData(data);
        result.setMessage(message);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("generate rest result:{}", result);
        }
        return result;
    }

    /**
     * success
     * @param data
     * @param 
     * @return
     */
    public static  RestResult genSuccessResult(T data) {

        return genResult(true, data, null);
    }

    /**
     * error message
     * @param message error message
     * @param 
     * @return
     */
    public static  RestResult genErrorResult(String message) {

        return genResult(false, null, message);
    }

    /**
     * error
     * @param error error enum
     * @param 
     * @return
     */
    public static  RestResult genErrorResult(ErrorCode error) {

        return genErrorResult(error.getMessage());
    }

    /**
     * success no message
     * @return
     */
    public static RestResult genSuccessResult() {
        return genSuccessResult(null);
    }
}

统一异常拦截处理:RestExceptionHandler

/**
 * Created by kaenry on 2016/9/20.
 * RestExceptionHandler
 */
@ControllerAdvice(annotations = RestController.class)
public class RestExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestExceptionHandler.class);

    @ExceptionHandler
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    private  RestResult runtimeExceptionHandler(Exception e) {
        LOGGER.error("---------> huge error!", e);
        return RestResultGenerator.genErrorResult(ErrorCode.SERVER_ERROR);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    private  RestResult illegalParamsExceptionHandler(MethodArgumentNotValidException e) {
        LOGGER.error("---------> invalid request!", e);
        return RestResultGenerator.genErrorResult(ErrorCode.ILLEGAL_PARAMS);
    }

}

无论请求成功或失败统一返回RestResult,可自由定义,比如加上错误code或异常的多次处理以及日志啊什么的,代码都很简单,这里就不详细介绍了,返回的结果类似{"result":true,"message":null,"data":{"id":3,"username":"kaenry","password":"jianshu"}}spring-boot默认使用Jackson解析拼装json,如需要忽略null,加个注解即可:@JsonInclude(JsonInclude.Include.NON_NULL),fastjson默认开启。@Valid注解会验证属性,不通过会先交给BindingResult,如果没有这个参数则会抛出异常MethodArgumentNotValidException@ExceptionHandler捕捉到异常则会进入illegalParamsExceptionHandler方法返回结果:

{
  "result": false,
  "message": "request params invalid"
}

测试RestController修改已有代码UserRestController:

@RestController
@RequestMapping("/api/users")
public class UserRestController {

    @Autowired
    IUserService userService;

    /**
     * get all user, GET
     * @return
     */
    @RequestMapping(value = "", method = RequestMethod.GET)
    public RestResult> all() {
        List all = userService.findAll();
        return RestResultGenerator.genSuccessResult(all);
    }

    /**
     * add single user
     * @param user username, password
     * @return RestResult
     * @throws Exception valid check
     */
    @RequestMapping(value = "", method = RequestMethod.POST)
    public RestResult save(@Valid @RequestBody User user) throws Exception {
        User save = userService.save(user);
        return RestResultGenerator.genSuccessResult(save);
    }

    /**
     * get single user by id, GET /id
     * @param id user id
     * @return RestResult
     * @throws Exception
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public RestResult get(@PathVariable Long id) throws Exception {
        User user = userService.findById(id);
        return RestResultGenerator.genSuccessResult(user);
    }

    /**
     * delete user by id
     * @param id user id
     * @return success
     * @throws Exception
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public RestResult delete(@PathVariable Long id) throws Exception {
        userService.delete(id);
        return RestResultGenerator.genSuccessResult();
    }

    /**
     * update user for all props
     * @param id update user id
     * @param newUser new props
     * @return updated User
     * @throws Exception
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    public RestResult updateAll(@PathVariable Long id, @Valid @RequestBody User newUser) throws Exception {
        User user = userService.findById(id);
        // copy all new user props to user except id
        BeanUtils.copyProperties(newUser, user, "id");
        user = userService.save(user);
        return RestResultGenerator.genSuccessResult(user);
    }

    /**
     * update user for some props
     * @param id update user id
     * @param newUser some props
     * @return updated user
     * @throws Exception
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
    public RestResult update(@PathVariable Long id, @Valid @RequestBody User newUser) throws Exception {
        User user = userService.findById(id);
        // copy all new user props to user except null props
        BeanUtils.copyProperties(newUser, user, Utils.getNullPropertyNames(newUser));
        user = userService.save(user);
        return RestResultGenerator.genSuccessResult(user);
    }
}

尽量按照RESTFULL标准书写的:

  1. GET /api/users,获取全部
  2. POST /api/users,新增一个
  3. GET /api/users/:id,获取单个
  4. DELETE /api/users/:id,删除单个
  5. PUT /api/users/:id,全量更新
  6. PATCH /api/users/:id,部分更新

代码都很简单,注意参数尽量使用Bean,非特殊情况千万不要使用诸如Map作为接收参数,图一时痛快,饮恨一生啊;在这里使用@RequestBody的原因是因为现在的前端(因为有了nodejs)大多都会采用JSON直传而不是传统意义上的form了,对应其实就是http协议里的请求头从application/x-www-form-urlencoded换成了application/json;这里在更新的时候有个小技巧,使用BeanUtils复制需要的属性,getNullPropertyNames方法是返回对象里面的为null的属性,因为不需要更新,具体请看代码。地址还是那个地址:https://github.com/kaenry/spring-boot-magneto/releases/tag/v1.8.2。
毕竟不是真实项目,没有写测试,测试工具推荐使用PostMan插件,记得先获取token,随便上个图

5. spring-boot REST 全局异常处理_第1张图片
新增一个用户,kaenry/jianshu

你可能感兴趣的:(5. spring-boot REST 全局异常处理)