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

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

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

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

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

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

/**
 * 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

/**
 * 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方法返回结果:

{
  "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<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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Flutter&Dart

DartVM服务器开发(第二十二天)--RestfulApi封装

阅读该文章之前,我会默认的认为你已经看过我之前的文章,并知道如何使用数据库,开启服务器等!

24630
来自专栏bboysoul

给你的linux电脑跑个分(unixbench)

第一次碰到unixbench是在看LinusTechTips视频的时候碰到的,那时候没有对这个软件产生兴趣,直到今天在看博客的时候又碰到了这个软件,所以想研究下...

42520
来自专栏软件开发

Spring MVC 学习总结(九)——Spring MVC实现RESTful与JSON(Spring MVC为前端提供服务)

 很多时候前端都需要调用后台服务实现交互功能,常见的数据交换格式多是JSON或XML,这里主要讲解Spring MVC为前端提供JSON格式的数据并实现与前台交...

37180
来自专栏JAVA烂猪皮

Spring IoC中各个注解的理解和使用

一、把在Spring的xml文件中配置bean改为Spring的注解来配置bean

8930
来自专栏Java后端技术

slf4j介绍以及实现原理窥探

  slf4j(全称是Simple Loging Facade For Java)是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就好...

10220
来自专栏大大的微笑

java使用mina和websocket通信

这里以mina整合springMVC为例: //springMVC的配置: <!-- mina --> <bean class="org.spring...

1.7K100
来自专栏一个会写诗的程序员的博客

Spring Boot 使用 Kotlin Script Template 模板引擎kts 开发web

在 Spring Framework 5.0 M4 中引入了一个专门的Kotlin支持。

25020
来自专栏我是攻城师

万法归宗之Hadoop编程无界限

34670
来自专栏王硕

原 建立共享存储实验记录

384120
来自专栏Java与Android技术栈

使用 Kotlin + WebFlux/RxJava 2 实现响应式以及尝试正式版本的协程WebFluxRxJava 2Kotlin 1.3 的 Coroutines总结

在前一篇文章《使用 Kotlin + Spring Boot 进行后端开发》中,曾介绍过尝试使用 Kotlin 来做后端开发。这一次,尝试 WebFlux 以及...

24210

扫码关注云+社区

领取腾讯云代金券