/* * Copyright 2002-2017 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 * * http://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.servlet.mvc.method.annotation; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.lang.reflect.Method; import java.security.Principal; import java.time.ZoneId; import java.util.Locale; import java.util.TimeZone; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.springframework.core.MethodParameter; import org.springframework.http.HttpMethod; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.multipart.MultipartRequest; import org.springframework.web.servlet.support.RequestContextUtils; /** * Resolves request-related method argument values of the following types: * <ul> * <li>{@link WebRequest} * <li>{@link ServletRequest} * <li>{@link MultipartRequest} * <li>{@link HttpSession} * <li>{@link PushBuilder} (as of Spring 5.0 on Servlet 4.0) * <li>{@link Principal} * <li>{@link InputStream} * <li>{@link Reader} * <li>{@link HttpMethod} (as of Spring 4.0) * <li>{@link Locale} * <li>{@link TimeZone} (as of Spring 4.0) * <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8) * </ul> * * @author Arjen Poutsma * @author Rossen Stoyanchev * @author Juergen Hoeller * @since 3.1 */ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver { private static final Method newPushBuilderMethod = ClassUtils.getMethodIfAvailable(HttpServletRequest.class, "newPushBuilder"); @Override public boolean supportsParameter(MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); return (WebRequest.class.isAssignableFrom(paramType) || ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType) || HttpSession.class.isAssignableFrom(paramType) || (newPushBuilderMethod != null && newPushBuilderMethod.getReturnType().isAssignableFrom(paramType)) || Principal.class.isAssignableFrom(paramType) || InputStream.class.isAssignableFrom(paramType) || Reader.class.isAssignableFrom(paramType) || HttpMethod.class == paramType || Locale.class == paramType || TimeZone.class == paramType || ZoneId.class == paramType); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Class<?> paramType = parameter.getParameterType(); // WebRequest / NativeWebRequest / ServletWebRequest if (WebRequest.class.isAssignableFrom(paramType)) { if (!paramType.isInstance(webRequest)) { throw new IllegalStateException( "Current request is not of type [" + paramType.getName() + "]: " + webRequest); } return webRequest; } // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) { return resolveNativeRequest(webRequest, paramType); } // HttpServletRequest required for all further argument types return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class)); } private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) { T nativeRequest = webRequest.getNativeRequest(requiredType); if (nativeRequest == null) { throw new IllegalStateException( "Current request is not of type [" + requiredType.getName() + "]: " + webRequest); } return nativeRequest; } private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException { if (HttpSession.class.isAssignableFrom(paramType)) { HttpSession session = request.getSession(); if (session != null && !paramType.isInstance(session)) { throw new IllegalStateException( "Current session is not of type [" + paramType.getName() + "]: " + session); } return session; } else if (newPushBuilderMethod != null && newPushBuilderMethod.getReturnType().isAssignableFrom(paramType)) { Object pushBuilder = ReflectionUtils.invokeMethod(newPushBuilderMethod, request); if (pushBuilder != null && !paramType.isInstance(pushBuilder)) { throw new IllegalStateException( "Current push builder is not of type [" + paramType.getName() + "]: " + pushBuilder); } return pushBuilder; } else if (InputStream.class.isAssignableFrom(paramType)) { InputStream inputStream = request.getInputStream(); if (inputStream != null && !paramType.isInstance(inputStream)) { throw new IllegalStateException( "Request input stream is not of type [" + paramType.getName() + "]: " + inputStream); } return inputStream; } else if (Reader.class.isAssignableFrom(paramType)) { Reader reader = request.getReader(); if (reader != null && !paramType.isInstance(reader)) { throw new IllegalStateException( "Request body reader is not of type [" + paramType.getName() + "]: " + reader); } return reader; } else if (Principal.class.isAssignableFrom(paramType)) { Principal userPrincipal = request.getUserPrincipal(); if (userPrincipal != null && !paramType.isInstance(userPrincipal)) { throw new IllegalStateException( "Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal); } return userPrincipal; } else if (HttpMethod.class == paramType) { return HttpMethod.resolve(request.getMethod()); } else if (Locale.class == paramType) { return RequestContextUtils.getLocale(request); } else if (TimeZone.class == paramType) { TimeZone timeZone = RequestContextUtils.getTimeZone(request); return (timeZone != null ? timeZone : TimeZone.getDefault()); } else if (ZoneId.class == paramType) { TimeZone timeZone = RequestContextUtils.getTimeZone(request); return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault()); } // Should never happen... throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName()); } }