как правильно обрабатывать ошибки весной-webflux
я проводил некоторые исследования с помощью spring-webflux, и мне нравится понимать, как правильно обрабатывать ошибки с помощью функций маршрутизатора.
Я создал небольшой проект, чтобы проверить несколько сценариев, и мне нравится получать отзывы об этом, и посмотреть, что делают другие люди.
пока то, что я делаю.
предоставление следующей функции маршрутизации:
@Component
public class HelloRouter {
@Bean
RouterFunction<?> helloRouterFunction() {
HelloHandler handler = new HelloHandler();
ErrorHandler error = new ErrorHandler();
return nest(path("/hello"),
nest(accept(APPLICATION_JSON),
route(GET("/"), handler::defaultHello)
.andRoute(POST("/"), handler::postHello)
.andRoute(GET("/{name}"), handler::getHello)
)).andOther(route(RequestPredicates.all(), error::notFound));
}
}
Я сделал это на моем обработчике
class HelloHandler {
private ErrorHandler error;
private static final String DEFAULT_VALUE = "world";
HelloHandler() {
error = new ErrorHandler();
}
private Mono<ServerResponse> getResponse(String value) {
if (value.equals("")) {
return Mono.error(new InvalidParametersException("bad parameters"));
}
return ServerResponse.ok().body(Mono.just(new HelloResponse(value)), HelloResponse.class);
}
Mono<ServerResponse> defaultHello(ServerRequest request) {
return getResponse(DEFAULT_VALUE);
}
Mono<ServerResponse> getHello(ServerRequest request) {
return getResponse(request.pathVariable("name"));
}
Mono<ServerResponse> postHello(ServerRequest request) {
return request.bodyToMono(HelloRequest.class).flatMap(helloRequest -> getResponse(helloRequest.getName()))
.onErrorResume(error::badRequest);
}
}
них мой обработчик ошибок:
class ErrorHandler {
private static Logger logger = LoggerFactory.getLogger(ErrorHandler.class);
private static BiFunction<HttpStatus,String,Mono<ServerResponse>> response =
(status,value)-> ServerResponse.status(status).body(Mono.just(new ErrorResponse(value)),
ErrorResponse.class);
Mono<ServerResponse> notFound(ServerRequest request){
return response.apply(HttpStatus.NOT_FOUND, "not found");
}
Mono<ServerResponse> badRequest(Throwable error){
logger.error("error raised", error);
return response.apply(HttpStatus.BAD_REQUEST, error.getMessage());
}
}
вот полный образец РЕПО:
5 ответов
Весна 5 обеспечивает WebHandler, а в JavaDoc есть строка:
используйте HttpWebHandlerAdapter для адаптации WebHandler к HttpHandler. WebHttpHandlerBuilder предоставляет удобный способ сделать это, а также дополнительно настроить один или несколько фильтров и / или обработчиков исключений.
В настоящее время официальная документация предполагает, что мы должны обернуть функцию маршрутизатора в HttpHandler перед загрузкой любого сервер:
HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction);
С помощью WebHttpHandlerBuilder, мы можем настроить пользовательские обработчики исключений:
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(toHttpHandler(routerFunction))
.prependExceptionHandler((serverWebExchange, exception) -> {
/* custom handling goes here */
return null;
}).build();
Если вы считаете, что функции маршрутизатора не подходят для обработки исключений, вы создаете исключения HTTP, что приведет к правильным кодам ошибок HTTP. Для Spring-Boot (также webflux) это:
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
.
.
.
new ResponseStatusException(HttpStatus.NOT_FOUND, "Collection not found");})
spring securities AccessDeniedException также будет обрабатываться правильно (403/401 коды ответа).
Если у вас есть микросервис и вы хотите использовать REST для него, это может быть хорошим вариантом, так как эти исключения http довольно близки к бизнес-логике и в этом случае следует поместить рядом с бизнес-логикой. И поскольку в микросервисе вам не нужно много businesslogic и исключений,он не должен загромождать ваш код... (но, конечно, все зависит).
почему бы не сделать это старомодным способом, бросая исключения из функций обработчика и реализуя свой собственный WebExceptionHandler, чтобы поймать их всех:
@Component
class ExceptionHandler : WebExceptionHandler {
override fun handle(exchange: ServerWebExchange?, ex: Throwable?): Mono<Void> {
/* Handle different exceptions here */
when(ex!!) {
is NoSuchElementException -> exchange!!.response.statusCode = HttpStatus.NOT_FOUND
is Exception -> exchange!!.response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
}
/* Do common thing like logging etc... */
return Mono.empty()
}
}
выше пример находится в Котлине, так как я просто скопировал его из проекта, над которым я сейчас работаю, и так как исходный вопрос не был помечен для java в любом случае.
то, что я сейчас делаю, просто предоставляет Боб мой WebExceptionHandler:
@Bean
@Order(0)
public WebExceptionHandler responseStatusExceptionHandler() {
return new MyWebExceptionHandler();
}
преимущество, чем создать HttpHandler
сам себе, что у меня есть лучшая интеграция с WebFluxConfigurer
Если я предоставлю свой собственный ServerCodecConfigurer
, например, или используя SpringSecurity
быстрый способ сопоставить ваши исключения со статусом ответа http-это бросить org.springframework.web.server.ResponseStatusException
/ или создавать свои собственные подклассы...
полный контроль над статусом ответа http + spring добавит тело ответа с возможностью добавления reason
.
в Котлине это может выглядеть так же просто, как
@Component
class MyHandler(private val myRepository: MyRepository) {
fun getById(req: ServerRequest) = req.pathVariable("id").toMono()
.map { id -> uuidFromString(id) } // throws ResponseStatusException
.flatMap { id -> noteRepository.findById(id) }
.flatMap { entity -> ok().json().body(entity.toMono()) }
.switchIfEmpty(notFound().build()) // produces 404 if not found
}
fun uuidFromString(id: String?) = try { UUID.fromString(id) } catch (e: Throwable) { throw BadRequestStatusException(e.localizedMessage) }
class BadRequestStatusException(reason: String) : ResponseStatusException(HttpStatus.BAD_REQUEST, reason)
Ответ Тела:
{
"timestamp": 1529138182607,
"path": "/api/notes/f7b.491bc-5c86-4fe6-9ad7-111",
"status": 400,
"error": "Bad Request",
"message": "For input string: \"f7b.491bc\""
}