在分布式、微服务盛行的今天,绝大部分项目都采用的微服务框架,先后端分离方式。前端和后端进行交互,前端按照约定请求URL
路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。html
因此统一接口的返回值,保证接口返回值的幂等性很重要,本文主要介绍博主当前使用的结果集。前端
{
# 是否响应成功
success: true,
# 响应状态码
code: 200,
# 响应数据
data: Object
# 返回错误信息
message: "",
}
复制代码
public enum ResultCodeEnum {
/*** 通用部分 100 - 599***/
// 成功请求
SUCCESS(200, "successful"),
// 重定向
REDIRECT(301, "redirect"),
// 资源未找到
NOT_FOUND(404, "not found"),
// 服务器错误
SERVER_ERROR(500,"server error"),
/*** 这里能够根据不一样模块用不一样的区级分开错误码,例如: ***/
// 1000~1999 区间表示用户模块错误
// 2000~2999 区间表示订单模块错误
// 3000~3999 区间表示商品模块错误
// 。。。
;
/** * 响应状态码 */
private Integer code;
/** * 响应信息 */
private String message;
ResultCodeEnum(Integer code, String msg) {
this.code = code;
this.message = msg;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
复制代码
code
:响应状态码通常小伙伴们是在开发的时候须要什么,就添加什么。可是,为了规范,咱们应当参考HTTP
请求返回的状态码。java
code区间 | 类型 | 含义 | |
---|---|---|---|
1** | 100-199 | 信息 | 服务器接收到请求,须要请求者继续执行操做 |
2** | 200-299 | 成功 | 请求被成功接收并处理 |
3** | 300-399 | 重定向 | 须要进一步的操做以完成请求 |
4** | 400-499 | 客户端错误 | 请求包含语法错误或没法完成请求 |
5** | 500-599 | 服务器错误 | 服务器在处理的时候发生错误 |
常见的HTTP
状态码:git
200
- 请求成功;301
- 资源(网页等)被永久转移到其它URL
;404
- 请求的资源(网页等)不存在;500
- 内部服务器错误。message
:错误信息在发生错误时,如何友好的进行提示?github
code
给予对应的错误码定位;message
中,便于接口调用者更详细的了解错误。public class HttpResult <T> implements Serializable {
/** * 是否响应成功 */
private Boolean success;
/** * 响应状态码 */
private Integer code;
/** * 响应数据 */
private T data;
/** * 错误信息 */
private String message;
// 构造器开始
/** * 无参构造器(构造器私有,外部不能够直接建立) */
private HttpResult() {
this.code = 200;
this.success = true;
}
/** * 有参构造器 * @param obj */
private HttpResult(T obj) {
this.code = 200;
this.data = obj;
this.success = true;
}
/** * 有参构造器 * @param resultCode */
private HttpResult(ResultCodeEnum resultCode) {
this.success = false;
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
// 构造器结束
/** * 通用返回成功(没有返回结果) * @param <T> * @return */
public static<T> HttpResult<T> success(){
return new HttpResult();
}
/** * 返回成功(有返回结果) * @param data * @param <T> * @return */
public static<T> HttpResult<T> success(T data){
return new HttpResult<T>(data);
}
/** * 通用返回失败 * @param resultCode * @param <T> * @return */
public static<T> HttpResult<T> failure(ResultCodeEnum resultCode){
return new HttpResult<T>(resultCode);
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "HttpResult{" +
"success=" + success +
", code=" + code +
", data=" + data +
", message='" + message + '\'' +
'}';
}
}
复制代码
说明:spring
success
是一个Boolean
值,经过这个值,能够直接观察到该次请求是否成功;data
表示响应数据,用于请求成功后,返回客户端须要的数据。@RestController
@RequestMapping("/httpRest")
@Api(tags = "统一结果测试")
public class HttpRestController {
@ApiOperation(value = "通用返回成功(没有返回结果)", httpMethod = "GET")
@GetMapping("/success")
public HttpResult success(){
return HttpResult.success();
}
@ApiOperation(value = "返回成功(有返回结果)", httpMethod = "GET")
@GetMapping("/successWithData")
public HttpResult successWithData(){
return HttpResult.success("风尘博客");
}
@ApiOperation(value = "通用返回失败", httpMethod = "GET")
@GetMapping("/failure")
public HttpResult failure(){
return HttpResult.failure(ResultCodeEnum.NOT_FOUND);
}
}
复制代码
这里
Swagger
以及SpringMVC
的配置就没贴出来了,详见Github 示例代码。后端
http://localhost:8080/swagger-ui.html#/api
{
"code": 200,
"success": true
}
复制代码
{
"code": 200,
"data": "风尘博客",
"success": true
}
复制代码
{
"code": 404,
"message": "not found",
"success": false
}
复制代码
使用统一返回结果时,还有一种状况,就是程序的报错是因为运行时异常致使的结果,有些异常是咱们在业务中抛出的,有些是没法提早预知。springboot
所以,咱们须要定义一个统一的全局异常,在Controller
捕获全部异常,而且作适当处理,并做为一种结果返回。服务器
TokenVerificationException
),捕获针对项目或业务的异常;@ExceptionHandler
注解捕获自定义异常和通用异常;@ControllerAdvice
集成@ExceptionHandler
的方法到一个类中;public class TokenVerificationException extends RuntimeException {
/** * 错误码 */
protected Integer code;
protected String msg;
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
/** * 有参构造器,返回码在枚举类中,这里能够指定错误信息 * @param msg */
public TokenVerificationException(String msg) {
super(msg);
}
}
复制代码
@ControllerAdvice
注解是一种做用于控制层的切面通知(Advice
),可以将通用的@ExceptionHandler
、@InitBinder
和@ModelAttributes
方法收集到一个类型,并应用到全部控制器上。
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/** * 异常捕获 * @param e 捕获的异常 * @return 封装的返回对象 **/
@ExceptionHandler(Exception.class)
public HttpResult handlerException(Exception e) {
ResultCodeEnum resultCodeEnum;
// 自定义异常
if (e instanceof TokenVerificationException) {
resultCodeEnum = ResultCodeEnum.TOKEN_VERIFICATION_ERROR;
resultCodeEnum.setMessage(getConstraintViolationErrMsg(e));
log.error("tokenVerificationException:{}", resultCodeEnum.getMessage());
}else {
// 其余异常,当咱们定义了多个异常时,这里能够增长判断和记录
resultCodeEnum = ResultCodeEnum.SERVER_ERROR;
resultCodeEnum.setMessage(e.getMessage());
log.error("common exception:{}", JSON.toJSONString(e));
}
return HttpResult.failure(resultCodeEnum);
}
/** * 获取错误信息 * @param ex * @return */
private String getConstraintViolationErrMsg(Exception ex) {
// validTest1.id: id必须为正数
// validTest1.id: id必须为正数, validTest1.name: 长度必须在有效范围内
String message = ex.getMessage();
try {
int startIdx = message.indexOf(": ");
if (startIdx < 0) {
startIdx = 0;
}
int endIdx = message.indexOf(", ");
if (endIdx < 0) {
endIdx = message.length();
}
message = message.substring(startIdx, endIdx);
return message;
} catch (Throwable throwable) {
log.info("ex caught", throwable);
return message;
}
}
}
复制代码
@RestControllerAdvice
,等同于@ControllerAdvice
+ @ResponseBody
@RestController
@RequestMapping("/exception")
@Api(tags = "异常测试接口")
public class ExceptionRestController {
@ApiOperation(value = "业务异常(token 异常)", httpMethod = "GET")
@GetMapping("/token")
public HttpResult token() {
// 模拟业务层抛出 token 异常
throw new TokenVerificationException("token 已通过期");
}
@ApiOperation(value = "其余异常", httpMethod = "GET")
@GetMapping("/errorException")
public HttpResult errorException() {
//这里故意形成一个其余异常,而且不进行处理
Integer.parseInt("abc123");
return HttpResult.success();
}
}
复制代码
http://localhost:8080/swagger-ui.html#/
{
"code": 500,
"message": "For input string: \"abc123\"",
"success": false
}
复制代码
{
"code": 4000,
"message": "token 已通过期",
"success": false
}
复制代码
@RestControllerAdvice
和@ExceptionHandler
会捕获全部Rest
接口的异常并封装成咱们定义的HttpResult
的结果集返回,可是:处理不了拦截器里的异常
没有哪种方案是适用于各类状况的,如:分页状况,还能够增长返回分页结果的静态方案,具体实现,这里就不展现了。因此,适合本身的,具备必定可读性都是很好的,欢迎持不一样意见的大佬给出意见建议。