目录
一、springmvc响应json
1. web场景自动引入了json场景
2.使用方式
二、springmvc响应json数据原理
1.springmvc请求处理逻辑
2.返回值的处理
3.返回值处理器
4.SpringMVC到底支持哪些返回值
5.处理@ResponseBody 注解的RequestResponseBodyMethodProcessor
6.内容协商
7.HttpMessageConverter
8.MappingJackson2HttpMessageConverter
9.总结
三、初识内容协商
1.什么是内容协商
2.springboot响应xml报文
四、内容协商的原理
1.关键代码AbstractMessageConverterMethodProcessor
2.各种服务器端支持的Converts
3.获取客户端(PostMan、浏览器)支持接收的内容类型
4.遍历服务器所有MessageConverter,看谁支持 *** 作这个对象(Person)
5.服务端支持的10种处理数据类型(MediaType)
6.权重优先匹配原则
7.开启基于请求参数的内容协商功能
五、自定义MessageConverter
1.功能
2.实现
3.测试
4.使用参数方式
一、springmvc响应json 1. web场景自动引入了json场景
org.springframework.boot spring-boot-starter-web
引入web场景会自动引入json,json默认是使用jackson解析的。
2.使用方式org.springframework.boot spring-boot-starter-json2.3.4.RELEASE compile
(1)@ResponseBody
在方法上加@ResponseBody
(2)@RestController
实际上@RestController=@Controller+@ResponseBody
将实体类转成json格式,给前端自动返回json数据。
二、springmvc响应json数据原理 1.springmvc请求处理逻辑springmvc处理逻辑参考博文:
springBoot-springMVC请求处理原理_A_art_xiang的博客-CSDN博客
2.返回值的处理@Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) { // 设置请求参数解析器 invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { // 设置响应值处理器 invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelonRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(result, !traceOn); return "Resume with async result [" + formatted + "]"; }); invocableMethod = invocableMethod.wrapConcurrentResult(result); } invocableMethod.invokeAndHandle(webRequest, mavContainer); // 执行目标方法 if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
// org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { // 处理返回值,利用所有的返回值处理器来处理返回值。 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
// org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue @Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 找能处理返回值的返回值处理器 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);// 返回值处理 }
// org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#selectHandler @Nullable private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { // 循环遍历所有的返回值处理器 if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { // 如果返回值类型找到对应的返回值处理器,就返回该处理器 return handler; } } return null; }3.返回值处理器
有许多返回值处理器
返回值处理器也是一个接口,HandlerMethodReturnValueHandler。
supportsReturnType来判断是否能处理该返回值类型。
handleReturnValue来真正处理返回值。
(1)、返回值处理器判断是否支持这种类型返回值 supportsReturnType
(2)、返回值处理器调用 handleReturnValue 进行处理
(3)、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
4.SpringMVC到底支持哪些返回值ModelAndView Model View // 视图 ResponseEntity ResponseBodyEmitter StreamingResponseBody // 流式数据 HttpEntity // 不能为RequestEntity HttpHeaders Callable// 支持异步 DeferredResult // 支持异步 ListenableFuture// 支持异步 CompletionStage WebAsyncTask 方法有 @ModelAttribute 且为对象类型的 方法有@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;(自定义实体类响应json数据,使用此返回值处理器)5.处理@ResponseBody 注解的RequestResponseBodyMethodProcessor
// 处理返回值 // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue @Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // Try even with null return value. ResponseBodyAdvice could get involved. // 利用 MessageConverters 进行处理 将数据写为json writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }
protected6.内容协商void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object body; Class> valueType; Type targetType; // 判断返回值是否是字符串类型 if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else { body = value; valueType = getReturnValueType(body, returnType); targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); } // 判断返回值是否是资源类型(stream流数据) if (isResourceType(value, returnType)) { outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null && outputMessage.getServletResponse().getStatus() == 200) { Resource resource = (Resource) value; try { List httpRanges = inputMessage.getHeaders().getRange(); outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value()); body = HttpRange.toResourceRegions(httpRanges, resource); valueType = body.getClass(); targetType = RESOURCE_REGION_LIST_TYPE; } catch (IllegalArgumentException ex) { outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength()); outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value()); } } } // 媒体类型(牵扯到内容协商)服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据 MediaType selectedMediaType = null; MediaType contentType = outputMessage.getHeaders().getContentType(); boolean isContentTypePreset = contentType != null && contentType.isConcrete(); if (isContentTypePreset) { if (logger.isDebugEnabled()) { logger.debug("Found 'Content-Type:" + contentType + "' in response"); } selectedMediaType = contentType; } else { HttpServletRequest request = inputMessage.getServletRequest(); List acceptableTypes = getAcceptableMediaTypes(request); // 获取浏览器能接收的内容类型 List producibleTypes = getProducibleMediaTypes(request, valueType, targetType); // 服务器能生产的内容类型 if (body != null && producibleTypes.isEmpty()) { throw new HttpMessageNotWritableException( "No converter found for return value of type: " + valueType); } List mediaTypesToUse = new ArrayList<>(); for (MediaType requestedType : acceptableTypes) { //浏览器与服务器匹配,看能互相匹配的内容类型 for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } } if (mediaTypesToUse.isEmpty()) { if (body != null) { throw new HttpMediaTypeNotAcceptableException(producibleTypes); } if (logger.isDebugEnabled()) { logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes); } return; } MediaType.sortBySpecificityAndQuality(mediaTypesToUse); for (MediaType mediaType : mediaTypesToUse) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (logger.isDebugEnabled()) { logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes); } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); // // 循环所有的消息转换器,SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理 for (HttpMessageConverter> converter : this.messageConverters) { // 得到MappingJackson2HttpMessageConverter可以将对象写为json // 利用MappingJackson2HttpMessageConverter将对象转为json再写出去。最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的) GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter>) converter : null); if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class extends HttpMessageConverter>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]"); addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } else { if (logger.isDebugEnabled()) { logger.debug("Nothing to write: null body"); } } return; } } } if (body != null) { Set producibleMediaTypes = (Set ) inputMessage.getServletRequest() .getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) { throw new HttpMessageNotWritableException( "No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'"); } throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } }
内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型,请求头Accept)
7.HttpMessageConverter
(1)HttpMessageConverter实际是一个接口
HttpMessageConverter左右: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
例子:Person对象转为JSON。或者 JSON转为Person
canRead:是否能读(媒体类型参数)
canWrite:是否能写(媒体类型参数)
getSypportedMediaTypes:判断是否支持该类型的返回值
(2)默认的MessageConverter
0 - 只支持Byte类型的返回值
1 - 只支持String类型的返回值
2 - 只支持String类型的返回值
3 - 只支持Resource类型的返回值
4 - 只支持ResourceRegion类型的返回值
5 - 支持DOMSource.class SAXSource.class StAXSource.class StreamSource.class Source.class类型的返回值
6 - 只支持MultiValueMap类型的返回值
7 - 直接返回true
8 - 直接返回true
9 - 支持注解方式xml处理的
8.MappingJackson2HttpMessageConverter最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
// org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal @Override protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { MediaType contentType = outputMessage.getHeaders().getContentType(); JsonEncoding encoding = getJsonEncoding(contentType); JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding); try { writePrefix(generator, object); Object value = object; Class> serializationView = null; FilterProvider filters = null; JavaType javaType = null; if (object instanceof MappingJacksonValue) { MappingJacksonValue container = (MappingJacksonValue) object; value = container.getValue(); serializationView = container.getSerializationView(); filters = container.getFilters(); } if (type != null && TypeUtils.isAssignable(type, value.getClass())) { javaType = getJavaType(type, null); } ObjectWriter objectWriter = (serializationView != null ? this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer()); if (filters != null) { objectWriter = objectWriter.with(filters); } if (javaType != null && javaType.isContainerType()) { objectWriter = objectWriter.forType(javaType); } SerializationConfig config = objectWriter.getConfig(); if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) && config.isEnabled(SerializationFeature.INDENT_OUTPUT)) { objectWriter = objectWriter.with(this.ssePrettyPrinter); } objectWriter.writevalue(generator, value); writeSuffix(generator, object); generator.flush(); } catch (InvalidDefinitionException ex) { throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); } catch (JsonProcessingException ex) { throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex); } }
// org.springframework.http.converter.AbstractGenericHttpMessageConverter#write @Override public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final HttpHeaders headers = outputMessage.getHeaders(); addDefaultHeaders(headers, t, contentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() { @Override public OutputStream getBody() { return outputStream; } @Override public HttpHeaders getHeaders() { return headers; } })); } else { writeInternal(t, type, outputMessage); // 最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的) outputMessage.getBody().flush(); } }
9.总结
标注@ResponseBody -> 使用RequestResponseBodyMethodProcessor处理 -> 查找对应的messageConverter处理返回值。
messageConverter可以处理多种返回值(不止局限于自定义的实体类,但是一定要加上@ResponseBody注解)
三、初识内容协商 1.什么是内容协商内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型,请求头Accept)。
根据客户端接收能力不同,返回不同媒体类型的数据。
2.springboot响应xml报文
(1)引入支持xml依赖
com.fasterxml.jackson.dataformat jackson-dataformat-xml
(2)服务器会按照请求头来判断返回的内容格式
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
如果写application/json会返回json报文,如果写application/xml就会返回xml报文。
四、内容协商的原理
同上,标注@ResponseBody -> 使用RequestResponseBodyMethodProcessor处理 -> 查找对应的messageConverter处理返回值。
1.关键代码AbstractMessageConverterMethodProcessor// AbstractMessageConverterMethodProcessor的writeWithMessageConverters protected2.各种服务器端支持的Convertsvoid writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object body; Class> valueType; Type targetType; if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else { body = value; valueType = getReturnValueType(body, returnType); targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); } if (isResourceType(value, returnType)) { outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null && outputMessage.getServletResponse().getStatus() == 200) { Resource resource = (Resource) value; try { List httpRanges = inputMessage.getHeaders().getRange(); outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value()); body = HttpRange.toResourceRegions(httpRanges, resource); valueType = body.getClass(); targetType = RESOURCE_REGION_LIST_TYPE; } catch (IllegalArgumentException ex) { outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength()); outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value()); } } } MediaType selectedMediaType = null; // 判断当前响应头中是否已经有确定的媒体类型。MediaType,只要没加拦截器处理一般情况下都没有 MediaType contentType = outputMessage.getHeaders().getContentType(); boolean isContentTypePreset = contentType != null && contentType.isConcrete(); if (isContentTypePreset) { if (logger.isDebugEnabled()) { logger.debug("Found 'Content-Type:" + contentType + "' in response"); } selectedMediaType = contentType; } else { HttpServletRequest request = inputMessage.getServletRequest(); // 获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】 // 底层其实就是使用原生request,调用getHeaderValues("Accept") List acceptableTypes = getAcceptableMediaTypes(request); // 遍历循环所有当前系统的 MessageConverter,看谁支持 *** 作这个对象(Person) // 找到支持 *** 作Person的converter,把converter支持的媒体类型统计出来。(详见上面) // 导入了jackson处理xml的包,xml的converter就会自动进来 List producibleTypes = getProducibleMediaTypes(request, valueType, targetType); // 最终发现,客户端需要【application/xml】。服务端可以提供10种服务【json、xml】 if (body != null && producibleTypes.isEmpty()) { throw new HttpMessageNotWritableException( "No converter found for return value of type: " + valueType); } List mediaTypesToUse = new ArrayList<>(); // 内容协商,双重for循环,找出最佳匹配媒体类型。最终将匹配的媒体类型放到mediaTypesToUse for (MediaType requestedType : acceptableTypes) { for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } } // 若无匹配,会抛异常 if (mediaTypesToUse.isEmpty()) { if (body != null) { throw new HttpMediaTypeNotAcceptableException(producibleTypes); } if (logger.isDebugEnabled()) { logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes); } return; } // 排序 MediaType.sortBySpecificityAndQuality(mediaTypesToUse); // 选中的媒体类型只会有一个,有匹配的就break。 for (MediaType mediaType : mediaTypesToUse) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (logger.isDebugEnabled()) { logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes); } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); // 找Converter,看谁能将对象转成需要的媒体类型(xml) // 用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。 for (HttpMessageConverter> converter : this.messageConverters) { GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter>) converter : null); if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class extends HttpMessageConverter>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]"); addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } else { if (logger.isDebugEnabled()) { logger.debug("Nothing to write: null body"); } } return; } } } if (body != null) { Set producibleMediaTypes = (Set ) inputMessage.getServletRequest() .getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) { throw new HttpMessageNotWritableException( "No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'"); } throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } }
后四个分别支持对象转json、对象转xml。
其中对象转xml的因为导入了jackson的xml支持的包,会自动出现。
3.获取客户端(PostMan、浏览器)支持接收的内容类型
底层其实就是使用原生request,调用getHeaderValues("Accept")
// org.springframework.web.accept.HeaderContentNegotiationStrategy#resolveMediaTypes @Override public List4.遍历服务器所有MessageConverter,看谁支持 *** 作这个对象(Person)resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException { String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT); if (headerValueArray == null) { return MEDIA_TYPE_ALL_LIST; } List headerValues = Arrays.asList(headerValueArray); try { List mediaTypes = MediaType.parseMediaTypes(headerValues); MediaType.sortBySpecificityAndQuality(mediaTypes); return !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST; } catch (InvalidMediaTypeException ex) { throw new HttpMediaTypeNotAcceptableException( "Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage()); } }
// org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#getProducibleMediaTypes(javax.servlet.http.HttpServletRequest, java.lang.Class>, java.lang.reflect.Type) protected List5.服务端支持的10种处理数据类型(MediaType)getProducibleMediaTypes( HttpServletRequest request, Class> valueClass, @Nullable Type targetType) { Set mediaTypes = (Set ) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (!CollectionUtils.isEmpty(mediaTypes)) { return new ArrayList<>(mediaTypes); } else if (!this.allSupportedMediaTypes.isEmpty()) { List result = new ArrayList<>(); for (HttpMessageConverter> converter : this.messageConverters) { if (converter instanceof GenericHttpMessageConverter && targetType != null) { if (((GenericHttpMessageConverter>) converter).canWrite(targetType, valueClass, null)) { result.addAll(converter.getSupportedMediaTypes()); } } else if (converter.canWrite(valueClass, null)) { result.addAll(converter.getSupportedMediaTypes()); } } return result; } else { return Collections.singletonList(MediaType.ALL); } }
6.权重优先匹配原则
浏览器q=0.9,权重为0.9,没有写支持json,而*
public class TestMessageConverter implements HttpMessageConverter (2)在配置类中注册刚写的Converter. (1)加配置 (2)配置类加配置 (3)测试一下吧 欢迎分享,转载请注明来源:内存溢出//WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
// 扩展MessageConverters
@Override
public void extendMessageConverters(List
3.测试
spring:
mvc:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration(proxyBeanMethods = false)
public class WebConfig{
//WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
//Map
评论列表(0条)