/** * Register the specified handler for the given URL paths. * @param urlPaths the URLs that the bean should be mapped to * @param beanName the name of the handler bean * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { Assert.notNull(urlPaths, "URL path array must not be null"); for (String urlPath : urlPaths) { registerHandler(urlPath, beanName); } }
urlPath = prependLeadingSlash(urlPath); PathPattern pattern = getPathPatternParser().parse(urlPath); if (this.handlerMap.containsKey(pattern)) { Object existingHandler = this.handlerMap.get(pattern); if (existingHandler != null && existingHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to [" + urlPath + "]: " + "there is already " + getHandlerDescription(existingHandler) + " mapped."); if (obtainApplicationContext().isSingleton(handlerName)) { resolvedHandler = obtainApplicationContext().getBean(handlerName); logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
/** * Look up a handler instance for the given URL lookup path. * <p>Supports direct matches, e.g. a registered "/test" matches "/test", * and various path pattern matches, e.g. a registered "/t*" matches * both "/test" and "/team". For details, see the PathPattern class. * @param lookupPath the URL the handler is mapped to * @param exchange the current exchange * @return the associated handler instance, or {@code null} if not found * @see org.springframework.web.util.pattern.PathPattern */ @Nullable protected Object lookupHandler(PathContainer lookupPath, ServerWebExchange exchange) throws Exception { List<PathPattern> matches = this.handlerMap.keySet().stream() .filter(key -> key.matches(lookupPath)) .collect(Collectors.toList()); if (matches.isEmpty()) { return null; } if (matches.size() > 1) { matches.sort(PathPattern.SPECIFICITY_COMPARATOR); if (logger.isTraceEnabled()) { logger.debug(exchange.getLogPrefix() + "Matching patterns " + matches); } } PathPattern pattern = matches.get(0); PathContainer pathWithinMapping = pattern.extractPathWithinPattern(lookupPath); return handleMatch(this.handlerMap.get(pattern), pattern, pathWithinMapping, exchange); }
private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer pathWithinMapping, ServerWebExchange exchange) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } validateHandler(handler, exchange); exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler); exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch); exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); return handler; }
return handleMatch(handler, urlPath, urlPath, exchange); if (getPathMatcher().match(pattern, urlPath)) { matches.add(pattern); else if (useTrailingSlashMatch()) { if (!pattern.endsWith("/") && getPathMatcher().match(pattern + "/", urlPath)) { matches.add(pattern +"/"); Comparator<String> comparator = getPathMatcher().getPatternComparator(urlPath); if (!matches.isEmpty()) { Collections.sort(matches, comparator); handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath); return handleMatch(handler, bestMatch, pathWithinMapping, exchange);
private Object handleMatch(Object handler, String bestMatch, String pathWithinMapping, ServerWebExchange exchange) throws Exception { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, exchange); exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch); return handler; }
if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
@Override public Mono<Object> getHandlerInternal(ServerWebExchange exchange) { String lookupPath = getPathHelper().getLookupPathForRequest(exchange); Object handler; try { handler = lookupHandler(lookupPath, exchange); } catch (Exception ex) { return Mono.error(ex); } if (handler != null && logger.isDebugEnabled()) { logger.debug("Mapping [" + lookupPath + "] to " + handler); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return Mono.justOrEmpty(handler); }
@Test public void actualRequestWithCorsConfigurationSource() throws Exception { this.handlerMapping.setCorsConfigurationSource(new CustomCorsConfigurationSource()); String origin = "http://domain2.com"; ServerWebExchange exchange = createExchange(HttpMethod.GET, "/welcome.html", origin); Object actual = this.handlerMapping.getHandler(exchange).block(); assertNotNull(actual); assertSame(this.welcomeController, actual); assertEquals("http://domain2.com", exchange.getResponse().getHeaders() .getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("true", exchange.getResponse().getHeaders() .getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); }
/** * Calls the {@link #registerHandlers} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); }
@Test public void actualRequestWithGlobalCorsConfig() throws Exception { CorsConfiguration mappedConfig = new CorsConfiguration(); mappedConfig.addAllowedOrigin("*"); this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/welcome.html", mappedConfig)); String origin = "http://domain2.com"; ServerWebExchange exchange = createExchange(HttpMethod.GET, "/welcome.html", origin); Object actual = this.handlerMapping.getHandler(exchange).block(); assertNotNull(actual); assertSame(this.welcomeController, actual); assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); }
@Override public Mono<Object> getHandlerInternal(ServerWebExchange exchange) { PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication(); Object handler; try { handler = lookupHandler(lookupPath, exchange); } catch (Exception ex) { return Mono.error(ex); } return Mono.justOrEmpty(handler); }
@Test public void actualRequestWithoutCorsConfigurationProvider() throws Exception { String origin = "http://domain2.com"; ServerWebExchange exchange = createExchange(HttpMethod.GET, "/welcome.html", origin); Object actual = this.handlerMapping.getHandler(exchange).block(); assertNotNull(actual); assertSame(this.welcomeController, actual); }
@Override public List<DispatcherHandlerMappingDescription> describe( AbstractUrlHandlerMapping handlerMapping) { return handlerMapping.getHandlerMap().entrySet().stream().map(this::describe) .collect(Collectors.toList()); }
@Test public void preFlightRequestWithCorsConfigurationSource() throws Exception { this.handlerMapping.setCorsConfigurationSource(new CustomCorsConfigurationSource()); String origin = "http://domain2.com"; ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/welcome.html", origin); Object actual = this.handlerMapping.getHandler(exchange).block(); assertNotNull(actual); assertNotSame(this.welcomeController, actual); assertEquals("http://domain2.com", exchange.getResponse().getHeaders() .getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("true", exchange.getResponse().getHeaders() .getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); }
/** * Calls the {@link #registerHandlers} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); }
@Test public void preFlightRequestWithGlobalCorsConfig() throws Exception { CorsConfiguration mappedConfig = new CorsConfiguration(); mappedConfig.addAllowedOrigin("*"); this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/welcome.html", mappedConfig)); String origin = "http://domain2.com"; ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/welcome.html", origin); Object actual = this.handlerMapping.getHandler(exchange).block(); assertNotNull(actual); assertNotSame(this.welcomeController, actual); assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); }
@Test public void preflightRequestWithoutCorsConfigurationProvider() throws Exception { String origin = "http://domain2.com"; ServerWebExchange exchange = createExchange(HttpMethod.OPTIONS, "/welcome.html", origin); Object actual = this.handlerMapping.getHandler(exchange).block(); assertNotNull(actual); assertNotSame(this.welcomeController, actual); assertNull(exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); }
@Before public void setup() { this.handlerMapping = new AbstractUrlHandlerMapping() {}; this.handlerMapping.registerHandler("/welcome.html", this.welcomeController); this.handlerMapping.registerHandler("/cors.html", this.corsController); }
@Test public void actualRequestWithCorsAwareHandler() throws Exception { String origin = "http://domain2.com"; ServerWebExchange exchange = createExchange(HttpMethod.GET, "/cors.html", origin); Object actual = this.handlerMapping.getHandler(exchange).block(); assertNotNull(actual); assertSame(this.corsController, actual); assertEquals("*", exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); }