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

org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver Maven / Gradle / Ivy

There is a newer version: 6.1.6
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.servlet.mvc.method.annotation;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import jakarta.servlet.http.HttpServletRequest;

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.multipart.support.MultipartResolutionDelegate;
import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;

/**
 * Resolves the following method arguments:
 * 
    *
  • Annotated with @{@link RequestPart} *
  • Of type {@link MultipartFile} in conjunction with Spring's {@link MultipartResolver} abstraction *
  • Of type {@code jakarta.servlet.http.Part} in conjunction with Servlet multipart requests *
* *

When a parameter is annotated with {@code @RequestPart}, the content of the part is * passed through an {@link HttpMessageConverter} to resolve the method argument with the * 'Content-Type' of the request part in mind. This is analogous to what @{@link RequestBody} * does to resolve an argument based on the content of a regular request. * *

When a parameter is not annotated with {@code @RequestPart} or the name of * the part is not specified, the request part's name is derived from the name of * the method argument. * *

Automatic validation may be applied if the argument is annotated with any * {@linkplain org.springframework.validation.annotation.ValidationAnnotationUtils#determineValidationHints * annotations that trigger validation}. In case of validation failure, a * {@link MethodArgumentNotValidException} is raised and a 400 response status code returned if the * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver} * is configured. * * @author Rossen Stoyanchev * @author Brian Clozel * @author Juergen Hoeller * @since 3.1 */ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver { /** * Basic constructor with converters only. */ public RequestPartMethodArgumentResolver(List> messageConverters) { super(messageConverters); } /** * Constructor with converters and {@code RequestBodyAdvice} and * {@code ResponseBodyAdvice}. */ public RequestPartMethodArgumentResolver(List> messageConverters, List requestResponseBodyAdvice) { super(messageConverters, requestResponseBodyAdvice); } /** * Whether the given {@linkplain MethodParameter method parameter} is * supported as multipart. Supports the following method parameters: *
    *
  • annotated with {@code @RequestPart} *
  • of type {@link MultipartFile} unless annotated with {@code @RequestParam} *
  • of type {@code jakarta.servlet.http.Part} unless annotated with * {@code @RequestParam} *
*/ @Override public boolean supportsParameter(MethodParameter parameter) { if (parameter.hasParameterAnnotation(RequestPart.class)) { return true; } else { if (parameter.hasParameterAnnotation(RequestParam.class)) { return false; } return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional()); } } @Override @Nullable public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); Assert.state(servletRequest != null, "No HttpServletRequest"); RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class); boolean isRequired = ((requestPart == null || requestPart.required()) && !parameter.isOptional()); String name = getPartName(parameter, requestPart); parameter = parameter.nestedIfOptional(); Object arg = null; Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest); if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) { arg = mpArg; } else { try { HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name); arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType()); if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(request, arg, name); if (arg != null) { validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } } catch (MissingServletRequestPartException | MultipartException ex) { if (isRequired) { throw ex; } } } if (arg == null && isRequired) { if (!MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { throw new MultipartException("Current request is not a multipart request"); } else { throw new MissingServletRequestPartException(name); } } return adaptArgumentIfNecessary(arg, parameter); } private String getPartName(MethodParameter methodParam, @Nullable RequestPart requestPart) { String partName = (requestPart != null ? requestPart.name() : ""); if (partName.isEmpty()) { partName = methodParam.getParameterName(); if (partName == null) { throw new IllegalArgumentException("Request part name for argument type [" + methodParam.getNestedParameterType().getName() + "] not specified, and parameter name information not found in class file either."); } } return partName; } @Override void closeStreamIfNecessary(InputStream body) { // RequestPartServletServerHttpRequest exposes individual part streams, // potentially from temporary files -> explicit close call after resolution // in order to prevent file descriptor leaks. try { body.close(); } catch (IOException ex) { // ignore } } }