spring HttpEntityMethodProcessor 源码
spring HttpEntityMethodProcessor 代码
文件路径:/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java
/*
* 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.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ProblemDetail;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.ErrorResponse;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
* Resolves {@link HttpEntity} and {@link RequestEntity} method argument values,
* as well as return values of type {@link HttpEntity}, {@link ResponseEntity},
* {@link ErrorResponse} and {@link ProblemDetail}.
*
* <p>An {@link HttpEntity} return type has a specific purpose. Therefore, this
* handler should be configured ahead of handlers that support any return
* value type annotated with {@code @ModelAttribute} or {@code @ResponseBody}
* to ensure they don't take over.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Brian Clozel
* @author Sam Brannen
* @since 3.1
*/
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
/**
* Basic constructor with converters only. Suitable for resolving
* {@code HttpEntity}. For handling {@code ResponseEntity} consider also
* providing a {@code ContentNegotiationManager}.
*/
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters) {
super(converters);
}
/**
* Basic constructor with converters and {@code ContentNegotiationManager}.
* Suitable for resolving {@code HttpEntity} and handling {@code ResponseEntity}
* without {@code Request~} or {@code ResponseBodyAdvice}.
*/
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters, ContentNegotiationManager manager) {
super(converters, manager);
}
/**
* Complete constructor for resolving {@code HttpEntity} method arguments.
* For handling {@code ResponseEntity} consider also providing a
* {@code ContentNegotiationManager}.
* @since 4.2
*/
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
List<Object> requestResponseBodyAdvice) {
super(converters, null, requestResponseBodyAdvice);
}
/**
* Complete constructor for resolving {@code HttpEntity} and handling
* {@code ResponseEntity}.
*/
public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
@Nullable ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
super(converters, manager, requestResponseBodyAdvice);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (HttpEntity.class == parameter.getParameterType() ||
RequestEntity.class == parameter.getParameterType());
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> type = returnType.getParameterType();
return ((HttpEntity.class.isAssignableFrom(type) && !RequestEntity.class.isAssignableFrom(type)) ||
ErrorResponse.class.isAssignableFrom(type) || ProblemDetail.class.isAssignableFrom(type));
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
Type paramType = getHttpEntityType(parameter);
if (paramType == null) {
throw new IllegalArgumentException("HttpEntity parameter '" + parameter.getParameterName() +
"' in method " + parameter.getMethod() + " is not parameterized");
}
Object body = readWithMessageConverters(webRequest, parameter, paramType);
if (RequestEntity.class == parameter.getParameterType()) {
return new RequestEntity<>(body, inputMessage.getHeaders(),
inputMessage.getMethod(), inputMessage.getURI());
}
else {
return new HttpEntity<>(body, inputMessage.getHeaders());
}
}
@Nullable
private Type getHttpEntityType(MethodParameter parameter) {
Assert.isAssignable(HttpEntity.class, parameter.getParameterType());
Type parameterType = parameter.getGenericParameterType();
if (parameterType instanceof ParameterizedType type) {
if (type.getActualTypeArguments().length != 1) {
throw new IllegalArgumentException("Expected single generic parameter on '" +
parameter.getParameterName() + "' in method " + parameter.getMethod());
}
return type.getActualTypeArguments()[0];
}
else if (parameterType instanceof Class) {
return Object.class;
}
else {
return null;
}
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
mavContainer.setRequestHandled(true);
if (returnValue == null) {
return;
}
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
HttpEntity<?> httpEntity;
if (returnValue instanceof ErrorResponse response) {
httpEntity = new ResponseEntity<>(response.getBody(), response.getHeaders(), response.getStatusCode());
}
else if (returnValue instanceof ProblemDetail detail) {
httpEntity = ResponseEntity.of(detail).build();
}
else {
Assert.isInstanceOf(HttpEntity.class, returnValue);
httpEntity = (HttpEntity<?>) returnValue;
}
if (httpEntity.getBody() instanceof ProblemDetail detail) {
if (detail.getInstance() == null) {
URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
detail.setInstance(path);
}
}
HttpHeaders outputHeaders = outputMessage.getHeaders();
HttpHeaders entityHeaders = httpEntity.getHeaders();
if (!entityHeaders.isEmpty()) {
entityHeaders.forEach((key, value) -> {
if (HttpHeaders.VARY.equals(key) && outputHeaders.containsKey(HttpHeaders.VARY)) {
List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders);
if (!values.isEmpty()) {
outputHeaders.setVary(values);
}
}
else {
outputHeaders.put(key, value);
}
});
}
if (httpEntity instanceof ResponseEntity<?> responseEntity) {
int returnStatus = responseEntity.getStatusCode().value();
outputMessage.getServletResponse().setStatus(returnStatus);
if (returnStatus == 200) {
HttpMethod method = inputMessage.getMethod();
if ((HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method))
&& isResourceNotModified(inputMessage, outputMessage)) {
outputMessage.flush();
return;
}
}
else if (returnStatus / 100 == 3) {
String location = outputHeaders.getFirst("location");
if (location != null) {
saveFlashAttributes(mavContainer, webRequest, location);
}
}
}
// Try even with null body. ResponseBodyAdvice could get involved.
writeWithMessageConverters(httpEntity.getBody(), returnType, inputMessage, outputMessage);
// Ensure headers are flushed even if no body was written.
outputMessage.flush();
}
private List<String> getVaryRequestHeadersToAdd(HttpHeaders responseHeaders, HttpHeaders entityHeaders) {
List<String> entityHeadersVary = entityHeaders.getVary();
List<String> vary = responseHeaders.get(HttpHeaders.VARY);
if (vary != null) {
List<String> result = new ArrayList<>(entityHeadersVary);
for (String header : vary) {
for (String existing : StringUtils.tokenizeToStringArray(header, ",")) {
if ("*".equals(existing)) {
return Collections.emptyList();
}
for (String value : entityHeadersVary) {
if (value.equalsIgnoreCase(existing)) {
result.remove(value);
}
}
}
}
return result;
}
return entityHeadersVary;
}
private boolean isResourceNotModified(ServletServerHttpRequest request, ServletServerHttpResponse response) {
ServletWebRequest servletWebRequest =
new ServletWebRequest(request.getServletRequest(), response.getServletResponse());
HttpHeaders responseHeaders = response.getHeaders();
String etag = responseHeaders.getETag();
long lastModifiedTimestamp = responseHeaders.getLastModified();
if (request.getMethod() == HttpMethod.GET || request.getMethod() == HttpMethod.HEAD) {
responseHeaders.remove(HttpHeaders.ETAG);
responseHeaders.remove(HttpHeaders.LAST_MODIFIED);
}
return servletWebRequest.checkNotModified(etag, lastModifiedTimestamp);
}
private void saveFlashAttributes(ModelAndViewContainer mav, NativeWebRequest request, String location) {
mav.setRedirectModelScenario(true);
ModelMap model = mav.getModel();
if (model instanceof RedirectAttributes redirectAttributes) {
Map<String, ?> flashAttributes = redirectAttributes.getFlashAttributes();
if (!CollectionUtils.isEmpty(flashAttributes)) {
HttpServletRequest req = request.getNativeRequest(HttpServletRequest.class);
HttpServletResponse res = request.getNativeResponse(HttpServletResponse.class);
if (req != null) {
RequestContextUtils.getOutputFlashMap(req).putAll(flashAttributes);
if (res != null) {
RequestContextUtils.saveOutputFlashMap(location, req, res);
}
}
}
}
}
@Override
protected Class<?> getReturnValueType(@Nullable Object returnValue, MethodParameter returnType) {
if (returnValue != null) {
return returnValue.getClass();
}
else {
Type type = getHttpEntityType(returnType);
type = (type != null ? type : Object.class);
return ResolvableType.forMethodParameter(returnType, type).toClass();
}
}
}
相关信息
相关文章
spring AbstractMappingJacksonResponseBodyAdvice 源码
spring AbstractMessageConverterMethodArgumentResolver 源码
spring AbstractMessageConverterMethodProcessor 源码
spring AsyncTaskMethodReturnValueHandler 源码
spring CallableMethodReturnValueHandler 源码
spring ContinuationHandlerMethodArgumentResolver 源码
spring DeferredResultMethodReturnValueHandler 源码
spring ExceptionHandlerExceptionResolver 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦