Spring Boot 框架的诞生,使得 Java 框架整合变得越来越容易了,只要加入对应的 starter 和 简单的必要设置 就可以轻松的完成。框架整合完成后就可以开始愉快的开发了。如果我们整合的是一个基于 Web 的框架,那么整合完框架后,就需要引入几个小的功能到项目中,分别是 统一返回格式、参数校验 和 异常处理。


准备工作

        我们创建一个 Spring Boot 的项目,然后引入 Lombok 和 Spring Web 即可。因为这几个功能都不需要使用数据库,只需要一个 Controller 即可。Controller 的代码如下:

@RestController
public class TestController{ @PostMapping​("test")public​ Object ​test(){return "test";}}

        我们可以简单的测试一下这个 Controller 是否可以请求成功,如果请求成功了,那么就可以开始我们的几个通用的小功能了。

统一返回格式

        通常境况下,Controller 的返回值具有一定的格式,这样的好处是前后端程序员能在一个统一的格式下工作,如果后端程序员返回各种各样的格式,就会给前端带来不必要的麻烦。

        一般情况下 Controller 的返回格式中包含 状态码、消息提示 和 数据,大体格式如下。

{"code":​ 状态码​,"msg":​ 消息提示,
"data":​ T
}

        其中,code 是状态码,该状态码是 Enum(枚举值),可以根据业务去划分不同的状态码,msg 是消息提示,一般是状态码对应的消息提示,最后 data 是数据,数据通常是一个 Object 或 List。


        先定义一个状态码的枚举类出来,代码如下。

publicenum​ StateCodeEnum
{SUCCESS(1, "请求成功"),FAIL(0, "请求失败");
private int​ code​;private​ String msg​;
StateCodeEnum(int​ code​,​ String msg​){this.​code ​=​ code​;this.​msg ​=​ msg​;}
public int getCode(){return​ code​;}
public void setCode(int​ code​){this.​code ​=​ code​;}
public​ String ​getMsg(){return​ msg​;}
public void setMsg(​String msg​){this.​msg ​=​ msg​;}}

        有了状态枚举类后,还需要一个构造它的包装类,代码如下。

@Data
@Builder
public class ResultWrapper<​T​> implements Serializable{private int​ code​;private​ String msg​;private​ T data​;

public static​ ResultWrapper​.​ResultWrapperBuilder ​getSuccessBuilder(){return​ ResultWrapper
.builder().code(​StateCodeEnum​.​SUCCESS​.getCode()).msg(​StateCodeEnum​.​SUCCESS​.getMsg());}

public static​ ResultWrapper​.​ResultWrapperBuilder ​getFailBuilder(){return​ ResultWrapper
.builder().code(​StateCodeEnum​.​FAIL​.getCode()).msg(​StateCodeEnum​.​FAIL​.getMsg());}}

        用 Lombok 可以方便的构建一个具有 “建造者模式” 这样的工具。有了上面的代码,我们就完成了返回格式的功能。我们来测试一下。

@PostMapping​("success")public​ ResultWrapper ​success(){return​ ResultWrapper​.getSuccessBuilder().data("").build();}
@PostMapping​("fail")public​ ResultWrapper ​fail(){return​ ResultWrapper​.getFailBuilder().data("").build();}

        我们用 PostMan 来请求一下 success 接口,返回结果如下。

{"code":0,"msg":"请求成功","data":""}

        可以看到,返回的数据格式和我们想要的格式一样。


参数校验

        通常我们调用接口,都会给接口传递相应的参数,外部的输入通常是需要进行校验的。我们来写一个简单的参数校验的 Demo。

        首先需要引入 Validation 的 starter,这是一个用于对数据校验的依赖。然后,写一个简单的 DTO 类,用于接收请求传递来的参数,代码如下。

@Data
public class User{private​ String username​;private​ String password​;}

        增加一个接口用来接受请求,代码如下:

@PostMapping​("login")public​ ResultWrapper ​login(​@RequestBody User user​){return​ ResultWrapper​.getSuccessBuilder().data(​user​).build();}

        该接口把接收到的参数直接再返回给客户端,并没有做任何的处理。用 PostMan 来请求一下该接口,请求内容和返回结果如下。

        请求内容如下:

{"username":"username","password":"password"}

        返回结果如下:

{"code":1,"msg":"请求成功","data":{"username":"username","password":"password"}}

        通常情况下,我们会对 username 和 password 有一些校验规则,比如 username 和 password 不能为空,password 的长度必须大于 8 且小于 20 等一些规则。我们可以使用刚才添加的 Validation 提供的注解添加在实体类的属性上,代码如下。

@Data
public class User{ @NotNull
@NotEmpty
private​ String username​; @NotNull
@NotEmpty
@Size​(​min ​=8,​ max ​=20,​ message ​= "密码长度在8~20之间")private​ String password​;}

        这里使用 @NotNull、@NotEmpty 和 @Size 三个注解。除了在实体类的属性上加相应的注解外,还需要在接口上加 @Valid 注解。然后,我们用 PostMan 进行测试。

        请求内容如下:

{"username":"username","password":"pwd"}

        返回结果如下:

{"timestamp":"2021-01-16T00:56:47.936+00:00","status":400,"error":"Bad Request","message":"","path":"/login"}

        可以看到,我们把请求参数 password 的值设置成了 pwd,导致请求失败了,返回的结果是状态是 400,我们需要去看一下控制台中的输出。输出提示如下:

Java 项目中几个必不可少的小功能_状态码

        从控制台中可以看到,提示了 [密码长度在8~20之间],导致请求错误的原因就在这里了。因为它没有把校验提示返回给 PostMan,而是输出到了控制台中。这个问题的解决就需要使用异常处理了。


异常处理

        前面的参数校验是可行的,但是参数校验的错误提示并没有返回到客户端而是输出到了控制台中。这里可以通过异常处理将错误提示返回给客户端,代码如下。

@ControllerAdvice
public class ValidateHandler extends ResponseEntityExceptionHandler
{
@Override

protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {


StringBuilder sb ​=newStringBuilder();

for(​FieldError fieldError ​:​ ex​.getBindingResult().getFieldErrors()){

String defaultMessage ​=​ fieldError​.getDefaultMessage();

sb.append(defaultMessage);


}returnnewResponseEntity(​ ResultWrapper​.builder().code(3).msg(​sb​.toString()).build(),​ status​);}}

        @ControllerAdvice 注解可以将该类应用的到所有的 Controller 中,ResponseEntityExceptionHandler 用于处理 SpringMVC 中请求异常的处理,handleMethodArugmentNotValid 方法用来处理参数无效的异常。对代码进行测试,结果如下。

{
"code":3,
"msg":"密码长度在8~20之间",
"data":null
}


        通过异常处理将参数校验的消息返回给了客户端。


总结

        统一的返回格式、参数的校验、异常的处理是项目中最常用的功能,留着,也许将来你会用到。

Java 项目中几个必不可少的小功能_状态码_02