一个后端中国证券程序化交易接口,接口正常情况下会包含中国证券程序化交易接口,接口地址url中国证券程序化交易接口,接口的请求方式(get/post)请求数据④相应数据
在此记录一下如何构建一个完整的后端中国证券程序化交易接口,接口的过程无论一个简单还是复杂的中国证券程序化交易接口,接口,无论是对外开放的中国证券程序化交易接口,接口还是http中国证券程序化交易接口,接口,参数校验是比不可少的,因为调用者每次调用传入的参数是未知的,那如果他传进来的参数是不符合规则的,我们就没必要去执行后面的业务逻辑,只有在客户(调用者)传入的参数是合法且有效的,才必要去执行业务逻辑,这样在一定程度上也减少了程序的压力。
参数校验
最基础的直接在业务层进行参数校验比如下面一个新增用户的中国证券程序化交易接口,接口,直接在业务层进行参数校验:
@PostMapping('add')
public String inserSysUser(SysUser sysUser){
if(StringUtils.isBlank(sysUser.getName()) ){
return '用户名不能为空';
}
if(sysUser.getName().length()<8 || sysUser.getName().length()>12){
return '账号必须是8~11个字符';
}
if(!Pattern.matches('^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$', sysUser.getEmail())){
return '邮箱格式不正确';
}
sysUserService.insert(sysUser);
log.info('添加用户成功');
return 'success';
}
当我们输入可预知的参数时,运行结果下所示:这样做的弊端在于,太过繁琐,当中国证券程序化交易接口,接口数量少且每个中国证券程序化交易接口,接口的请求参数数量少的时候,假如有100个中国证券程序化交易接口,接口并且每个中国证券程序化交易接口,接口有三四十个字段需要校验,如果用这种校验方法的话,会有很对的还重复代码,还没写业务逻辑呢就一大片的参数校验代码,使得我们的中国证券程序化交易接口,接口很繁琐。那有什么方法可以简化我们的代码呢。请看下面的SpringValidator和HibernateValidator这两套Validator来进行参数校验
Validator+BindingResult进行校验Validator可以自定义校验规则并且自动完成参数校验,想要它帮我们进行参数校验的前提是在需要被校验的字段上加上注解,当然了每个注解所对应的校验规则是不同的,与此我们可以自定义验证失败后的提示信息在使用它之前,先了解一下在使用它做参数校验的时候常用的注解有哪些:
@Null限制只能为null@NotNull限制必须不为null@AssertFalse限制必须为false@AssertTrue限制必须为true@DecimalMax(valu限制必须为一个不大于指定值的数字@DecimalMin(valu限制必须为一个不小于指定值的数字@Digits(integer,fractio限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction@Future限制必须是一个将来的日期@Max(valu限制必须为一个不大于指定值的数字@Min(valu限制必须为一个不小于指定值的数字@Past限制必须是一个过去的日期@Pattern(valu限制必须符合指定的正则表达式@Size(max,mi限制字符长度必须在min到max之间@Past验证注解的元素值比当前时间早@NotEmpty验证注解的元素值不为null且不为空@NotBlank验证注解的元素值不为空,不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
知道了常用的校验注解,下面是具体的使用方法:
@Data
public class SysUser implements Serializable {
private static final long serialVersionUID = 854274168569296410L;
private Long id;
@NotNull(message = '用户名不能为空')
@Size(min = 8,max =11,message ='用户名长度必须为8~11字符')
private String name;
@NotNull(message = '年龄不能为空')
private Integer age;
@NotNull(message = '邮箱不能为空')
@Email(message = '邮箱格式不正确')
private String email;
}
校验规则和错误信息提示配置完成后,需要在中国证券程序化交易接口,接口中需要校验的参数上加上@Valid注解,并添加BindingResult参数就可完成参数的校验:
@PostMapping('addSysUser')
public String inserSysUser(@Valid SysUser sysUser, BindingResult bindingResult){
// 有参数校验失败 将错误信息封装在BindingResult对象中返回
for(ObjectError error:bindingResult.getAllErrors()){
return error.getDefaultMessage();
}
sysUserService.insert(sysUser);
log.info('添加用户成功');
return 'success';
}
这样当请求数据传递到中国证券程序化交易接口,接口的时候Validator就自动完成校验了,校验的结果就会封装到BindingResult中去,如果有错误信息我们就直接返回给前端,业务逻辑代码也根本没有执行下去。测试传一个错误的参数(邮箱给一个错误的格式)运行效果:
同直接在业务代码中做参数校验相比,利用Validator进行参数校验有以下好处:使用方便,只需要一个注解就可以完成参数校验并指定校验失败的提示信息简化代码,有眼可见的简洁,避免了一大堆的if代码块减少耦合度,使用Validator让业务层的代码只关注与业务逻辑,剔除基本的参数校验
使用Validator+BindingResult大部分情况下已经可以满足我们的需求了,不过这样做的话,需要在每个中国证券程序化交易接口,接口中后都添加一个BindingResult参数,然后通过BindingResult把错误的信息提示给前端。这样你会发现,每个中国证券程序化交易接口,接口都有这个参数,又有一大堆的重复代码,那可否想一种解决办法去掉每个中国证券程序化交易接口,接口中的BindingResult这个参数呢?请往下看!!
Validator+自动抛出异常在上面的代码中去掉BindingResult参数,去掉之后会发生什么事儿呢?还是上面的校验方法,给邮箱一个错误的格式,运行代码,效果如下:
@PostMapping('addSysUser')
public String insert(@Valid SysUser sysUser){
sysUserService.insert(sysUser);
log.info('添加用户成功');
return 'success';
}
这样就已经达到我们想要的效果了,参数校验不通过自然就不执行接下来的业务逻辑,去掉BindingResult后会自动引发异常,异常发生了自然而然就不会执行业务逻辑。也就是说,我们完全没必要添加相关BindingResult相关操作。不过事情还没有完,异常是引发了,可我们并没有编写返回错误信息的代码,那参数校验失败了会响应什么数据给前端呢?
我们来看一下刚才异常发生后中国证券程序化交易接口,接口响应的数据:
{
'timestamp': '2020-04-08T06:26:30.305+0000',
'status': 400,
'error': 'Bad Request',
'errors': [
{
'codes': [
'Email.sysUser.email',
'Email.email',
'Email.java.lang.String',
'Email'
],
'arguments': [
{
'codes': [
'sysUser.email',
'email'
],
'arguments': null,
'defaultMessage': 'email',
'code': 'email'
},
[],
{
'arguments': null,
'defaultMessage': '.*',
'codes': [
'.*'
]
}
],
'defaultMessage': '邮箱格式不正确',
'objectName': 'sysUser',
'field': 'email',
'rejectedValue': '1111',
'bindingFailure': false,
'code': 'Email'
}
],
'message': 'Validation failed for object='sysUser'. Error count: 1',
'trace': 'org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'sysUser' on field 'email': rejected value [1111]; codes [Email.sysUser.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [sysUser.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@fe2742e,.*]; default message [邮箱格式不正确]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:164)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1639)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
',
'path': '/sysUser/addSysUser'
}
直接将整个错误对象相关信息都响应给前端了!这样就很难受,不过解决这个问题也很简单,就是我们接下来要讲的全局异常处理!
全局异常处理
参数校验失败会自动引发异常,我们当然不可能再去手动捕捉异常进行处理,不然还不如用之前BindingResult方式呢。又不想手动捕捉这个异常,又要对这个异常进行处理,那正好使用SpringBoot全局异常处理来达到一劳永逸的效果!
首先需要创建一个新的类,在类上添加@ControllerAdvice或者@RestControllerAdvice注解,该类就配置成了全局处理类了然后再类中新建方法,给方法加上@ExceptionHandler注解并指定我们想要处理的异常类型,接着在方法内编写对该异常的操作逻辑,就完成了对该异常的全局处理。
@RestControllerAdvice
@Slf4j
public class ExceptionControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public String MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
// 从异常对象中拿到ObjectError对象
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
// 然后提取错误提示信息进行返回
return objectError.getDefaultMessage();
}
}
自定义异常
在很多情况下,我们需要手动抛出异常,比如在业务层当有些条件不符合业务逻辑的时候,我们就可以手动抛出异常。自定义一个异常类:
@Slf4j
@Getter
public class BizException extends RuntimeException {
private String code;
private String desc;
public BizException(String code,String desc){
this.code = code;
this.desc = desc;
}
}
在上面的全局异常处理类中添加对自定义异常类的处理:
@ExceptionHandler(BizException.class)
public String BizException(BizException e){
return e.getMessage();
}
数据统一响应
现在我们规范好了参数校验方式和异常处理方式,然而还没有规范响应数据!比如我要获取一个分页信息数据,获取成功了呢自然就返回的数据列表,获取失败了后台就会响应异常信息,即一个字符串,就是说前端开发者压根就不知道后端响应过来的数据会是啥样的!统一响应数据是前后端规范中必须要做的!
自定义统一响应体统一数据响应第一步肯定要做的就是我们自己自定义一个响应体类,无论后台是运行正常还是发生异常,响应给前端的数据格式是不变的!那么如何定义响应体呢?关于异常的设计:如何更优雅的设计异常可以参考我们自定义异常类,也来一个响应信息代码code和响应信息说明desc。
@Getter
public class ResultVO {
private String code;
private String desc;
//响应数据
private T data;
public ResultVO(T data){
this('1000','success',data);
}
public ResultVO(String code, String desc, T data) {
this.code = code;
this.desc = desc;
this.data = data;
}
}
有了上面的统一响应数据,只需要把之前的全局异常中的返回值做如下修改即可:
@ExceptionHandler(BizException.class)
public ResultVO BizException(BizException e){
return new ResultVO(e.getCode(),'响应失败',e.getDesc());
}
OK,这个异常信息响应就非常好了,状态码和响应说明还有错误提示数据都返给了前端,并且是所有异常都会返回相同的格式!异常这里搞定了,别忘了我们到中国证券程序化交易接口,接口那也要修改返回类型,我们新增一个中国证券程序化交易接口,接口好来看看效果:
@PostMapping('addSysUser')
public ResultVO insert(@Valid SysUser sysUser){
sysUserService.insert(sysUser);
log.info('添加用户成功');
return new ResultVO<>(sysUser);
}
这样无论是正确响应还是发生异常,响应数据的格式都是统一的,十分规范!数据格式是规范了,不过响应码code和响应信息desc还没有规范呀!大家发现没有,无论是正确响应,还是异常响应,响应码和响应信息是想怎么设置就怎么设置,要是10个开发人员对同一个类型的响应写10个不同的响应码,那这个统一响应体的格式规范就毫无意义!必须要将响应码和响应信息给规范起来。那接下来就该请出响应码枚举了!
响应码枚举
@Getter
public enum ResultEnum {
SUCCESS('1000','请求成功'),
FAILED('1001','请求失败'),
PARAM_FAIL('1002','参数校验失败'),
ERROR('1003','请求异常'),
;
private String code;
private String desc;
ResultEnum(String code,String desc){
this.code = code;
this.desc = desc;
}
}
然后同时修改全局异常处理的响应码设置方式:
@ExceptionHandler(BizException.class)
public ResultVO BizException(BizException e){
return new ResultVO(ResultEnum.PARAM_FAIL,e.getDesc());
}
同时需要修改统一响应方法如下:
public ResultVO(ResultEnum resultEnum,T data){
this.code = resultEnum.getCode();
this.desc = resultEnum.getDesc();
this.data = data;
}
全局处理响应数据
中国证券程序化交易接口,接口返回统一响应体+异常也返回统一响应体,其实这样已经很好了,但还是有可以优化的地方。要知道一个项目下来定义的中国证券程序化交易接口,接口搞个几百个太正常不过了,要是每一个中国证券程序化交易接口,接口返回数据时都要用响应体来包装一下好像有点麻烦,有没有办法省去这个包装过程呢?当然是有滴,还是要用到全局处理。先创建一个类加上注解使其成为全局处理类。然后继承ResponseBodyAdvice中国证券程序化交易接口,接口重写其中的方法,即可对我们的controller进行增强操作,具体看代码和注释:
@RestControllerAdvice(basePackages = {'com.kyriemtx.easycode.controller'})
public class ResponseControllerAdvice implements ResponseBodyAdvice
文章为作者独立观点,不代表股票交易接口观点