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.