前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >5. spring-boot REST 全局异常处理

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

作者头像
MasterVin
发布2018-08-30 10:16:31
1.1K0
发布2018-08-30 10:16:31
举报

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

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

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

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

代码语言:javascript
复制
public class RestResult<T> {

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

    private RestResult() {}

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

    // ...setter and getter

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

result工具类RestResultGenerator

代码语言:javascript
复制
/**
 * 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 <T>
     * @return
     */
    public static <T> RestResult<T> genResult(boolean success, T data, String message) {
        RestResult<T> 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 <T>
     * @return
     */
    public static <T> RestResult<T> genSuccessResult(T data) {

        return genResult(true, data, null);
    }

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

        return genResult(false, null, message);
    }

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

        return genErrorResult(error.getMessage());
    }

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

统一异常拦截处理:RestExceptionHandler

代码语言:javascript
复制
/**
 * 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 <T> RestResult<T> runtimeExceptionHandler(Exception e) {
        LOGGER.error("---------> huge error!", e);
        return RestResultGenerator.genErrorResult(ErrorCode.SERVER_ERROR);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    private <T> RestResult<T> 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方法返回结果:

代码语言:javascript
复制
{
  "result": false,
  "message": "request params invalid"
}

测试RestController修改已有代码UserRestController:

代码语言:javascript
复制
@RestController
@RequestMapping("/api/users")
public class UserRestController {

    @Autowired
    IUserService userService;

    /**
     * get all user, GET
     * @return
     */
    @RequestMapping(value = "", method = RequestMethod.GET)
    public RestResult<List<User>> all() {
        List<User> 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<User> 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<User>
     * @throws Exception
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public RestResult<User> 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<User> 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<User> 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,随便上个图

新增一个用户,kaenry/jianshu

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.09.20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档