背景
越来越多的我的项目开始基于前后端拆散的模式进行开发,这对后端接口的报文格式便有了肯定的要求。通常,咱们会采纳JSON格局作为前后端替换数据格式,从而缩小沟通老本等。
这篇文章,就带大家理解一下基于SpringBoot框架来封装返回报文以及对立异样解决。
报文根本格局
个别报文格式通常会蕴含状态码、状态形容(或谬误提示信息)、业务数据等信息。在此基础上,不同的架构师、我的项目搭建者可能会有所调整。但从整体上来说,基本上都是大同小异。
在SpringBoot我的项目中,通常接口返回的报文中至多蕴含三个属性:
- code:申请接口的返回码,胜利或者异样等返回编码,例如定义申请胜利。
- message:申请接口的形容,也就是对返回编码的形容。
- data:申请接口胜利,返回的业务数据。
示例报文如下:
{ "code":200, "message":"SUCCESS", "data":{ "info":"测试胜利" }}
在上述报文格式中,不同的设计者是会有一些一致的,特地是code值的定义。如果齐全基于RESTful API设计的话,code字段可能就不须要存在了,而是通过HTTP协定中提供的GET、POST、PUT、DELETE操作等来实现资源的拜访。
但在实践中,不论是出于目前国内大多数程序员的习惯,还是受限于HTTP协定提供的操作方法的局限性,很少齐全遵循RESTful API形式进行设计。通常都是通过自定义Code值的模式来赋予它业务意义或业务谬误编码。
尽管能够不必齐全恪守RESTful API格调来定义Code,在Code值的自定义中,也存在两种模式:遵循HTTP状态码和自主定义。
像下面的示例,用200示意返回胜利,这就是遵循HTTP响应状态码的模式来返回,比方还有其余的400、401、404、500等。当然,还有齐全自主定义的,比方用0示意胜利,1示意失败,而后再跟进通用编码、业务分类编码等进行定义。
在此,笔者暂不评论每种模式的好坏,只列举了惯例的几种模式,大家理解对应的状况,做到成竹在胸,有所抉择即可。
响应参数封装实际
创立一个SpringBoot我的项目,并引入Lombok依赖(精简代码),对应的外围依赖如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
创立枚举类,用于定义返回的错误码:
@Getter@AllArgsConstructorpublic enum ResponseCodeEnums { SUCCESS(200, "success"), FAIL(500, "failed"), HTTP_STATUS_200(200, "ok"), HTTP_STATUS_400(400, "request error"), HTTP_STATUS_401(401, "no authentication"), HTTP_STATUS_403(403, "no authorities"), HTTP_STATUS_500(500, "server error"); private final int code; private final String message;}
这里只定义了一些通用的、基于的HTTP响应状态码,业务相干的编码可依据业务需要进行定义。
定义对立返回后果实体类:
@Datapublic class ResponseInfo<T> { /** * 状态码 */ protected int code; /** * 响应信息 */ protected String message; /** * 返回数据 */ private T data; public static <T> ResponseInfo<T> success() { return new ResponseInfo<>(); } public static <T> ResponseInfo<T> success(T data) { return new ResponseInfo<>(data); } public static <T> ResponseInfo<T> fail(String message) { return new ResponseInfo<>(ResponseCodeEnums.FAIL.getCode(), message); } public ResponseInfo() { this.code = ResponseCodeEnums.SUCCESS.getCode(); this.message = ResponseCodeEnums.SUCCESS.getMessage(); } public ResponseInfo(ResponseCodeEnums statusEnums) { this.code = statusEnums.getCode(); this.message = statusEnums.getMessage(); } /** * 若没有数据返回,能够人为指定状态码和提示信息 */ public ResponseInfo(int code, String msg) { this.code = code; this.message = msg; } /** * 有数据返回时,状态码为200,默认提示信息为“操作胜利!” */ public ResponseInfo(T data) { this.data = data; this.code = ResponseCodeEnums.SUCCESS.getCode(); this.message = ResponseCodeEnums.SUCCESS.getMessage(); } /** * 有数据返回,状态码为 200,人为指定提示信息 */ public ResponseInfo(T data, String msg) { this.data = data; this.code = ResponseCodeEnums.SUCCESS.getCode(); this.message = msg; }}
在ResponseInfo中使用了泛型和公共办法、构造方法的封装,不便在业务中应用。示例中只提供了局部办法的封装,依据本身业务场景和须要可进一步封装。
对立报文封装在接口中的应用:
@Slf4j@RestControllerpublic class TestController { @RequestMapping("/calc") public ResponseInfo<String> calc(Integer id) { try { // 模仿异样业务代码 int num = 1 / id; log.info("计算结果num={}", num); return ResponseInfo.success(); } catch (Exception e) { return ResponseInfo.fail("零碎异样,请分割管理员!"); } }}
在浏览器中拜访:http://localhost:8080/calc,返回后果如下:
{ "code": 500, "message": "零碎异样,请分割管理员!", "data": null}
这是因为没传递id参数,导致业务抛异样,走异样报文返回。
在浏览器中拜访:http://localhost:8080/calc?id=1,返回后果如下:
{ "code": 200, "message": "success", "data": null}
失常返回后果。
对立异样解决
在上述实例中,咱们通过try...catch的模式捕捉异样,并进行解决。在SpringBoot中,咱们能够通过RestControllerAdvice注解来定义全局异样解决,这样就无需每处都try...catch了。
@Slf4j@RestControllerAdvicepublic class ExceptionHandlerAdvice { /** * 参数格局异样解决 */ @ExceptionHandler({IllegalArgumentException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseInfo<String> badRequestException(IllegalArgumentException ex) { log.error("参数格局不非法:{}", ex.getMessage()); return new ResponseInfo<>(HttpStatus.BAD_REQUEST.value() + "", "参数格局不符!"); } /** * 权限有余异样解决 */ @ExceptionHandler({AccessDeniedException.class}) @ResponseStatus(HttpStatus.FORBIDDEN) public ResponseInfo<String> badRequestException(AccessDeniedException ex) { return new ResponseInfo<>(HttpStatus.FORBIDDEN.value() + "", ex.getMessage()); } /** * 参数缺失异样解决 */ @ExceptionHandler({MissingServletRequestParameterException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseInfo<String> badRequestException(Exception ex) { return new ResponseInfo<>(HttpStatus.BAD_REQUEST.value() + "", "短少必填参数!"); } /** * 空指针异样 */ @ExceptionHandler(NullPointerException.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) public ResponseInfo<String> handleTypeMismatchException(NullPointerException ex) { log.error("空指针异样,{}", ex.getMessage()); return ResponseInfo.fail("空指针异样"); } @ExceptionHandler(Exception.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) public ResponseInfo<String> handleUnexpectedServer(Exception ex) { log.error("零碎异样:", ex); return ResponseInfo.fail("零碎产生异样,请分割管理员"); } /** * 零碎异样解决 */ @ExceptionHandler(Throwable.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResponseInfo<String> exception(Throwable throwable) { log.error("零碎异样", throwable); return new ResponseInfo<>(HttpStatus.INTERNAL_SERVER_ERROR.value() + "零碎异样,请分割管理员!"); }}
在上述办法中,对一些常见的异样进行了对立解决。通常状况下,依据业务须要还会定义业务异样,并对业务异样进行解决,大家能够依据本人我的项目中异样的应用状况进行拓展。
对于@RestControllerAdvice的几点阐明:
- @RestControllerAdvice注解蕴含了@Component注解,会把被注解的类作为组件交给Spring来治理。
- @RestControllerAdvice注解蕴含了@ResponseBody注解,异样解决完之后给调用方输入一个JSON格局的封装数据。
- @RestControllerAdvice注解有一个basePackages属性,该属性用来拦挡哪个包中的异样信息,个别不指定,拦挡我的项目工程中的所有异样。
- 在办法上通过@ExceptionHandler注解来指定具体的异样,在办法中解决该异样信息,最初将后果通过对立的JSON构造体返回给调用者。
从新定义一个接口:
@RequestMapping("/calc1") public ResponseInfo<String> calc1(Integer id) { // 模仿异样业务代码 int num = 1 / id; log.info("计算结果num={}", num); return ResponseInfo.success(); }
在申请的时候,不传递id值,即在浏览器中拜访:
{ "code": 500, "message": "空指针异样", "data": null}
能够看到对立异样解决对空指针异样进行了拦挡解决,并返回了ExceptionHandlerAdvice中定义的对立报文格式。
小结
在应用SpringBoot或其余我的项目中,对立的报文格式和对立的异样解决都是必须的。本篇文章介绍了基于SpringBoot的实现,如果你的我的项目中采纳了其余的技术栈,则可思考对应的解决形式。同时,日常中很多相似的性能都能够对立进行解决,防止大量有效的硬编码。
博主简介:《SpringBoot技术底细》技术图书作者,热爱钻研技术,写技术干货文章。
公众号:「程序新视界」,博主的公众号,欢送关注~
技术交换:请分割博主微信号:zhuan2quan