iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Software Development

從零開始Reactive Programming- Spring系列 第 25

[Day 24] Reactive Programming - Spring WebFlux(Router)

前言

接續上一篇RestController轉成Reactive,可以看到邏輯的部分已經被抽到Handler內了,剩下就是路徑轉導就是交由Router來處理。

Router

首先看RouterFunctions.java,大部分s結尾的都是工具類別,像是CollectionsArraysRouterFunctions內有提供route,傳入predicate&handlerFunction,很直覺得理解就是符合predicate條件則交由handlerFunction處理,這邊predicateRequestPredicate,傳入的參數就是ServerRequest

public static <T extends ServerResponse> RouterFunction<T> route(
  RequestPredicate predicate, HandlerFunction<T> handlerFunction) {

 return new DefaultRouterFunction<>(predicate, handlerFunction);
}

很直覺得轉換後,將原本的annotation-based的寫法轉換如下,包含了路徑與HttpMethodServerRequest本身有提供取得路徑(path(),與method()可以取得HttpMethod

RouterFunctions.route(request ->
    request.path().equals("/router/greeting") && HttpMethod.GET.equals(request.method()), greetingHandler::allGreeting);

接下來有一個好用的工具類別RequestPredicates.java裡面可以看到很多方便的function,上面的條件判斷都可以被簡化,像是比對HttpMethodmethod(),比對路徑的path(),就像是整合後的@GetMapping()一樣,同樣有一個GET()整合了pathget,對於header也都有支援,contentTypeaccept,這些條件(RequestPredicate)可以透過RequestPredicate.java內的default function (and())來串接,剩餘其他好用的方法,之後有遇到再來介紹。

public abstract class RequestPredicates {
...
    public static RequestPredicate method(HttpMethod httpMethod) { 
	return new HttpMethodPredicate(httpMethod); 
    }

    public static RequestPredicate path(String pattern) { 
	Assert.notNull(pattern, "'pattern' must not be null"); 
	if (!pattern.isEmpty() && !pattern.startsWith("/")) { 
		pattern = "/" + pattern; 
	} 
	return pathPredicates(PathPatternParser.defaultInstance).apply(pattern); 
    }

    
    public static RequestPredicate GET(String pattern) { 
	return method(HttpMethod.GET).and(path(pattern)); 
    }

    public static RequestPredicate contentType(MediaType... mediaTypes) { 
	Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty"); 
	return new ContentTypePredicate(mediaTypes); 
    } 
    public static RequestPredicate accept(MediaType... mediaTypes) { 
	Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty"); 
	return new AcceptPredicate(mediaTypes); 
    }
...
}

經過整理後,再將另外兩個也一併改寫,再透過RouterFunction.java內的default function (and())來串聯(是不是跟上面RequestPredicate有異曲同工之妙)。

    RouterFunctions 
        .route(GET("/router/greeting/{id}"), greetingHandler::getGreeting); 
    RouterFunctions 
        .route(POST("/router/greeting").and(contentType(APPLICATION_JSON)), greetingHandler::saveGreeting); 
    RouterFunctions 
        .route(GET("/router/greeting"), greetingHandler::allGreeting);
////////////////////////////////
    RouterFunctions.route(GET("/router/greeting/{id}"), greetingHandler::getGreeting) 
        .and( 
            RouterFunctions.route( 
                POST("/router/greeting").and(contentType(APPLICATION_JSON)), 
                greetingHandler::saveGreeting)) 
        .and(RouterFunctions.route(GET("/router/greeting"), greetingHandler::allGreeting));

再來可以很明顯可以看出and之後都是接route,而且沒有一個像是掛在class層的@RequestMapping()可以把相同的路徑往上抽,所以還差最後的一步驟整合

RouterFunction.java  幫你整合好的andRoute
default RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction) { 
	return and(RouterFunctions.route(predicate, handlerFunction)); 
}

RouterFunctions.java nest(),他可以把整個共同的部分統一在第一個參數(predicate),後面就放相同類型的routerFunction


public static <T extends ServerResponse> RouterFunction<T> nest(
  RequestPredicate predicate, RouterFunction<T> routerFunction) {

 return new DefaultNestedRouterFunction<>(predicate, routerFunction);
}

成果如下,將相同的/router/greeting往上抽到第一層nest,兩個get都需要有accept,抽到第二層的nest,最後Postaccept無關所以並不在第二層nest範圍內。

GreetingRouter.java
public class GreetingRouter {

  @Bean
  public RouterFunction<ServerResponse> routerFunction(GreetingHandler handler) {
    return nest(
        path("/router/greeting"),
        nest(
                accept(APPLICATION_JSON),
                route(GET("/{id}"), handler::getGreeting)
                    .andRoute(method(HttpMethod.GET), handler::allGreeting))
            .andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::saveGreeting));
  }
}

結語

經過這兩天的學習,大致上能掌握將controller轉換成reactive的方式,相信大部分的情境應該都可以在介紹的工具類別裡面找到方法處理。

資料來源

上一篇
[Day 23] Reactive Programming - Spring WebFlux(Handler)
下一篇
[Day 25] Reactive Programming - Spring WebFlux(R2DBC)
系列文
從零開始Reactive Programming- Spring32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言