SpringMVC函数式接口Router Function,你用过吗?

开发 前端
Spring Web MVC包括WebMvc.fn,这是一种轻量级函数式编程模型,其中函数用于路由和处理请求,参数及返回值设计为不可变。

[[437513]]

概述

Spring Web MVC包括WebMvc.fn,这是一种轻量级函数式编程模型,其中函数用于路由和处理请求,参数及返回值设计为不可变。它是基于注释的编程模型的替代方案,但在其他方面还是运行在同一DispatcherServlet上。

在WebMvc.fn中,HTTP请求由HandlerFunction处理:该函数接受ServerRequest并返回ServerResponse。请求和响应对象都有不可变,提供对HTTP请求和响应的JDK 8友好访问。HandlerFunction相当于基于注释的编程模型中@RequestMapping方法的主体。

传入的请求通过RouterFunction路由到处理程序函数:一个接受ServerRequest并返回可选HandlerFunction(即可选的)的函数。当路由器函数匹配时,返回处理函数;否则为空可选。RouterFunction相当于@RequestMapping注释,但主要区别在于路由器函数不仅提供数据,还提供行为。

示例:

  1. @Configuration 
  2. public class PersonHandlerConfiguration { 
  3.      
  4.   @Bean 
  5.   public RouterFunction<ServerResponse> person() { 
  6.     return route().GET("/person", accept(MediaType.APPLICATION_JSON), request -> { 
  7.       return ServerResponse.status(HttpStatus.OK).body("Hello World") ; 
  8.     }).build() ; 
  9.   } 
  10.      

我们需要在一个@Configuration配置类中将RouterFunction暴露为Bean对象即可。

GET方法的3个参数:

第一个:请求的接口地址。

第二个:谓词也就是限定哪些调用能够匹配上,这个类似注解接口@RequestMapping参数中的consumer,params等属性一样。

第三个:HandlerFunction,这就是处理器对象了,实际的业务方法的处理对比@RequestMapping的方法体了。

HandlerFunction对象

ServerRequest和ServerResponse是不可变的接口,提供对HTTP请求和响应的JDK8友好访问,包括头、正文、方法和状态代码。

  • ServerRequest

ServerRequest提供对HTTP方法、URI、头和查询参数的访问,而对主体的访问是通过主体方法提供的。如下示例:

  1. @Bean 
  2. public RouterFunction<ServerResponse> student() { 
  3.   return route().GET("/student/{id}", accept(MediaType.APPLICATION_JSON), request -> { 
  4.     return ServerResponse.ok().body("name = " + request.param("name").get() + ", id = " + request.pathVariable("id")) ; 
  5.   }) 
  6.   .POST("/student", accept(MediaType.APPLICATION_JSON), request -> { 
  7.     return ServerResponse.ok().body(request.body(Student.class)) ; 
  8.   }) 
  9.   .build() ; 

GET接口获取查询参数和路径上的参数。

POST接口获取body主体内容。

  • ServerResponse

ServerResponse提供对HTTP响应的访问,因为它是不可变的,所以可以使用构建方法来创建它。可以使用生成器设置响应状态、添加响应标题或提供正文。

在上面的示例中已经看到了如何使用,这里就不再给出示例了。

  • Handler Classes处理器类

将处理器类单独定义到一个文件中进行相应的处理,这就与传统的@RestController注解类似了将很多的接口方法都定义在一个Controller类中。

示例:

  1. @Configuration 
  2. public class PersonHandlerConfiguration { 
  3.      
  4.   @Resource 
  5.   private PersonHandler ph ; 
  6.    
  7.   @Bean 
  8.   public RouterFunction<ServerResponse> person() { 
  9.   return route() 
  10.       .GET("/person/{id}", accept(MediaType.APPLICATION_JSON), ph::queryPerson) 
  11.       .POST("/person", accept(MediaType.APPLICATION_JSON), ph::save) 
  12.       .build() ; 
  13.   } 

 处理器类(在该类中可以注入DAO类进行相关的数据库操作) 

  1. @Component 
  2. public class PersonHandler { 
  3.      
  4.   public ServerResponse save(ServerRequest request) throws Exception { 
  5.     return ok().body(request.body(Person.class)) ; 
  6.   } 
  7.      
  8.   public ServerResponse queryPerson(ServerRequest request) throws Exception { 
  9.     return ok().body(new Person(Integer.valueOf(request.pathVariable("id")), "中国")) ; 
  10.   } 
  11.      
  • Validation验证

可以使用Spring的验证工具将验证应用于请求主体。例如,给定一个人的自定义Spring验证器实现。示例:

  1. @Component 
  2. public class PersonHandler { 
  3.      
  4.   @Resource 
  5.   private Validator validator ; 
  6.      
  7.   public ServerResponse save(ServerRequest request) throws Exception { 
  8.     Person person = request.body(Person.class) ; 
  9.     Errors errors = validate(person) ; 
  10.     if (errors == null) { 
  11.       return ok().body(person) ; 
  12.     } 
  13.     return ok().body(errors.toString()) ; 
  14.   } 
  15.  
  16.   private Errors validate(Person person) { 
  17.     Errors errors = new BeanPropertyBindingResult(person, "person"); 
  18.     validator.validate(person, errors); 
  19.     if (errors.hasErrors()) { 
  20.       return errors ;  
  21.     } 
  22.     return null ; 
  23.   } 
  24.      

需要引入依赖:

  1. <dependency> 
  2.   <groupId>org.springframework.boot</groupId> 
  3.   <artifactId>spring-boot-starter-validation</artifactId> 
  4. </dependency>

关于参数验证《Springboot项目中你的参数都在如何验证?这个starter你知道吗?》

RouterFunction

路由器功能用于将请求路由到相应的HandlerFunction。通常,你不会自己编写路由器函数,而是使用RouterFunctions类上的方法来创建路由器函数。RouterFunctions.route()(无参数)为你提供了创建路由器函数的流畅生成器,而RouterFunctions.route(RequestPredicate,HandlerFunction)则提供了创建路由器的直接方法。

通常,建议使用route() 构建,因为它为典型映射场景提供了方便的捷径,而不需要难以发现的静态导入。例如,router function builder提供方法GET(String,HandlerFunction)为GET请求创建映射;和POST(字符串、HandlerFunction)用于POST。

除了基于HTTP方法的映射之外,route builder还提供了一种在映射到请求时引入额外谓词的方法。对于每个HTTP方法,都有一个重载变量,该变量将RequestPredicate作为参数,通过它可以表示额外的约束。

在上面的示例中已经看到了在做相应GET,POST方法时传的第二个参数。

  • 谓词Predicate

我们可以编写自己的RequestPredicate,但是RequestPredicates类提供了基于请求路径、HTTP方法、内容类型等的常用实现。如下示例通过Accept来限定了能够接收的数据类型。

  1. import static org.springframework.web.servlet.function.RequestPredicates.accept; 
  2. @Bean 
  3. public RouterFunction<ServerResponse> hello() { 
  4.   return route().GET("/hello", accept(MediaType.APPLICATION_JSON), request -> { 
  5.     return ServerResponse.status(HttpStatus.OK).body("Hello World") ; 
  6.   }).build() ; 

 还可以通过and 或者 or 来添加多个谓词

accept(...).and() || or()

  • 嵌套路由

在传统的Controller定义时,可以在类上加@RequestMapping("/person")注解的方式来统一请求接口的前缀。在函数式接口中我们可以通过如下的方式设定:

  1. @Bean 
  2. public RouterFunction<ServerResponse> nestPerson() { 
  3.   return route() 
  4.               .path("/persons", builder -> builder  
  5.               .GET("/{id}", accept(MediaType.APPLICATION_JSON), ph::queryPerson) 
  6.               .POST("/save", ph::save)) 
  7.                   .build(); 

 通过path定义了路由的前缀。也可以通过如下方式:

  1. @Bean 
  2. public RouterFunction<ServerResponse> nestPerson2() { 
  3.   return route() 
  4.       .path("/persons2", b1 -> b1 
  5.         .nest(accept(MediaType.APPLICATION_JSON), b2 -> b2 
  6.           .GET("/{id}", accept(MediaType.APPLICATION_JSON), ph::queryPerson)) 
  7.         .POST("/save", ph::save)) 
  8.       .build(); 

HandlerMapping

这种函数式接口既然底层还是使用的DispatcherServlet ,那么它就会有对应的HandlerMapping和Adapter

RouterFunctionMapping:检测Spring配置中的一个或多个RouterFunctionbean,对它们进行排序,通过RouterFunction.andOther组合它们,并将请求路由到生成的组合RouterFunction。

HandlerFunctionAdapter:让DispatcherHandler调用映射到请求的HandlerFunction的简单适配器。

过滤器

可以使用路由函数生成器上的before、after或filter方法筛选处理程序函数。对于接口注释的方式,我们可以通过使用@ControllerAdvice、ServletFilter或两者来实现类似的功能。过滤器将应用于生成器生成的所有路由。这意味着嵌套路由中定义的过滤器不适用于“顶层”路由。示例:

  1. @Bean 
  2. public RouterFunction<ServerResponse> nestPerson2() { 
  3.   return route() 
  4.         .path("/persons2", b1 -> b1 
  5.           .nest(accept(MediaType.APPLICATION_JSON), b2 -> b2 
  6.             .GET("/{id}", accept(MediaType.APPLICATION_JSON), ph::queryPerson) 
  7.             .before(request -> ServerRequest.from(request).header("x-pack""123123").build())) 
  8.           .POST("/save", ph::save)) 
  9.           .after((request, response) -> { 
  10.             System.out.println("after execution..." + response.statusCode()); 
  11.             return response ; 
  12.           }) 
  13.          .filter((request, next) -> { 
  14.            if (request.pathVariable("id").equals("100")) { 
  15.              return ServerResponse.ok().body("参数错误") ; 
  16.            } else { 
  17.             return next.handle(request) ; 
  18.            } 
  19.          }) 
  20.         .build(); 
  21. public ServerResponse queryPerson(ServerRequest request) throws Exception { 
  22.   System.out.println(request.headers().header("x-pack")) ; 
  23.   return ok().body(new Person(Integer.valueOf(request.pathVariable("id")), "中国")) ; 

before:添加了自定义header信息,然后我们可以在queryPerson中获取到,并且该before只能应用于当前这个嵌套的路由中。

after:能够应用所有的路由中

filter:filter方法采用HandlerFilterFunction:采用ServerRequest和HandlerFunction并返回ServerResponse的函数。handler函数参数表示链中的下一个元素。这通常是路由到的处理程序,但如果应用了多个过滤器,它也可以是另一个过滤器。

Swagger

此时的Swagger没有用了,所以你要慎用啊。

 

责任编辑:姜华 来源: 今日头条
相关推荐

2023-09-09 12:23:24

函数式接口程序

2023-01-28 09:38:48

接口SpringMVC

2024-01-09 08:20:05

2023-12-22 16:39:47

Java函数式接口开发

2020-08-16 10:58:20

Pandaspython开发

2017-10-26 08:53:38

前端JavaScript函数式编程

2020-12-31 05:49:44

FlinkSQL函数

2012-02-06 13:52:33

JavaScript

2021-11-11 08:20:47

Vue 技巧 开发工具

2023-09-12 08:19:48

接口Controller线程

2024-03-11 08:21:49

2022-05-18 08:00:26

函数劫持JavaScript钩子函数

2022-05-05 07:25:03

Supervisor监控Python

2024-03-21 10:39:24

CIOAI

2021-05-07 13:39:20

Python工具代码

2020-09-21 14:55:15

数据库SQL技术

2020-10-13 14:54:11

机器学习技术工具

2023-11-22 07:42:01

2021-05-21 13:36:46

iOSiPhone功能

2020-01-17 20:00:25

SQL函数数据库
点赞
收藏

51CTO技术栈公众号