• 我的订阅
  • 科技

看动画,轻松学习23种C++设计模式-霜飞浪活河汉精

类别:科技 发布时间:2022-12-20 04:02:00 来源:我是程序猿
看动画,轻松学习23种C++设计模式-霜飞浪活河汉精

控制器层代码就应该这么写,简洁优雅!

出色的控制器层逻辑

看动画,轻松学习23种C++设计模式

download:https://www.sisuoit.com/3611.html

说起控制器,相信大家都不陌生,它可以很方便的对外提供数据接口。它的定位,在我看来,是不可或缺的配角。

不可或缺是因为无论是传统的三层架构还是现在的可乐架构,控制器层还是有一席之地的,可见其必要性。

之所以说是配角,是因为控制器层的代码一般不负责具体的逻辑业务逻辑实现,而是负责接收和响应请求。

从现状看问题

控制器的主要工作如下:

看动画,轻松学习23种C++设计模式-霜飞浪活河汉精

接收请求并解析参数。

调用服务来执行特定的业务代码(可能包括参数验证)

捕捉业务逻辑异常并给出反馈。

逻辑成功执行以做出响应。

//DTO

@数据

公共类TestDTO <{p>私有整数num

私有字符串类型;

}

//服务

@服务

公共类测试服务<{p>

公共双服务(TestDTO testDTO)引发异常<{p>if (testDTO.getNum() 1) <{p>结果=结果*数量;

num-= 1;

}

返回结果;

}

抛出新异常(“无法识别的算法”);

}

}

//控制器

@RestController

公共类TestController <{p>

私有TestService testService

@PostMapping("/test ")

公共双重测试(@RequestBody TestDTO testDTO) <{p>尝试<{p>double result = this . test service . service(test dto);

返回结果;

} catch(异常e) <{p>抛出新的runtime exception(e);

}

}

@自动连线

公共DTOid setTestService(TestService TestService)<{p>this.testService = testService

}

}

如果你真的按照上面列出的工作项来开发控制器代码,会有几个问题:

看动画,轻松学习23种C++设计模式-霜飞浪活河汉精

参数检查与业务代码耦合过多,违背了单一责任原则。

同一个异常可能在多个服务中抛出,导致代码重复。

各种异常反馈和成功响应格式不统一,界面对接不友好。

转换控制器层逻辑

统一回报结构

无论项目前后是否分离,都需要统一返回值类型,让对接接口的开发者更清楚的知道这个接口的调用是否成功(我们不能简单的判断返回值是否为null,因为有些接口就是这样设计的)。

推荐一个开源免费的Spring Boot最全教程:

https://github.com/javastacks/spring-boot-best-practice

使用状态代码和状态信息来清楚地理解接口调用:

//定义返回数据结构

公共接口IResult <{p>integer getCode();

string getMessage();

}

//常用结果的枚举

公共枚举ResultEnum实现IResult <{p>成功(2001,“接口调用成功”),

VALIDATE_FAILED(2002,“参数验证失败”),

COMMON_FAILED(2003,“接口调用失败”),

禁止(2004年《无权限访问资源》);

私有整数代码;

私有字符串消息;

//省略get、set方法和构造方法

}

//统一返回数据结构

@数据

@NoArgsConstructor

@AllArgsConstructor

公共类结果<{p>私有整数代码;

私有字符串消息;

私人测试数据;

公共静态结果成功(测试数据)<{p>返回新结果(ResultEnum。SUCCESS.getCode(),ResultEnum。SUCCESS.getMessage(),数据);

}

公共静态结果成功(字符串消息,测试数据)<{p>返回新结果(ResultEnum。SUCCESS.getCode(),message,data);

}

公共静态结果失败()<{p>返回新结果(ResultEnum。COMMON_FAILED.getCode(),ResultEnum。COMMON_FAILED.getMessage(),null);

}

公共静态结果失败(字符串消息)<{p>返回新结果(ResultEnum。COMMON_FAILED.getCode(),message,null);

}

公共静态结果失败(IResult errorResult) <{p>返回新结果(errorResult.getCode()、errorResult.getMessage()、null);

}

公共静态结果实例(整数代码、字符串消息、测试数据)<{p>结果Result = new Result();

result.setCode(代码);

result.setMessage(消息);

result.setData(数据);

返回结果;

}

}

统一结构返回后,可以在控制器中使用。但是,每个控制器都要编写这样一段最终封装的逻辑,这是一个非常重复的工作。因此,我们应继续寻找进一步处理统一回报结构的方法。

统一包装处理

Spring中提供了一个类ResponseBodyAdvice,可以帮助我们实现上述需求:

公共接口响应加载Advice <{p>布尔支持(MethodParameter returnType,Class > converter type);

@Nullable

T before body write(@ Nullable T body,MethodParameter returnType,MediaType selectedContentType,Class> selectedConverterType,ServerHttpRequest,server httpresponse response);

}

Body Advice在HttpMessageConverter执行类型转换之前截获控制器返回的内容,然后在相应的处理操作之后将结果返回给客户端。

然后你可以把统一打包的工作放到这个类中:

支持:确定是否交给beforeBodyWrite方法执行,true:yes;False:不是必需的。

BeforeBodyWrite:响应的特殊处理

//如果引入了swagger或者knife4j的文档生成组件,在这里只需要扫描自己项目的包,否则无法正常生成文档。

@ RestControllerAdvice(base packages = " com . example . demo ")

公共类ResponseAdvice实现ResponseBodyAdvice <{p>@覆盖

公共布尔支持(MethodParameter returnType,Class> converterType) <{p>//如果不需要封装,可以添加一些验证手段,比如添加标记排除的注释。

返回true

}

@覆盖

public Object beforeBodyWrite(Object body,MethodParameter returnType,MediaType selectedContentType,Class> selectedConverterType,ServerHttpRequest,ServerHttpResponse response) <{p>//提供一定程度的灵活性。如果几何体已经打包,则不会打包。

if(结果的正文实例)<{p>返回正文;

}

返回result . success(body);

}

}

经过这种转换,控制器返回的数据可以统一打包,不需要对原代码做大量的改动。

参数验证

Java API的规范JSR303定义了用于验证的标准验证API,其中一个众所周知的实现是hibernate验证。

Spring validation是SpringMVC的二次封装,经常用来自动验证Spring MVC的参数,所以参数验证的代码不需要耦合业务逻辑代码。

①@ path variable和@RequestParam的参数验证

Get请求的参数接收一般依赖于这两个注释,但是url长度有限,代码可维护。如果实体尽可能传递五个以上的参数。

@PathVariable和@RequestParam参数的验证需要参数中约束声明的批注。

如果验证失败,将引发methoalgumentnotvalidieexception异常。

@ rest controller(value = " pretty test controller ")

@RequestMapping("/pretty ")

公共类TestController <{p>

私有TestService testService

@GetMapping("/{num} ")

公共整数细节(@ path variable(" num ")@ Min(1)@ Max(20)Integer num)<{p>返回num * num

}

@GetMapping("/getByEmail ")

public test to getby account(@ request param @ not blank @ Email String Email)<{p>test dto test dto = new test dto();

testDTO.setEmail(电子邮件);

返回testDTO

}

@自动连线

public void setTestService(TestService pretty TestService)<{p>this . testservice = pretty testservice;

}

}

证实原则

在SpringMVC中,有一个类叫做RequestResponseBodyMethodProcessor,它有两个功能(其实可以从名字中得到启发)。

用于解析@RequestBody批注的参数

处理@ResponseBody批注方法的返回值

解析@RequestBoyd批注参数的方法是resolveArgument。

公共类RequestResponseBodyMethodProcessor扩展了AbstractMessageConverterMethodProcessor <{p>/**

*如果验证失败,将引发MethodArgumentNotValidException。

* @如果{@link RequestBody#required()}则抛出httpmessagenoretreadableexception

* is {@code true}并且没有正文内容,或者如果没有合适的

*用于读取内容的转换器。

*/

@覆盖

公共对象resolve argument(method parameter参数,@ Nullable ModelAndViewContainer MAV container,

NativeWebRequest webRequest,@ Nullable WebDataBinderFactory binder factory)引发异常<{p>

parameter = parameter . nestedifoptional();

//将请求数据封装到标记的DTO对象中

object arg = readWithMessageConverters(webRequest,parameter,parameter . getnestedgenericparametertype());

string name = conventions . getvariablenameforparameter(参数);

if (binderFactory!= null) <{p>WebDataBinder binder = binder factory . create binder(webRequest,arg,name);

如果(arg!= null) <{p>//执行数据验证

validateifapplable(binder,parameter);

//如果验证失败,则抛出methodgargumentnotvalidieexception异常。

//如果我们自己不捕捉,最终会被DefaultHandlereXceptionResolver捕捉并处理。

if (binder.getBindingResult()。has errors()& & isBindExceptionRequired(binder,parameter)) <{p>抛出新方法argumentnotvaliexception(parameter,binder . getbinding result());

}

}

if (mavContainer!= null) <{p>MAV container . add attribute(binding result。MODEL_KEY_PREFIX + name,binder . getbinding result());

}

}

返回adaptArgumentIfNecessary,parameter);

}

}

公共抽象类AbstractMessageConverterMethodArgumentResolver实现HandlerMethodArgumentResolver <{p>/**

*如果适用,验证绑定目标。

*默认实现检查{@code @javax.validation.Valid},

* Spring的{ @ link org . Spring framework . validation . annotation . validated },

*以及名称以“Valid”开头的自定义标注。

* @param binder要使用的数据绑定器

* @param parameter方法参数描述符

* @从4.1.5开始

* @请参阅#isBindExceptionRequired

*/

受保护的void validateifapplable(web data binder binder,MethodParameter参数)<{p>//获取参数的所有注释

annotation[]annotations = parameter . getparameter annotations();

for(Annotation ann:annotations)<{p>//如果批注包含@Valid、@Validated或名称以Valid开头的批注,请检查参数。

object[]validation hints = validationannotationutils . determinevalidationhints(ann);

if (validationHints!= null) <{p>//实际的验证逻辑最终会调用Hibernate Validator来执行真正的验证。

//所以Spring验证是Hibernate验证的二次封装。

binder . validate(validation hints);

打破;

}

}

}

}

②@RequestBody参数验证

对于Post和Put请求的参数,建议使用@RequestBody请求体参数。

要验证@RequestBody参数,需要给d to对象添加一个验证条件,然后与@Validated进行匹配,完成自动验证。

如果验证失败,将引发ConstraintViolationException异常。

//DTO

@数据

公共类TestDTO <{p>@NotBlank

私有字符串用户名;

@NotBlank

@长度(最小值= 6,最大值= 20)

私有字符串密码;

@NotNull

@电子邮件

私人字符串电子邮件;

}

//控制器

@ rest controller(value = " pretty test controller ")

@RequestMapping("/pretty ")

公共类TestController <{p>

私有TestService testService

@PostMapping("/test-validation ")

public void test validation(@ request body @ Validated test dto test dto)<{p>this . testservice . save(test dto);

}

@自动连线

public void setTestService(TestService TestService)<{p>this.testService = testService

}

}

证实原则

很容易猜到AOP是通过声明方式和给参数添加注释来增强方法的。

其实Spring也是通过MethodValidationPostProcessor动态注册AOP切面,然后用MethodValidationInterceptor编织增强切点方法。

公共类MethodValidationPostProcessor扩展abstractbeanfactoryawaredispostingpostprocessor实现InitializingBean <{p>

//指定创建切面的Bean的注释。

private Class[]groups = determineValidationGroups(invocation);

executable validator exec val = this . validator . forexecutables();

method to validate = invocation . get method();

设置结果;

尝试<{p>//方法参数验证最终委托给Hibernate验证器。

//所以Spring验证是Hibernate验证的二次封装。

result = execval . validate parameters(

invocation.getThis()、methodToValidate、invocation.getArguments()、groups);

}

catch(IllegalArgumentException ex)<{p>...

}

//如果验证失败,则引发ConstraintViolationException异常。

如果(!result.isEmpty()) <{p>抛出新的ConstraintViolationException(结果);

}

//控制器方法调用

object return value = invocation . proceed();

//下面是验证返回值,流程大致如上。

result = exec val . validatereturnvalue(invocation . getthis()、methodToValidate、ReturnValue、groups);

如果(!result.isEmpty()) <{p>抛出新的ConstraintViolationException(结果);

}

返回returnValue

}

}

③用户自定义的验证规则

有时候JSR303标准提供的验证规则不能满足复杂的业务需求,你也可以自定义验证规则。

要自定义验证规则,您需要做两件事:

自定义注释类,定义错误消息和一些其他必需的内容。

批注检查器,定义决策规则

//自定义批注类

@Target({ElementType。方法,ElementType。字段,元素类型。ANNOTATION_TYPE,ElementType。构造函数,ElementType。参数})

@保留(RetentionPolicy。运行时间)

@已记录

@ Constraint(validated by = mobile validator . class)

公共@界面移动<{p>/**

*允许空白?

*/

boolean必选()默认值为true

/**

*检查失败返回的消息。

*/

Message()默认“不是手机号码格式”;

/**

*约束所需的属性,用于分组校验和扩展,将其留空即可。

*/

Class[] groups()默认{ };

类别handleBusinessException(business exception ex)<{p>返回result . failed(ex . getmessage());

}

/**

*捕捉{@code ForbiddenException}异常

*/

@ exception handler({ forbiddenexception . class })

公共结果handleForbiddenException(ForbiddenException ex)<{p>返回Result.failed(ResultEnum。禁止);

}

/**

* {@code @RequestBody}参数验证失败时引发的异常处理。

*/

@ exception handler({ method argumentnotvalidexception . class })

公共结果handlemethodgargumentnotvaliexception(methodgargumentnotvaliexception ex)<{p>binding result binding result = ex . getbinding result();

StringBuilder SB = New StringBuilder("验证失败:");

for(field error field error:binding result . getfield errors())<{p>sb.append(fieldError.getField())。追加(":")。append(field error . getdefaultmessage())。追加(",");

}

string msg = sb . tostring();

if (StringUtils.hasText(msg)) <{p>返回Result.failed(ResultEnum。VALIDATE_FAILED.getCode(),msg);

}

返回Result.failed(ResultEnum。VALIDATE _失败);

}

/**

* {@code @PathVariable}和{@code @RequestParam}参数验证失败,并引发异常处理。

*/

@ exception handler({ constraintviolationexception . class })

公共结果handleConstraintViolationException(ConstraintViolationException ex)<{p>if(string utils . hastext(ex . getmessage())<{p>返回Result.failed(ResultEnum。VALIDATE_FAILED.getCode(),ex . getmessage());

}

返回Result.failed(ResultEnum。VALIDATE _失败);

}

/**

*顶层异常捕获和统一处理,当其他异常不能处理时,选择使用。

*/

@ exception handler({ exception . class })

公共结果句柄(异常)<{p>返回result . failed(ex . getmessage());

}

}

摘要

在做了所有这些改动后,可以发现控制器的代码变得非常简洁,你可以清楚地知道每个参数和DTO的验证规则,你可以清楚地看到每个控制器方法返回什么数据,你也可以方便地反馈每个异常。

返回搜狐,查看更多

责任编辑:

以上内容为资讯信息快照,由td.fyun.cc爬虫进行采集并收录,本站未对信息做任何修改,信息内容不代表本站立场。

快照生成时间:2022-12-20 05:15:52

本站信息快照查询为非营利公共服务,如有侵权请联系我们进行删除。

信息原文地址:

城阳区“区长杯”青少年人工智能比赛开赛
...抓手、以普及提高有机结合为特色的人工智能教育“城阳模式”。截至目前,城阳区先后获评全国青少年人工智能活动特色单位、全国中学信息技术与教学融合创新学术活动优秀组织奖、教育部“央
2024-04-12 00:44:00
...宫》受南京小学生欢迎微纪录片进课堂,探索课后服务新模式□ 南京日报/紫金山新闻记者 邢虹近日,南京市夫子庙小学、南京致远外国语小学分校、南京师范大学附属小学铁北分校迎来了特别
2023-02-24 06:35:00
...量的校企合作是必经之路。秀水中专积极探索校企合作新模式,通过“引进来”“走出去”相结合,为学校的教学目标注入新活力。2018年,该校与浙江吉漫文化传播有限公司合作共建动画专业
2023-02-12 06:15:00
...术设计学院动漫专业因势而新,变革动漫专业教学内容与模式,在教学中实施多维教学模式,并充分发挥教学资源库的作用,推动多维教学模式与教学资源库融合发展,为学生提供更具创意和实践性
2023-06-25 09:05:00
...高知识掌握和运用能力、提高学生学习挑战度的新型教学模式。此外,学院持续深化课程范式改革,回归本分,深入推进“一师一优”课程建设,以提高教师教育能力为中心,以推进课堂教学改革和
2023-06-19 09:06:00
新区实验小学打造优质科教平台,31个信息科技类社团擦亮创新名片
...以像拼图一样将编程指令拼接在一起,进行互动式故事、动画、游戏等的创作。Scratch编程颇受孩子们青睐,在有人指导的情况下,六岁的孩子就可以自己拖拽脚本指令完成相应的操作,可
2023-11-20 12:50:00
海豚AI学斩获“2023年度AI科技创新产品”
...,它让原本平淡无味的数学变得很有趣。不同于传统课堂模式,它用动画来呈现数学背后的思考方式与过程,听起来不费劲也很轻松,遇到不懂的问题还有AI小白随时答疑,像玩儿一样,就能学到
2024-01-03 11:20:00
海豚AI学首发《让学习自然发生》,揭秘产品设计底层逻辑
...分析表示这背后是因为以灌输式、大班制为主的传统教育模式,容易忽略孩子个体差异和兴趣培养而导致的。对此,海豚AI学团队核心成员表示,人都是有求知欲的,没有孩子一开始就不喜欢学习
2024-01-04 15:17:00
3A级NFT游戏新星Illuvium:开启区块链新时代
...。作为一个全新的AAA级游戏,Illuvium不仅仅是对传统游戏模式的革新,更是对区块链技术的全新探索。让我们共同期待,Illuvium能够为我们带来一个全新的游戏时代,重新
2024-02-26 14:46:00
更多关于科技的资讯: