일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 2차원배열 정렬
- 운영체제
- circuitbreaker
- IOC
- circular dependency
- 마이크로서비스패턴
- Not Null constraint failed
- 컨트리뷰팅
- n poem
- GraphQL
- 프로젝트설정
- 쿼리셋합치기
- 함수형프로그래밍
- 일급함수
- django
- Java
- cannot import name
- 상속모델
- 객체비교
- 좋은코드란
- 주니어개발자
- Npoem
- resilience4j
- Spring
- API문서화
- 토이프로젝트
- SpringBoot
- DI
- bean-validator
- bulk_create
- Today
- Total
코딩 하는 가든
Spring Webflux의 Functional Endpoints 사용법. (with RouterFunction) 본문
Spring Webflux의 Functional Endpoints 사용법. (with RouterFunction)
가든리 2021. 5. 16. 23:32Spring Webflux에서는 엔드포인트를 매핑 하는 방식으로 기존에 사용하던 어노테이션 방식(@Controller, @RestController)이외에도 Router를 이용한 함수형 방식을 지원 합니다.
이번 글 에서는 웹플럭스에서 Functional Endpoints를 어떻게 사용하는지 알아봅니다.
당연한 이야기지만 우선 웹플럭스 의존성이 필요 합니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}
Functional Endpoint를 사용 하기 위해서는 함수형 인터페이스인 RouterFunction의 구현체를 만들어서 스프링 빈으로 등록 시켜야 합니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> routerExample(PostHandler postHandler) {
return RouterFunctions.route() //1
.GET("/post/{id}", request -> postHandler.getById(request)) //2
.POST("/post", postHandler::create) //3
.POST("/post/json", accept(MediaType.APPLICATION_JSON), postHandler::createFromJson) //4
.build(); //5
}
}
// 1 : RouterFunctions 의 route() 메소드는 RouterFunctionBuilder를 반환 합니다.
빌더 패턴을 이용하여 RouterFunction을 완성 시킬 수 있습니다.
// 2 : GET 메소드로 http GET 메소드가 들어왔을 때를 정의 해 줍니다.
uri pattern과 함수형 인터페이스를 인자로 받을 수 있습니다.
// 3 : POST 메소드 정의. 함수형 인터페이스를 postHandler::create 처럼 축약하여 사용 한 형태.
// 4 : uri pattern과 함수형 인터페이스와 더불어 accept, 혹은 content-type을 명시적으로 써줄 수 있습니다.
// 5: build() 메소드로 빌더 패턴을 완성 시켜 구체 클래스를 만들어 냅니다.
다음으로 Handler의 구현을 보겠습니다.
@Component
public class PostHandler {
/**
* 이번 프로젝트의 주제는 router의 이해이므로
* 로직은 최대한 간단하게 해서 결과만 받아 볼 수 있도록 구현
* */
// path variable 추출
public Mono<ServerResponse> getById(ServerRequest request) {
Long id = Long.parseLong(request.pathVariable("id"));
Post post = new Post(id, "garden", "hello");
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(post);
}
// x-www-form-urlencoded 추출
public Mono<ServerResponse> create(ServerRequest request) {
Mono<MultiValueMap<String, String>> formData = request.formData();
return formData.flatMap(data -> {
Map<String, String> dataMap = data.toSingleValueMap();
String title = dataMap.getOrDefault("title", null);
String content = dataMap.getOrDefault("content", null);
Post newPost = new Post(1L, title, content);
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(newPost);
});
}
// json data 추출
public Mono<ServerResponse> createFromJson(ServerRequest request) {
Mono<Post> PostMono = request.bodyToMono(Post.class);
return PostMono.flatMap(post ->
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(post));
}
}
Post.java
@AllArgsConstructor
@Setter
@Getter
public class Post {
private Long id;
private String title;
private String content;
}
각 메소드는 router에서 넘겨준 것 처럼 ServerRequest 객체를 넘겨 받습니다.
ServerRequest는 Request에 대한 모든 정보를 담고 있으며 클라에서 넘겨준 데이터를 추출 하여 사용 하면 됩니다.
pathVariable() 메소드를 통해 "post/{id}" 처럼 들어온 요청의 pathVariable을 추출 할 수 있습니다.
request.pathVariable("id")
formData() 메소드를 통해 x-www-form-urlencoded 형식의 데이터를 추출 할 수 있습니다.
formData는 Mono<Map> 을 반환 합니다.
(multipartData() 를 통해 multipart form-data 도 추출 가능)
Mono<MultiValueMap<String, String>> formData = request.formData();
bodyToMono() 메소드를 통해 json 데이터를 Dto로 매핑 시킬 수 있습니다.
Mono<Post> PostMono = request.bodyToMono(Post.class);
어노테이션 방식을 이용 하면 @RequestMapping 외에도 @Controller나 @RestController에 공통으로 들어가는 path를 써주는 형태가 있는데 RouterFunctionsBuilder의 path 메소드를 통해 공통 path를 지정 할 수 있습니다.
@Bean
public RouterFunction<ServerResponse> nestedRouter(PostHandler postHandler) {
return RouterFunctions.route()
.path("/post", builder -> builder
.GET("/{id}", postHandler::getById)
.POST("", postHandler::create)
.POST("/json", postHandler::createFromJson)
).build();
}
nest Method를 통해 공통으로 accept를 적용 시킬 수 도 있습니다.
@Bean
public RouterFunction<ServerResponse> nestedRouter2(PostHandler postHandler) {
return RouterFunctions.route()
.path("/post", builder -> builder
.nest(accept(MediaType.APPLICATION_JSON), builder2 -> builder2
.POST("/json", postHandler::createFromJson)
// .POST("", postHandler::xxx) 이 뒤로 붙는 RequestPredicate는 모두 APPLICATION_JSON이 accept
// .PUT("", postHandler::xxx)
)
).build();
}
글에서 작성된 코드는 아래에서 확인 가능 합니다.
https://github.com/97e57e/BLOG/tree/master/Spring/router-master
'Spring (boot)' 카테고리의 다른 글
Spring Webflux에 Resilience4j를 이용해 서킷브레이커 패턴 구현하기 - (2) (0) | 2021.05.05 |
---|---|
Spring Webflux에 Resilience4j를 이용해 서킷브레이커 패턴 구현하기 - (1) (0) | 2021.05.02 |
spring - validation (유효성 검사) 하기 (0) | 2020.09.07 |
spring - 스프링에서의 필터 개념 및 예제 (2) | 2020.08.31 |
spring - 의존성 주입을 받는 여러 가지 방법 (0) | 2020.08.02 |