All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.reactive.DispatcherHandler Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.reactive;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.cors.reactive.PreFlightRequestHandler;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;

/**
 * Central dispatcher for HTTP request handlers/controllers. Dispatches to
 * registered handlers for processing a request, providing convenient mapping
 * facilities.
 *
 * 

{@code DispatcherHandler} discovers the delegate components it needs from * Spring configuration. It detects the following in the application context: *

    *
  • {@link HandlerMapping} -- map requests to handler objects *
  • {@link HandlerAdapter} -- for using any handler interface *
  • {@link HandlerResultHandler} -- process handler return values *
* *

{@code DispatcherHandler} is also designed to be a Spring bean itself and * implements {@link ApplicationContextAware} for access to the context it runs * in. If {@code DispatcherHandler} is declared as a bean with the name * "webHandler", it is discovered by * {@link WebHttpHandlerBuilder#applicationContext(ApplicationContext)} which * puts together a processing chain together with {@code WebFilter}, * {@code WebExceptionHandler} and others. * *

A {@code DispatcherHandler} bean declaration is included in * {@link org.springframework.web.reactive.config.EnableWebFlux @EnableWebFlux} * configuration. * * @author Rossen Stoyanchev * @author Sebastien Deleuze * @author Juergen Hoeller * @since 5.0 * @see WebHttpHandlerBuilder#applicationContext(ApplicationContext) */ public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware { @Nullable private List handlerMappings; @Nullable private List handlerAdapters; @Nullable private List resultHandlers; /** * Create a new {@code DispatcherHandler} which needs to be configured with * an {@link ApplicationContext} through {@link #setApplicationContext}. */ public DispatcherHandler() { } /** * Create a new {@code DispatcherHandler} for the given {@link ApplicationContext}. * @param applicationContext the application context to find the handler beans in */ public DispatcherHandler(ApplicationContext applicationContext) { initStrategies(applicationContext); } /** * Return all {@link HandlerMapping} beans detected by type in the * {@link #setApplicationContext injected context} and also * {@link AnnotationAwareOrderComparator#sort(List) sorted}. *

Note: This method may return {@code null} if invoked * prior to {@link #setApplicationContext(ApplicationContext)}. * @return immutable list with the configured mappings or {@code null} */ @Nullable public final List getHandlerMappings() { return this.handlerMappings; } @Override public void setApplicationContext(ApplicationContext applicationContext) { initStrategies(applicationContext); } protected void initStrategies(ApplicationContext context) { Map mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerMapping.class, true, false); ArrayList mappings = new ArrayList<>(mappingBeans.values()); AnnotationAwareOrderComparator.sort(mappings); this.handlerMappings = Collections.unmodifiableList(mappings); Map adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerAdapter.class, true, false); this.handlerAdapters = new ArrayList<>(adapterBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerAdapters); Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerResultHandler.class, true, false); this.resultHandlers = new ArrayList<>(beans.values()); AnnotationAwareOrderComparator.sort(this.resultHandlers); } @Override public Mono handle(ServerWebExchange exchange) { if (this.handlerMappings == null) { return createNotFoundError(); } if (CorsUtils.isPreFlightRequest(exchange.getRequest())) { return handlePreFlight(exchange); } return Flux.fromIterable(this.handlerMappings) .concatMap(mapping -> mapping.getHandler(exchange)) .next() .switchIfEmpty(createNotFoundError()) .onErrorResume(ex -> handleDispatchError(exchange, ex)) .flatMap(handler -> handleRequestWith(exchange, handler)); } private Mono createNotFoundError() { return Mono.defer(() -> { Exception ex = new ResponseStatusException(HttpStatus.NOT_FOUND); return Mono.error(ex); }); } private Mono handleDispatchError(ServerWebExchange exchange, Throwable ex) { Mono resultMono = Mono.error(ex); if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter instanceof DispatchExceptionHandler exceptionHandler) { resultMono = resultMono.onErrorResume(ex2 -> exceptionHandler.handleError(exchange, ex2)); } } } return resultMono.flatMap(result -> handleResult(exchange, result)); } private Mono handleRequestWith(ServerWebExchange exchange, Object handler) { if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) { return Mono.empty(); // CORS rejection } if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter.handle(exchange, handler) .flatMap(result -> handleResult(exchange, result)); } } } return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); } private Mono handleResult(ServerWebExchange exchange, HandlerResult result) { Mono resultMono = doHandleResult(exchange, result, "Handler " + result.getHandler()); if (result.getExceptionHandler() != null) { resultMono = resultMono.onErrorResume(ex -> result.getExceptionHandler().handleError(exchange, ex).flatMap(result2 -> doHandleResult(exchange, result2, "Exception handler " + result2.getHandler() + ", error=\"" + ex.getMessage() + "\""))); } return resultMono; } private Mono doHandleResult( ServerWebExchange exchange, HandlerResult handlerResult, String description) { if (this.resultHandlers != null) { for (HandlerResultHandler resultHandler : this.resultHandlers) { if (resultHandler.supports(handlerResult)) { description += " [DispatcherHandler]"; return resultHandler.handleResult(exchange, handlerResult).checkpoint(description); } } } return Mono.error(new IllegalStateException( "No HandlerResultHandler for " + handlerResult.getReturnValue())); } @Override public Mono handlePreFlight(ServerWebExchange exchange) { return Flux.fromIterable(this.handlerMappings != null ? this.handlerMappings : Collections.emptyList()) .concatMap(mapping -> mapping.getHandler(exchange)) .switchIfEmpty(Mono.fromRunnable(() -> exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN))) .next() .then(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy