过滤功能
由路由器函数进行映射的路由可以通过调用 RouterFunction.filter(FilterFunction) 来进行过滤, 这里的 FilterFunction 其实就是一个 BiFunction, Response>。函数的处理器(handler)参数代表的就是整个链条中的下一项: 这是一个典型的 HandlerFunction, 但如果附加了多个过滤器的话,它也能够是另外的一个 FilterFunction。让我们向路由添加一个日志过滤器:
1 2 3 4 5 6 7 8 9 10 | RouterFunction route =
route(GET( "/hello-world" ), handler::helloWorld)
.and(route(GET( "/the-answer" ), handler::theAnswer))
.filter((request, next) -> {
System.out.println( "Before handler invocation: " + request.path());
Response response = next.handle(request);
Object body = response.body();
System.out.println( "After handler invocation: " + body);
return response;
});
|
注意这里对下一个处理器的调用时可选的。这个在安全或者缓存的场景中是很有用的 (例如只在用户拥有足够的权限时才调用 next)。
因为 route 是一个没有被绑定的路由器函数,我们就得知道接下来的处理会返回什么类型的响应消息。这就是为什么我们在过滤器中要以一个 Response 结束, 那样它就会可能有一个 String 类型的响应消息体。我们可以通过使用 RouterFunction.andSame() 而不是 and() 来完成这件事情。这个组合方法要求路由器函数参数是同一个类型。例如,我们可以让所有的响应消息变成小写的文本形式:
1 2 3 4 5 6 7 8 | RouterFunction route =
route(GET( "/hello-world" ), handler::helloWorld)
.andSame(route(GET( "/the-answer" ), handler::theAnswer))
.filter((request, next) -> {
Response response = next.handle(request);
String newBody = response.body().toUpperCase();
return Response.from(response).body(fromObject(newBody));
});
|
使用注解的话,类似的功能可以使用 @ControllerAdvice 或者是一个 ServletFilter 来实现。
运行一个服务端
所有这些都很不错,不过仍然有一块欠缺:我们如何实际地将这些函数在一个 HTTP 服务器中跑起来呢? 答案毋庸置疑,那就是通过调用另外的一个函数。 你可以通过使用 RouterFunctions.toHttpHandler() 来将一个路由器函数转换成 HttpHandler。HttpHandler 是 Spring 5.0 M1 中引入的一个响应式抽象: 它能让你运行许多的响应式运行时: Reactor Netty, RxNetty, Servlet 3.1+, 以及 Undertow。在本示例中,我们已经展示了在 Reactor Netty 中运行一个路由会是什么样子的。对于 Tomcat 来说则是像下面这个样子:
1 2 3 4 5 6 7 8 | HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
HttpServlet servlet = new ServletHttpHandlerAdapter(httpHandler);
Tomcat server = new Tomcat();
Context rootContext = server.addContext( "" ,
System.getProperty( "java.io.tmpdir" ));
Tomcat.addServlet(rootContext, "servlet" , servlet);
rootContext.addServletMapping( "/" , "servlet" );
tomcatServer.start();
|
需要注意的意见事情就是上面的东西并不依赖于一个 Spring 应用程序上下文。就跟 JdbcTemplate 以及其它 Spring 的工具类那样, 要不要使用应用程序上下文是可以选的: 你可以将你的处理器和路由器函数在一个上下文中进行绑定,但并不是必须的。
还要注意的就是你也可以将一个路由器函数转换到一个 HandlerMapping中去,那样就它可以在一个 DispatcherHandler (可能是跟响应式的 @Controllers 并行)中运行了。