springmvc学习笔记--REST API的异常处理

时间:2022-09-12 23:57:22

前言:
  最近使用springmvc写了不少rest api, 觉得真是一个好框架. 之前描述的几篇关于rest api的文章, 其实还是不够完善. 比如当遇到参数缺失, 类型不匹配的情况时, 直接抛出异常, 返回的内容是400+的错误页面, 而不是json内容, 这让移动端的调用方很难处理.
  本文主要讲述对于rest api, springmvc对异常的解决处理方案.

系列整理:
  springmvc学习笔记系列的文章目录:
  • idea创建springmvc项目
  • 面向移动端的REST API
  • Jackson的使用和定制
  REST API的设计原则博文
  • 移动互联网实战—Web Restful API设计和基础架构

场景构造:
  大背景, 我们已借助使用Jackson框架和注解@ResonseBody来实现pojo对象以json形式返回.
  先来构造几个案例, 来描述我们所要解决的问题.
  共同的测试代码:

@Controller
@RequestMapping("/math")
public class TestController { @RequestMapping(value="/div", method= RequestMethod.GET)
@ResponseBody
public int div(@RequestParam("a") int a,
@RequestParam("b") int b) {
// *) div zero error
return a / b;
} }

  1). 场景一(参数缺失, 类型不匹配)
  case 1:
    http://localhost:8080/math/div?a=10
  参数b缺失, 返回400错误页
  springmvc学习笔记--REST API的异常处理
  case 2:
    http://localhost:8080/math/div?a=10&b=hehe
  参数b类型不对, 类型转换失败
  springmvc学习笔记--REST API的异常处理
  我们的希望是, 将这些参数缺少/类型不匹配的异常, 以JSON串的形式返回, 而不是以400的错误页面返回.
  2). 场景二(业务代码抛出异常)
  case 1:
    http://localhost:8080/math/div/a=10&b=0
  发生除零异常
  springmvc学习笔记--REST API的异常处理
  内部抛出业务异常非常常见, 防不胜防, 返回是500+的错误页面.

解决方案:
  基于上文的场景, 遇到异常时, 返回的都是400+/500+的错误页面, 一方面客户端sdk解析和处理麻烦, 另一方面泄露了内部的错误细节. 那是否一种办法拦截异常, 并返回自己定义的错误数据格式呢?
  答案是肯定的, springmvc引入了织入@ControlAdvice.
  其对异常的处理, 非常的方便, 可简单参考如下sample.

@ControllerAdvice
public class RestApiControlAdvice { @ExceptionHandler(value=RuntimeException.class)
@ResponseBody
public String handle(RuntimeException e) {
// *记入异常日志
return e.getMessage();
} @ExceptionHandler(value=Exception.class)
@ResponseBody
public String handle(Exception e) {
// *记入异常日志
return e.getMessage(); } }

  通过结合注解@ExceptionHanlder, 来定义具体的异常处理, 以及返回的结果.
  这边需要注意的是, @ExceptionHandler可定义多个, 当多个匹配时, 按编写最早的处理函数优先处理. 这边就像try/catch那种常见的情况了, 既顺序敏感. 最佳实践, 需要把处于顶层的异常类搁置到代码最尾端.
  当然对于参数缺失/类型不匹配的处理, 可以如下定义.

@ExceptionHandler(value=MissingServletRequestParameterException.class)
@ResponseBody
public TResult<Void> handle(MissingServletRequestParameterException e) {
return "Miss parameter " + e.getParameterName() + ":" + e.getParameterType();
} @ExceptionHandler(value= TypeMismatchException.class)
@ResponseBody
public String handle(TypeMismatchException e) {
return e.getErrorCode() + ", required type: " + e.getRequiredType() + ", but value: " + e.getValue();
}

  业务上的异常代码, 往往可以抽象出一个异常基类.

总结:
  springmvc的入门和上手确实非常快, 但要真正的理解. 确实还是需要花功夫, 这边只是简单介绍一下, 并没有深入源码. 希望将来有一天有机会讲讲背后的技术原理.

公众号&游戏站点:
  个人微信公众号: 木目的H5游戏世界
  springmvc学习笔记--REST API的异常处理
  个人游戏作品集站点, www.mmxfgame.com,  请点击访问http://120.26.221.54/.