001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.reactive.result.method.annotation; 018 019import java.util.List; 020import java.util.Map; 021 022import org.springframework.beans.factory.config.ConfigurableBeanFactory; 023import org.springframework.core.MethodParameter; 024import org.springframework.core.ReactiveAdapterRegistry; 025import org.springframework.core.convert.ConversionService; 026import org.springframework.lang.Nullable; 027import org.springframework.util.Assert; 028import org.springframework.web.bind.annotation.RequestHeader; 029import org.springframework.web.server.ServerWebExchange; 030import org.springframework.web.server.ServerWebInputException; 031 032/** 033 * Resolves method arguments annotated with {@code @RequestHeader} except for 034 * {@link Map} arguments. See {@link RequestHeaderMapMethodArgumentResolver} for 035 * details on {@link Map} arguments annotated with {@code @RequestHeader}. 036 * 037 * <p>An {@code @RequestHeader} is a named value resolved from a request header. 038 * It has a required flag and a default value to fall back on when the request 039 * header does not exist. 040 * 041 * <p>A {@link ConversionService} is invoked to apply type conversion to resolved 042 * request header values that don't yet match the method parameter type. 043 * 044 * @author Rossen Stoyanchev 045 * @since 5.0 046 * @see RequestHeaderMapMethodArgumentResolver 047 */ 048public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver { 049 050 /** 051 * Create a new {@link RequestHeaderMethodArgumentResolver} instance. 052 * @param factory a bean factory to use for resolving {@code ${...}} 053 * placeholder and {@code #{...}} SpEL expressions in default values; 054 * or {@code null} if default values are not expected to have expressions 055 * @param registry for checking reactive type wrappers 056 */ 057 public RequestHeaderMethodArgumentResolver(@Nullable ConfigurableBeanFactory factory, 058 ReactiveAdapterRegistry registry) { 059 060 super(factory, registry); 061 } 062 063 064 @Override 065 public boolean supportsParameter(MethodParameter param) { 066 return checkAnnotatedParamNoReactiveWrapper(param, RequestHeader.class, this::singleParam); 067 } 068 069 private boolean singleParam(RequestHeader annotation, Class<?> type) { 070 return !Map.class.isAssignableFrom(type); 071 } 072 073 @Override 074 protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { 075 RequestHeader ann = parameter.getParameterAnnotation(RequestHeader.class); 076 Assert.state(ann != null, "No RequestHeader annotation"); 077 return new RequestHeaderNamedValueInfo(ann); 078 } 079 080 @Override 081 protected Object resolveNamedValue(String name, MethodParameter parameter, ServerWebExchange exchange) { 082 List<String> headerValues = exchange.getRequest().getHeaders().get(name); 083 Object result = null; 084 if (headerValues != null) { 085 result = (headerValues.size() == 1 ? headerValues.get(0) : headerValues); 086 } 087 return result; 088 } 089 090 @Override 091 protected void handleMissingValue(String name, MethodParameter parameter) { 092 String type = parameter.getNestedParameterType().getSimpleName(); 093 throw new ServerWebInputException("Missing request header '" + name + "' " + 094 "for method parameter of type " + type, parameter); 095 } 096 097 098 private static final class RequestHeaderNamedValueInfo extends NamedValueInfo { 099 100 private RequestHeaderNamedValueInfo(RequestHeader annotation) { 101 super(annotation.name(), annotation.required(), annotation.defaultValue()); 102 } 103 } 104 105}