Request -> Response
In Netty, there are typically two EventLoopGroups. BossEventLoopGroup is used to listen for requests. When an ACCEPT event is received, a socket connection is created and the socket is registered with the WorkEventLoopGroup. When the WorkEventLoop listens to the READ event, it reads the byte stream, decodes the request, searches for the corresponding processing method according to the request content, and encodes the result and sends it to the client.
In WebFlux, the process of searching the corresponding processing method according to the request content is realized through HttpHandler. The interface definition is as follows:
public interface HttpHandler {
/**
* Handle the given request and write to the response.
* @param request current request
* @param response current response
* @return indicates completion of request handling
*/
Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response);
}
Copy the code
Try from the perspective of the source code, under the analysis of the implementation of an HttpHandler and function, version: org. Springframework. The boot: spring – the boot – starter – webflux: 2.3.3. RELEASE.
1.HttpHandler
When is HttpHandler called? When a request is received.
/** NettyWebServer **/
public class NettyWebServer implements WebServer {
private final BiFunction<? super HttpServerRequest, ? super HttpServerResponse, ? extends Publisher<Void>> handler;
private DisposableServer startHttpServer(a) {
HttpServer server = this.httpServer;
if (this.routeProviders.isEmpty()) {
server = server.handle(this.handler); //1 In this case, the type is HttpServerHandle}...return server.bindNow();
}
/** HttpServerHandle **/
final BiFunction<? super HttpServerRequest, ? super HttpServerResponse, ? extends Publisher<Void>> handler;
public void onStateChange(Connection connection, State newState) {
if (newState == HttpServerState.REQUEST_RECEIVED) { //2 When the Request is received
try {
HttpServerOperations ops = (HttpServerOperations) connection;
Mono.fromDirect(handler.apply(ops, ops)) / / 3 performs an HttpHandler.subscribe(ops.disposeSubscriber()); }... }}}Copy the code
An HttpHandlerbean is automatically created when the program starts.
/** HttpHandlerAutoConfiguration **/
public class HttpHandlerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
public static class AnnotationConfig {
@Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build(); // 1 Create HttpHandler
WebFluxProperties properties = propsProvider.getIfAvailable();
// 2 Base-path can be set, similar to server.servlet.context-path for SpringMVC
if(properties ! =null && StringUtils.hasText(properties.getBasePath())) {
Map<String, HttpHandler> handlersMap = Collections.singletonMap(properties.getBasePath(), httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
returnhttpHandler; }}}Copy the code
Note step 2, where you can set base-path, similar to server.servlet.context-path for SpringMVC;
Continuing with HttpHandler creation:
/** WebHttpHandlerBuilder **/
private final WebHandler webHandler;
private final List<WebFilter> filters = new ArrayList<>();
private final List<WebExceptionHandler> exceptionHandlers = new ArrayList<>();
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
WebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(
context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class), context); // Initialize the WebHandler
List<WebFilter> webFilters = context
.getBeanProvider(WebFilter.class)
.orderedStream() / / sorting
.collect(Collectors.toList());
builder.filters(filters -> filters.addAll(webFilters)); // Initialize the WebFilter
List<WebExceptionHandler> exceptionHandlers = context
.getBeanProvider(WebExceptionHandler.class)
.orderedStream() / / sorting
.collect(Collectors.toList());
builder.exceptionHandlers(handlers -> handlers.addAll(exceptionHandlers)); // Initialize ExceptionHandler.return builder;
}
Copy the code
HttpHandler is divided into three sections:
- WebFilter: a filter of type List, so this is a filter chain;
- WebHandler: Business processing of a request;
- WebExceptionHandler: exception handling.
Continue with the build method:
/** WebHttpHandlerBuilder **/
public HttpHandler build(a) {
WebHandler decorated = new FilteringWebHandler(this.webHandler, this.filters);
decorated = new ExceptionHandlingWebHandler(decorated, this.exceptionHandlers);
HttpWebHandlerAdapter adapted = newHttpWebHandlerAdapter(decorated); .if (this.applicationContext ! =null) {
adapted.setApplicationContext(this.applicationContext);
}
adapted.afterPropertiesSet();
return adapted;
}
Copy the code
Is ultimately generated by an HttpHandler HttpWebHandlerAdapter type and its delegate is ExceptionHandlingWebHandler, its delegate is FilteringWebHandler, Its delegate is this.webHandler. The following call chain is formed:
HttpWebHandlerAdapter – > ExceptionHandlingWebHandler – > FilteringWebHandler – > enclosing webHandler.
/** HttpWebHandlerAdapter **/
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {...return getDelegate().handle(exchange) / / call ExceptionHandlingWebHandler handle
.doOnSuccess(aVoid -> logResponse(exchange))
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
.then(Mono.defer(response::setComplete));
}
/** ExceptionHandlingWebHandler **/
public Mono<Void> handle(ServerWebExchange exchange) {
Mono<Void> completion;
try {
completion = super.handle(exchange); // Call the Handle of FilteringWebHandler
}
catch (Throwable ex) {
completion = Mono.error(ex);
}
for (WebExceptionHandler handler : this.exceptionHandlers) { // If an exception is reported, handle the exception
completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
}
return completion;
}
/** FilteringWebHandler **/
public class FilteringWebHandler extends WebHandlerDecorator {
private final DefaultWebFilterChain chain;
public FilteringWebHandler(WebHandler handler, List<WebFilter> filters) {
super(handler);
this.chain = new DefaultWebFilterChain(handler, filters);
}
public List<WebFilter> getFilters(a) {
return this.chain.getFilters();
}
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return this.chain.filter(exchange); // Call the filter of DefaultWebFilterChain}}/** DefaultWebFilterChain **/
private static DefaultWebFilterChain initChain(List<WebFilter> filters, WebHandler handler) {
DefaultWebFilterChain chain = new DefaultWebFilterChain(filters, handler, null.null);
ListIterator<? extends WebFilter> iterator = filters.listIterator(filters.size());
When chain.filter(exchange) is executed, the next webfilter is called
while (iterator.hasPrevious()) {
chain = new DefaultWebFilterChain(filters, handler, iterator.previous(), chain);
}
return chain;
}
public Mono<Void> filter(ServerWebExchange exchange) {
// After executing webFilter, start executing webHandler
return Mono.defer(() ->
this.currentFilter ! =null && this.chain ! =null ?
invokeFilter(this.currentFilter, this.chain, exchange) :
this.handler.handle(exchange));
}
private Mono<Void> invokeFilter(WebFilter current, DefaultWebFilterChain chain, ServerWebExchange exchange) {
String currentName = current.getClass().getName();
return current.filter(exchange, chain).checkpoint(currentName + " [DefaultWebFilterChain]");
}
Copy the code
The above code can be represented by a graph:
The black line indicates the normal process of the request, and the red line indicates the processing process after an exception occurs.
2,WebFilter
A simple demo of WebFilter usage:
/** Log1Filter **/
@Component
@Order(100)
public class Log1Filter implements WebFilter {
private static final Logger logger = LoggerFactory.getLogger(Log1Filter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
logger.info("left: 111");
return chain.filter(exchange)
.doOnSuccess(aVoid -> logger.info("right: 111"))
.doOnError(RuntimeException.class, e -> logger.error("error1: {}", e.getMessage())); }}/** Log2Filter **/
@Component
@Order(200)
public class Log2Filter implements WebFilter {
private static final Logger logger = LoggerFactory.getLogger(Log2Filter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
logger.info("left: 222");
return chain.filter(exchange)
.doOnSuccess(aVoid -> logger.info("right: 222"))
.doOnError(RuntimeException.class, e -> logger.error("error2: {}", e.getMessage())); }}@RestController
@RequestMapping("/testfilter")
public class TestFilterController {
private static final Logger logger = LoggerFactory.getLogger(TestFilterController.class);
@GetMapping("/test-ok")
public Mono<String> testOk(@RequestParam("name") String name) {
logger.info("get name: {}", name);
return Mono.just(name);
}
@GetMapping("/test-error")
public Mono<String> testError(@RequestParam("name") String name) {
logger.info("get name: {}", name);
throw new RuntimeException("get name error"); }}Copy the code
Calling the test-OK interface returns:
2020- 08 -23 16:51:15.749 INFO 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log1Filter : left: 111
2020- 08 -23 16:51:15.751 INFO 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log2Filter : left: 222
2020- 08 -23 16:51:15.778 INFO 50589 --- [ctor-http-nio-2] m.p.d.w.controller.TestFilterController : invoke method: testOk
2020- 08 -23 16:51:15.799 INFO 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log2Filter : right: 222
2020- 08 -23 16:51:15.799 INFO 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log1Filter : right: 111
Copy the code
Calling the test-error interface returns:
2020- 08 -23 16:51:18.909 INFO 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log1Filter : left: 111
2020- 08 -23 16:51:18.910 INFO 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log2Filter : left: 222
2020- 08 -23 16:51:18.911 INFO 50589 --- [ctor-http-nio-2] m.p.d.w.controller.TestFilterController : invoke method: testError
2020- 08 -23 16:51:18.920 ERROR 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log2Filter : error2: error
2020- 08 -23 16:51:18.928 ERROR 50589 --- [ctor-http-nio-2] m.p.demos.webflux.filter.Log1Filter : error1: error
2020- 08 -23 16:51:18.966 ERROR 50589 --- [ctor-http-nio-2] a.w.r.e.AbstractErrorWebExceptionHandler : [5b86a385-2] 500 Server Error for HTTP GET "/testfilter/test-error"
Copy the code
3,WebHandler
(DispatcherHandler
)
The default WebHandler is DispatcherHandler.
/** WebFluxConfigurationSupport **/
public class WebFluxConfigurationSupport implements ApplicationContextAware {
@Bean
public DispatcherHandler webHandler(a) {
return newDispatcherHandler(); }}Copy the code
Take a look at the implementation:
/** DispatcherHandler **/
public class DispatcherHandler implements WebHandler.ApplicationContextAware {
private List<HandlerMapping> handlerMappings;
private List<HandlerAdapter> handlerAdapters;
private List<HandlerResultHandler> resultHandlers;
protected void initStrategies(ApplicationContext context) {
Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true.false);
ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
AnnotationAwareOrderComparator.sort(mappings);
this.handlerMappings = Collections.unmodifiableList(mappings);
Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerAdapter.class, true.false);
this.handlerAdapters = new ArrayList<>(adapterBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerResultHandler.class, true.false);
this.resultHandlers = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(this.resultHandlers); }}Copy the code
The DispatcherHandler is also divided into three parts:
HandlerMapping
: Finds the corresponding processing method according to the HTTP request. For example, identify a method at the Controller layer based on the URL and HttpMethod.HandlerAdapter
: calls a specific processing method;HandlerResultHandler
: Performs different processing according to the return type and sends it to the client.
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange))
.next() // Find the first available handler
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler)) // Process the request
.flatMap(result -> handleResult(exchange, result)); // Process the result
}
Copy the code
3.1 HandlerMapping
Its interface is defined as follows:
/** HandlerMapping **/
public interface HandlerMapping {
Mono<Object> getHandler(ServerWebExchange exchange);
}
/** AbstractHandlerMapping **/
public Mono<Object> getHandler(ServerWebExchange exchange) {
return getHandlerInternal(exchange).map(handler -> {
...
return handler;
});
}
Copy the code
To list several common HandlerMapping methods:
RouterFunctionMapping: Processes request routes defined by the RouterFunction interface.
RequestMappingHandlerMapping: @ Controller and @ RequestMapping defined route requests;
SimpleUrlHandlerMapping: Handles URL-based request routing;
Note that it is possible to find multiple routes for the same request; Therefore, these routes need to have a priority. Once a matching route is found, subsequent routes are not queried.
/** WebFluxConfigurationSupport **/
public class WebFluxConfigurationSupport implements ApplicationContextAware {
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0); .return mapping;
}
@Bean
public RouterFunctionMapping routerFunctionMapping(ServerCodecConfigurer serverCodecConfigurer) {
RouterFunctionMapping mapping = createRouterFunctionMapping();
mapping.setOrder(-1); // go before RequestMappingHandlerMapping
mapping.setMessageReaders(serverCodecConfigurer.getReaders());
mapping.setCorsConfigurations(getCorsConfigurations());
returnmapping; }}Copy the code
From the above code, the priority than RequestMappingHandlerMapping RouterFunctionMapping.
Write some code to verify this:
@RestController
@RequestMapping("/testrouter")
public class UrlRouterFunction implements RouterFunction {
@GetMapping("/test")
public Mono<String> test(a) {
return Mono.just("RequestMappingHandlerMapping");
}
@Override
public Mono<HandlerFunction> route(ServerRequest request) {
HandlerFunction urlHandler = req -> ServerResponse.ok().bodyValue("RouterFunctionMapping");
if (request.uri().getPath().equals("/testrouter/test")) {
return Mono.defer(() -> Mono.just(urlHandler));
} else {
returnMono.empty(); }}}Copy the code
Call request:
➜ ~ curl - L - GET X 'http://127.0.0.1:8080/testrouter/test'. RouterFunctionMappingCopy the code
3.2 HandlerAdapter
Its interface is defined as follows:
public interface HandlerAdapter {
/**
* Whether this {@code HandlerAdapter} supports the given {@code handler}.
* @param handler the handler object to check
* @return whether or not the handler is supported
*/
boolean supports(Object handler);
/** * Handle the request with the given handler. * <p>Implementations are encouraged to handle exceptions resulting from the * invocation of a handler in order and if necessary to return an alternate * result that represents an error response. * <p>Furthermore since an async {@code HandlerResult} may produce an error
* later during result handling implementations are also encouraged to
* {@link HandlerResult#setExceptionHandler(Function) set an exception
* handler} on the {@code HandlerResult} so that may also be applied later
* after result handling.
* @param exchange current server exchange
* @param handler the selected handler which must have been previously
* checked via {@link #supports(Object)}
* @return {@link Mono} that emits a single {@code HandlerResult} or none if
* the request has been fully handled and doesn't require further handling.
*/
Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler);
}
Copy the code
The main purpose of the handlerAdapter.supports () method is to determine whether the current HandlerAdapter can support the handler that is being passed. In the case of RequestMappingHandlerAdapter,
public class RequestMappingHandlerAdapter implements HandlerAdapter.ApplicationContextAware.InitializingBean {
public boolean supports(Object handler) { // Support the HandlerMethod type
return handler instanceof HandlerMethod;
}
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
InitBinderBindingContext bindingContext = new InitBinderBindingContext(
getWebBindingInitializer(), this.methodResolver.getInitBinderMethods(handlerMethod));
InvocableHandlerMethod invocableMethod = this.methodResolver.getRequestMappingMethod(handlerMethod);
Function<Throwable, Mono<HandlerResult>> exceptionHandler =
ex -> handleException(ex, handlerMethod, bindingContext, exchange);
return this.modelInitializer
.initModel(handlerMethod, bindingContext, exchange)
.then(Mono.defer(() -> invocableMethod.invoke(exchange, bindingContext))) // Call a concrete method through reflection
.doOnNext(result -> result.setExceptionHandler(exceptionHandler))
.doOnNext(result -> bindingContext.saveModel())
.onErrorResume(exceptionHandler); // Handle the exception specified by @ExceptionHandler}}Copy the code
3.3 HandlerResultHandler
Its interface is defined as follows:
public interface HandlerResultHandler {
/**
* Whether this handler supports the given {@link HandlerResult}.
* @param result the result object to check
* @return whether or not this object can use the given result
*/
boolean supports(HandlerResult result);
/**
* Process the given result modifying response headers and/or writing data
* to the response.
* @param exchange current server exchange
* @param result the result from the handling
* @return {@code Mono<Void>} to indicate when request handling is complete.
*/
Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result);
}
Copy the code
ResponseBodyResultHandler, for example, can handle by @ ResponseBody identification method:
public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandler implements HandlerResultHandler {
@Override
public boolean supports(HandlerResult result) { MethodParameter returnType = result.getReturnTypeSource(); Class<? > containingClass = returnType.getContainingClass();// Support @responseBody annotation
return (AnnotatedElementUtils.hasAnnotation(containingClass, ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
@Override
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
Object body = result.getReturnValue();
MethodParameter bodyTypeParameter = result.getReturnTypeSource();
returnwriteBody(body, bodyTypeParameter, exchange); }}Copy the code
Continue to analyze what was done to the results:
public abstract class AbstractMessageWriterResultHandler extends HandlerResultHandlerSupport {
protected Mono<Void> writeBody(@Nullable Object body, MethodParameter bodyParameter,
@Nullable MethodParameter actualParam, ServerWebExchange exchange) {
ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter); // Return type, such as MonoJustResolvableType actualType = (actualParam ! =null ? ResolvableType.forMethodParameter(actualParam) : bodyType);
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyType.resolve(), body); / / adapterPublisher<? > publisher; ResolvableType elementType; ResolvableType actualElementType;// The actual type, if encapsulated by the adapter, gets the type before encapsulation
if(adapter ! =null) {
publisher = adapter.toPublisher(body);
booleanisUnwrapped = KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(bodyParameter.getContainingClass()) && KotlinDelegate.isSuspend(bodyParameter.getMethod()) && ! COROUTINES_FLOW_CLASS_NAME.equals(bodyType.toClass().getName()); ResolvableType genericType = isUnwrapped ? bodyType : bodyType.getGeneric(); elementType = getElementType(adapter, genericType); actualElementType = elementType; }else{ publisher = Mono.justOrEmpty(body); actualElementType = body ! =null? ResolvableType.forInstance(body) : bodyType; elementType = (bodyType.toClass() == Object.class && body ! =null ? actualElementType : bodyType);
}
if (elementType.resolve() == void.class || elementType.resolve() == Void.class) {
return Mono.from((Publisher<Void>) publisher);
}
// Select the best MediaType
MediaType bestMediaType = selectMediaType(exchange, () -> getMediaTypesFor(elementType));
if(bestMediaType ! =null) { String logPrefix = exchange.getLogPrefix(); .// Iterate through HttpMessageWriter, find the first writer that can handle the object type, and encode the response to the client
for(HttpMessageWriter<? > writer : getMessageWriters()) {if (writer.canWrite(actualElementType, bestMediaType)) {
returnwriter.write((Publisher) publisher, actualType, elementType, bestMediaType, exchange.getRequest(), exchange.getResponse(), Hints.from(Hints.LOG_PREFIX_HINT, logPrefix)); }}}... }}Copy the code
Analyzing the code above, we find that most of the previous code is used to determine the returned object type, calculate MediaType, etc. The key part is to select the first HttpMessageWriter that can handle the returned object type, and then send the response.
Take a look at the HttpMessageWriter definition:
public interface HttpMessageWriter<T> {
/**
* Return the {@linkMediaType}'s that this writer supports. * Which MediaType */
List<MediaType> getWritableMediaTypes(a);
/**
* Whether the given object type is supported by this writer.
* @param elementType the type of object to check
* @param mediaType the media type for the write (possibly {@code null})
* @return {@code true} if writable, {@codeFalse} otherwise * Whether the given object type */ is supported
boolean canWrite(ResolvableType elementType, @Nullable MediaType mediaType);
/**
* Write an given stream of object to the output message.
* @param inputStream the objects to write
* @param elementType the type of objects in the stream which must have been
* previously checked via {@link #canWrite(ResolvableType, MediaType)}
* @param mediaType the content type for the write (possibly {@code null} to
* indicate that the default content type of the writer must be used)
* @param message the message to write to
* @param hints additional information about how to encode and write
* @returnIndicates Indicates Completion or error */
Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType elementType,
@Nullable MediaType mediaType, ReactiveHttpOutputMessage message, Map<String, Object> hints);
/**
* Server-side only alternative to
* {@link #write(Publisher, ResolvableType, MediaType, ReactiveHttpOutputMessage, Map)}
* with additional context available.
* @param actualType the actual return type of the method that returned the
* value; for annotated controllers, the {@link MethodParameter} can be
* accessed via {@link ResolvableType#getSource()}.
* @param elementType the type of Objects in the input stream
* @param mediaType the content type to use (possibly {@code null} indicating
* the default content type of the writer should be used)
* @param request the current request
* @param response the current response
* @return a {@link Mono} that indicates completion of writing or error
*/
default Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType actualType,
ResolvableType elementType, @Nullable MediaType mediaType, ServerHttpRequest request,
ServerHttpResponse response, Map<String, Object> hints) {
returnwrite(inputStream, elementType, mediaType, response, hints); }}Copy the code
GetMessageWriters () returns messageWriters:
If byte[] is returned, the ByteArrayEncoder encoding is selected. If String is returned and MediaType is text/plain, CharSequenceEncoder encoding is selected. JSON will select Jackson2JsonEncoder and so on.
4,WebExceptionHandler
WebExceptionHandler is mainly used to handle exceptions. But if the exception specifies the @ ExceptionHandler annotation, the actual process in RequestMappingHandlerAdapter, specific see RequestMappingHandlerAdapter handleException method.
Let’s take a look at the interface definition:
public interface WebExceptionHandler {
/**
* Handle the given exception. A completion signal through the return value
* indicates error handling is complete while an error signal indicates the
* exception is still not handled.
* @param exchange the current exchange
* @param ex the exception to handle
* @return {@code Mono<Void>} to indicate when exception handling is complete
*/
Mono<Void> handle(ServerWebExchange exchange, Throwable ex);
}
Copy the code
There are two default beans that implement this interface: DefaultErrorWebExceptionHandler and WebFluxResponseStatusExceptionHandler, the order of the former is more small, and after handling the exception will be sent to the requesting party, so basically see under the former.
public class ErrorWebFluxAutoConfiguration {
@Bean
@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
@Order(-1)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ObjectProvider
viewResolvers, ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext)
{
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
resourceProperties, this.serverProperties.getError(), applicationContext);
exceptionHandler.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()));
exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
returnexceptionHandler; }}Copy the code
If you need a custom exception, you can customize a ErrorWebExceptionHandlerbean. This part of the content is more online, will not repeat.