SpringBoot2参数管理实践,⼊参出参与校验
⼀、参数管理
在编程系统中,为了能写出良好的代码,会根据是各种设计模式、原则、约束等去规范代码,从⽽提⾼代码的可读性、复⽤性、可修改,实际上个⼈觉得,如果写出的代码很好,即别⼈修改也⽆法破坏原作者的思路和封装,这应该是⾮常⾼⽔准。
但是在⽇常开发中,碍于很多客观因素,很少有时间去不断思考和优化代码,所以只能从实际情况的⾓度去思考如何构建系统代码,保证以后⾃⼰还能读懂⾃⼰的代码,在⾃⼰的⼏年编程中,实际会考虑如下⼏个⽅⾯:代码层级管理,命名和注释统⼀,合理的设计业务数据库,明确参数风格。
这⾥就来聊⼀下参数管理,围绕:⼊参、校验、返参三个⽅⾯内容。
如何理解代码规范这个概念:即⼤多数开发认同,愿意遵守的约束,例如Spring框架和Mvc模式对于⼯程的管理,《Java开发⼿册》中对于业务开发的规定,其根本⽬的都是想避免随着业务发展,代码演变到⽆法维护的境界。
⼆、接收参数
接收参数⽅式有很多种,List,Map,Object等等,但是为了明确参数的语义,通常都需要设计参数对象的结构并且遵守⼀定的规范,例如明确禁⽌Map接收参数:
Rest风格接收单个ID参数:
@GetMapping("/param/single/{id}")
public String paramSingle (@PathVariable Integer id){
return "Resp:"+id ;
}
接收多个指定的参数:
@GetMapping("/param/multi")
public String paramMulti (@RequestParam("key") String key, @RequestParam("var") String var){
return "Resp:"+key+var ;
}
基于Java包装对象⼊参:
@PostMapping("/param/wrap")
public ParamIn paramWrap (@RequestBody ParamIn paramIn){
return paramIn ;
}
-- 参数对象实体
public class ParamIn {
private Integer id ;
private String key ;
private String var ;
private String name ;
}
以上是在开发中常⽤的⼏种接参⽅式,这⾥通常会遵守下⾯⼏个习惯:
参数语义:明确接收参数的作⽤;
个数限制:参数超过三个使⽤包装对象;
避免多个接⼝使⽤单个包装对象⼊参;
避免包装对象主体过于复杂;
参数接收并没有很复杂的约束,整体上也⽐较容易遵守,通常的问题在于处理较⼤主体对象时,容易产⽣⼀个包装对象被多处复⽤,进⽽导致对象字段属性很多,这种情况在复杂业务中尤其容易出现,这种对象并不利于web层接⼝使⽤,或者很多时候都会在业务层和接⼝层混⽤对象;
在业务层封装复杂的BO对象来降低业务管理的复杂度,这是合理常见的操作,可以在web接⼝层⾯根据接⼝功能各⾃管理⼊参主体,在业务实现的过程中,再传⼊BO对象中。
避免复杂的业务包装对象在各个层乱飘,如果多个接⼝⼊参都是同⼀个复杂的对象,很容易让开发⼈员迷茫。
三、响应参数
与参数接收相对应的就是参数响应,参数响应通常具有明确的约束规范:响应主体数据,响应码,描述信息。通常来说就是这样三个核⼼要素。
响应参数主体:
这⾥泛型的使⽤通常⽤来做主体数据的接收。
public class Resp<T> {
private int code ;
private String msg ;
private T data ;
public static <T> Resp<T> ok (T data) {
Resp<T> result = new Resp<>(HttpStatus.OK);
result.setData(data);
return result ;
}
public Resp (HttpStatus httpStatus) {
this.msg = ReasonPhrase();
}
public Resp(int code, String msg, T data) {
this.msg = msg;
this.data = data;
}
}
Code状态码
即接⼝状态,建议参照并遵守HttpStatus中状态码的描述,这是开发普遍遵守的规范,如果不满⾜业务需求,在适当⾃定义部分编码,可以完全⾃定义⼀套响应码,但是没太多必要。
Msg描述
描述接⼝的响应的Msg可能就是:成功或失败,更多的时候是需要处理业务异常的提⽰信息,例如单号不存在,账号冻结等等,通常需要从业务异常中捕获提⽰信息,并响应页⾯,或者⼊参校验不通过的描述。
Data数据
接⼝响应的主体数据,不同的业务响应的对象肯定不同,所以这⾥基于泛型机制接收即可,再以JSON格式响应页⾯。
参考案例
接⼝返参:
@PostMapping("/resp/wrap")
public Resp<KeyValue> respWrap (@RequestBody KeyValue keyValue){
return Resp.ok(keyValue) ;
}
响应格式:
{
"code": 200,
"msg": "OK",
"data": {
"key": "hello",
"value": "world"
}
}
四、参数校验
参数接收和响应相对都不是复杂的,⽐较难处理的就是参数校验:⼊参约束校验,业务合法性校验,响应参数⾮空⾮null校验,等各种场景。
在系统运⾏过程中,任何参数都不是绝对可靠的,所以参数校验随处可见,不同场景下的参数校验,都有其必要性,但其根本⽬的都是为了给到请求端提⽰信息,快速打断流程,快速响应。
1、借鉴参考
很多封装思想,设计模式,或者这⾥说的参数校验,都可以参考现有Java源码或者优秀的框架,这是⼀个应该具备的基础意识。
Java原⽣⽅法之java.lang.Thread线程:
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0();
b.interrupt(this);
return;
}
}
interrupt0();
}
在Java源码中,⼤部分都是采⽤原⽣的if判断⽅式,对参数执⾏校验
Spring框架之org.springframework.util.ClassUtils⼯具类部分代码:
public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {
Class<?> clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
clazz = (name);
}
if (clazz != null) {
return clazz;
}
}
在Spring框架中除了基础的if判断之外,还封装⼀个org.springframework.util.Assert断⾔⼯具类。
2、常⽤校验⽅式
If判断
@GetMapping("/check/base")
public String baseCheck (@RequestParam("var") String var){
if (var == null) {
return var+" is null" ;
}
if ("".equals(var)){
return var+" is empty" ;
}
validation框架if ("hello".equals(var)){
return var+" sensitive word " ;
}
return var + " through " ;
}
这种判断在代码中很常见,只是⼀旦遇到校验的主体对象很⼤,并且在分布式的环境中,需要重复写if判断的话,容易出错是⼀个⽅⾯,对开发⼈员的耐⼼考验是另⼀个⽅⾯。
Valid组件
在早⼏年的时候,⽐较流⾏的常⽤校验组件Hibernate-Validator,后来兴起的Validation-Api,据说是参考前者实现,不过这并不重要,⼆者都简化了对JavaBean的校验机制。
基于注解的⽅式,标记Java对象的字段属性,并设定如果校验失败的提⽰信息。
public class JavaValid {
@NotNull(message="ID不能为空")
private Integer id ;
@Email(message="邮箱格式异常")
private String email ;
@NotEmpty(message = "字段不能为空")
@Size(min = 2,max = 10,message = "字段长度不合理")
private String data ;
}
校验结果打印:
public class JavaValidTest {
private static Validator validator ;
@BeforeClass
public static void beforeBuild (){
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void checkValid (){
JavaValid valid = new JavaValid(null,"email","data") ;
Set<ConstraintViolation<JavaValid>> validateInfo = validator.validate(valid) ;
/
/ 打印校验结果
validateInfo.stream().forEach(validObj -> {
System.out.println("validateInfo:"+Message());
});
}
}
接⼝使⽤:
@PostMapping("/java/valid")
public JavaValid javaValid (@RequestBody @Valid JavaValid javaValid,BindingResult errorMsg){
if (errorMsg.hasErrors()){
List<ObjectError> objectErrors = AllErrors() ;
objectErrors.stream().forEach(objectError -> {
logger.info("CheckRes:{}",DefaultMessage());
});
}
return javaValid ;
}
这种校验机制基于注解⽅式,可以⼤幅度简化普通的⼊参校验,但是对业务参数的合法校验并不适应,例如常见的ID不存在,状态拦截等。Assert断⾔
关于Assert断⾔⽅式,起初是在单元测试中常见,后来在各种优秀的框架中开始常见,例如Spring、Mybatis等,然后就开始出现在业务代码中:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论