私はこのようなコントローラーを持っています(Kotlinに):
_@RestController
@RequestMapping("/")
class CustomerController (private val service: CustomerService) {
@GetMapping("/{id}")
fun findById(@PathVariable id: String,
@RequestHeader(value = IF_NONE_MATCH) versionHeader: String?): Mono<HttpEntity<KundeResource>> =
return service.findById(id)
.switchIfEmpty(Mono.error(NotFoundException()))
.map {
// ETag stuff ...
ok().eTag("...").body(...)
}
}
_
@ResponseStatus(code = NOT_FOUND)
で注釈が付けられた例外をスローするよりも優れたアプローチがあるかどうか疑問に思っています。
例外をスローする代わりに、メソッドの実装を次のように変更できます。
fun findById(@PathVariable id: String,
@RequestHeader(value = IF_NONE_MATCH) versionHeader: String?): Mono<ResponseEntity<KundeResource>> =
return service.findById(id)
.map {
// ETag stuff ...
ok().eTag("...").body(...)
}
.defaultIfEmpty(notFound().build())
Spring 5が安定している場合は、@ RestControllerの代わりにRouteFunction
を使用したいと思います。リクエストを処理するためにHandlerFunctionを定義してから、リクエストをHandlerFunctionにマップするためにRouteFunction
を宣言します。
public Mono<ServerResponse> get(ServerRequest req) {
return this.posts
.findById(req.pathVariable("id"))
.flatMap((post) -> ServerResponse.ok().body(Mono.just(post), Post.class))
.switchIfEmpty(ServerResponse.notFound().build());
}
完全なサンプルコードを確認してください ここ 。
Kotlinバージョンでは、リクエストを処理する関数を定義し、RouteFunctionDSL
を使用して着信リクエストをHandlerFuncationにマップします。
fun get(req: ServerRequest): Mono<ServerResponse> {
return this.posts.findById(req.pathVariable("id"))
.flatMap { post -> ok().body(Mono.just(post), Post::class.Java) }
.switchIfEmpty(notFound().build())
}
次のような式にすることができます。
fun get(req: ServerRequest): Mono<ServerResponse> = this.posts.findById(req.pathVariable("id"))
.flatMap { post -> ok().body(Mono.just(post), Post::class.Java) }
.switchIfEmpty(notFound().build())
Kotlin DSLの完全なサンプルコードを確認してください ここ 。
従来のコントローラーでREST APIを公開したい場合は、このアプローチを試してください。
まず、例外を定義します。 PostNotFoundException
。次に、それをコントローラーにスローします。
@GetMapping(value = "/{id}")
public Mono<Post> get(@PathVariable(value = "id") Long id) {
return this.posts.findById(id).switchIfEmpty(Mono.error(new PostNotFoundException(id)));
}
例外を処理するためにExceptionHandler
を定義し、それをHttpHandler
に登録します。
@Profile("default")
@Bean
public NettyContext nettyContext(ApplicationContext context) {
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context)
.exceptionHandler(exceptionHandler())
.build();
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer httpServer = HttpServer.create("localhost", this.port);
return httpServer.newHandler(adapter).block();
}
@Bean
public WebExceptionHandler exceptionHandler() {
return (ServerWebExchange exchange, Throwable ex) -> {
if (ex instanceof PostNotFoundException) {
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
}
return Mono.error(ex);
};
}
完全なコード ここを確認してください。 Spring Bootユーザーの場合は、 このサンプル を確認してください。
Update:最新のSpring 5.2で、一般的な@RestControllerAdvice
がwebfluxアプリケーションのコントローラーで機能することがわかりました。
ResponseStatusException を使用できますが、例外を拡張するだけです。
public class YourLogicException extends ResponseStatusException {
public YourLogicException(String message) {
super(HttpStatus.NOT_FOUND, message);
}
public YourLogicException(String message, Throwable cause) {
super(HttpStatus.NOT_FOUND, message, cause);
}
そしてサービス中:
public Mono<String> doLogic(Mono<YourContext> ctx) {
return ctx.map(ctx -> doSomething(ctx));
}
private String doSomething(YourContext ctx) {
try {
// some logic
} catch (Exception e) {
throw new YourLogicException("Exception message", e);
}
}
そしてその後、あなたはかわいいメッセージを持つことができます:
{ "timestamp": 00000000, "path": "/endpoint", "status": 404, "error": "Not found", "message": "Exception message" }