1. 程式人生 > >Spring MVC -- Accept 與 Content-Type

Spring MVC -- Accept 與 Content-Type

Rest 請求

請求方式 安全 冪等 介面說明
GET 安全 冪等 獲取資源
PSOT 不安全 非冪等 建立資源
PUT 不安全 冪等 更新資源
DELETE 不安全 冪等 刪除資源

冪等/非冪等 依賴於服務端實現,這種方式是一種契約

HTTP

Accept 與 Content-Type

Accept代表傳送端(客戶端)希望接受的資料型別。

比如:Accept:text/xml(application/json); 
代表客戶端希望接受的資料型別是xml(json )型別 
Content-Type代表傳送端(客戶端|伺服器)傳送的實體資料的資料型別。

比如:Content-Type:text/html(application/json) ; 
代表傳送端傳送的資料格式是html(json)。 
二者合起來,

Accept:text/xml; 
Content-Type:text/html

即代表希望接受的資料型別是xml格式,本次請求傳送的資料的資料格式是html。
Accept  application/xml,application/json 
表示的是希望接收資料格式的順序 最好先是application/xml 不行就走 application/json 

Spring MVC 的實現

package org.springframework.http.converter;


//這個介面為spring MVC 的轉換器
public interface  HttpMessageConverter<T>  {

    //判斷一個MediaType 是否可讀
    boolean canRead(Class<?> clazz, @Nullable
MediaType mediaType); //判斷一個MediaType 是否可寫 boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); //可以支援哪些 MediaType List<MediaType> getSupportedMediaTypes(); //讀取引數 @RequestBody 時使用的 反序列化出一個物件 T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException; //將一個物件寫入到流中 @ResponseBody 時使用 序列化一個物件 void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; } 這裡的 MediaType 就是http請求頭中的 Accept/Content-Type 屬性

Spring MVC 的基本配置資訊


//這個類記錄了一些Spring MVC 的基本配置資訊
package org.springframework.web.servlet.config.annotation;

public class WebMvcConfigurationSupport implements 
            ApplicationContextAware, ServletContextAware {
    
    //這些就是對應不同的 HTTP 請求頭中 Accept/Content-Type 屬性轉換 Spring MVC 世界中為 MediaType 
    
    /***
     *  預設的spring boot 2.0 是沒有 application/xml 的轉換器但是可以根據
     *  以下這些預設依賴資訊去新增依賴
     */
    
    private static boolean romePresent =
			ClassUtils.isPresent("com.rometools.rome.feed.WireFeed",
					WebMvcConfigurationSupport.class.getClassLoader());

	private static final boolean jaxb2Present =
			ClassUtils.isPresent("javax.xml.bind.Binder",
					WebMvcConfigurationSupport.class.getClassLoader());

	private static final boolean jackson2Present =
			ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
					WebMvcConfigurationSupport.class.getClassLoader()) &&
			ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
					WebMvcConfigurationSupport.class.getClassLoader());

	private static final boolean jackson2XmlPresent =
			ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper",
					WebMvcConfigurationSupport.class.getClassLoader());

	private static final boolean jackson2SmilePresent =
			ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory",
					WebMvcConfigurationSupport.class.getClassLoader());

	private static final boolean jackson2CborPresent =
			ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory",
					WebMvcConfigurationSupport.class.getClassLoader());

	private static final boolean gsonPresent =
			ClassUtils.isPresent("com.google.gson.Gson",
					WebMvcConfigurationSupport.class.getClassLoader());

	private static final boolean jsonbPresent =
			ClassUtils.isPresent("javax.json.bind.Jsonb",
					WebMvcConfigurationSupport.class.getClassLoader());

    ...
    
    // 下面的這個方法就是根據上面一段程式碼中解析器的依賴新增可以執行處理的MediaType
    //  查詢一下哪些 HTTP 中的 Accept/Content-Type  的是可以被處理的新增到Map當中
    
    protected Map<String, MediaType> getDefaultMediaTypes() {
		Map<String, MediaType> map = new HashMap<>(4);
		if (romePresent) {
			map.put("atom", MediaType.APPLICATION_ATOM_XML);
			map.put("rss", MediaType.APPLICATION_RSS_XML);
		}
		if (jaxb2Present || jackson2XmlPresent) {
			map.put("xml", MediaType.APPLICATION_XML);
		}
		if (jackson2Present || gsonPresent || jsonbPresent) {
			map.put("json", MediaType.APPLICATION_JSON);
		}
		if (jackson2SmilePresent) {
			map.put("smile", MediaType.valueOf("application/x-jackson-smile"));
		}
		if (jackson2CborPresent) {
			map.put("cbor", MediaType.valueOf("application/cbor"));
		}
		return map;
	}
    ...
    
    //為了證實上面的陳述,找到了以下的這個方法,看看MVC對請求的對映處理
    // @Bean 可以看出來這個也是單例的
    @Bean
	public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
		RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
		adapter.setContentNegotiationManager(mvcContentNegotiationManager());
		adapter.setMessageConverters(getMessageConverters());
		adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
		adapter.setCustomArgumentResolvers(getArgumentResolvers());
		adapter.setCustomReturnValueHandlers(getReturnValueHandlers());


        //這裡是重點觀察物件 
        //存在jackson2Present (Jackson2的json對映處理器)往往處理器中新增處理例項
		if (jackson2Present) {
			adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
			adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
		}

		AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
		configureAsyncSupport(configurer);
		if (configurer.getTaskExecutor() != null) {
			adapter.setTaskExecutor(configurer.getTaskExecutor());
		}
		if (configurer.getTimeout() != null) {
			adapter.setAsyncRequestTimeout(configurer.getTimeout());
		}
		adapter.setCallableInterceptors(configurer.getCallableInterceptors());
		adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

		return adapter;
	}
	...
	
    //新增預設的 HttpMessage 處理器集合
	protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
		StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
		stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

		messageConverters.add(new ByteArrayHttpMessageConverter());
		messageConverters.add(stringHttpMessageConverter);
		messageConverters.add(new ResourceHttpMessageConverter());
		messageConverters.add(new ResourceRegionHttpMessageConverter());
		messageConverters.add(new SourceHttpMessageConverter<>());
		messageConverters.add(new AllEncompassingFormHttpMessageConverter());

        //根據最開始的程式碼查詢是否含有相關依賴王處理集合中新增處理器

		if (romePresent) {
			messageConverters.add(new AtomFeedHttpMessageConverter());
			messageConverters.add(new RssChannelHttpMessageConverter());
		}

		if (jackson2XmlPresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
		}
		else if (jaxb2Present) {
			messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
		}

		if (jackson2Present) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
		}
		else if (gsonPresent) {
			messageConverters.add(new GsonHttpMessageConverter());
		}
		else if (jsonbPresent) {
			messageConverters.add(new JsonbHttpMessageConverter());
		}

		if (jackson2SmilePresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
		}
		if (jackson2CborPresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
		}
	}
	...
}

Spring MVC 匹配 MediaType

package org.springframework.web.accept;

public class ContentNegotiationManager implements 
        ContentNegotiationStrategy, MediaTypeFileExtensionResolver {

    ...
    
    /**
	 * Resolve the given request to a list of media types. The returned list is
	 * ordered by specificity first and by quality parameter second.
	 * @param webRequest the current request
	 * @return the requested media types, or {@link #MEDIA_TYPE_ALL_LIST} if none
	 * were requested.
	 * @throws HttpMediaTypeNotAcceptableException if the requested media
	 * types cannot be parsed
	 */
	 
    /**
    *將給定請求解析為媒體型別列表。 返回的列表是
    *首先按特異性排序,然後按質量引數排序。
    * @param webRequest當前請求
    * @return請求的媒體型別,或{@link #MEDIA_TYPE_ALL_LIST}(如果沒有)
    *被要求。
    * @throws HttpMediaTypeNotAcceptableException如果請求的媒體
    *型別無法解析
    */
    
    @Override
	public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
		for (ContentNegotiationStrategy strategy : this.strategies) {
			List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
			if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
				continue;
			}
			return mediaTypes;
		}
		return MEDIA_TYPE_ALL_LIST;
	}
	
	/**
	* 可以看出是經過for迴圈去匹配所有可以處理的 MediaType
	* 因此預設的順序為list中的順序即上面一個類中 addDefaultHttpMessageConverters 方法的加入順序
	*/
    ...
       
}

除此之外可以看看Spring MVC 對 Request 和 Response 的包裝

package org.springframework.http.server;

public class ServletServerHttpRequest implements ServerHttpRequest {
    
    protected static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";

	protected static final Charset FORM_CHARSET = StandardCharsets.UTF_8;


	private final HttpServletRequest servletRequest;
    
    //沒錯,spring MVC 包裝的 Request 其實在內部包裝了一個 HttpServletRequest

    ...
    
    
    /**
	 * Construct a new instance of the ServletServerHttpRequest based on the
	 * given {@link HttpServletRequest}.
	 * @param servletRequest the servlet request
	 */
	public ServletServerHttpRequest(HttpServletRequest servletRequest) {
		Assert.notNull(servletRequest, "HttpServletRequest must not be null");
		this.servletRequest = servletRequest;
	}
	//就連建構函式都要一個 HttpServletRequest 
	...
    
}

// 同理Response 應該也是如此

package org.springframework.http.server;

public class ServletServerHttpResponse implements ServerHttpResponse {

    private final HttpServletResponse servletResponse;

	private final HttpHeaders headers;

	private boolean headersWritten = false;

	private boolean bodyUsed = false;


	/**
	 * Construct a new instance of the ServletServerHttpResponse based on the given {@link HttpServletResponse}.
	 * @param servletResponse the servlet response
	 */
	public ServletServerHttpResponse(HttpServletResponse servletResponse) {
		Assert.notNull(servletResponse, "HttpServletResponse must not be null");
		this.servletResponse = servletResponse;
		this.headers = new ServletResponseHttpHeaders();
	}
	
	// 看出來了吧就連建構函式都需要一個 HttpServletResponse 可以認為基本是等價的
	
	...
}

// 綜上所述可以看出來 Spring 就是喜歡裝*  

Spring MVC 對於 @RequestBody 引數入參反序列化的處理

package org.springframework.web.servlet.mvc.method.annotation;

public abstract class AbstractMessageConverterMethodArgumentResolver 
        implements HandlerMethodArgumentResolver {

    ...
    
    @SuppressWarnings("unchecked")
	@Nullable
	protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

		MediaType contentType;
		boolean noContentType = false;
		try {
			contentType = inputMessage.getHeaders().getContentType();
		}
		catch (InvalidMediaTypeException ex) {
			throw new HttpMediaTypeNotSupportedException(ex.getMessage());
		}
		if (contentType == null) {
			noContentType = true;
			contentType = MediaType.APPLICATION_OCTET_STREAM;
		}

		Class<?> contextClass = parameter.getContainingClass();
		Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
		if (targetClass == null) {
			ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
			targetClass = (Class<T>) resolvableType.resolve();
		}

		HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
		Object body = NO_VALUE;

		EmptyBodyCheckingHttpInputMessage message;
		
		
		//重點觀察
		try {
			message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

            //進入迴圈,根據已經初始化的 HttpMessageConverter 集合
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
				GenericHttpMessageConverter<?> genericConverter =
						(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
				if (genericConverter != null ? 
				// 眼睛請看過來
				/**
				* 如果當前處理器(HttpMessageConverter)可以讀取(反序列化)
				* 那麼就進入了 處理器的處理流程 canRead -> read 
				*
				*/
				genericConverter.canRead(targetType, contextClass, contentType) :
						(targetClass != null && converter.canRead(targetClass, contentType))) {
					if (logger.isDebugEnabled()) {
						logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
					}
					/**
					* 存在 待反序列化的訊息 那麼就開始了Spring MVC 的表演
					*/
					if (message.hasBody()) {
						HttpInputMessage msgToUse =
								getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
						// 前置增強處理器處理
						body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
								((HttpMessageConverter<T>) converter).read
            
           

相關推薦

Spring MVC -- Accept Content-Type

Rest 請求 請求方式 安全 冪等 介面說明 GET 安全 冪等 獲取資源 PSOT 不安全 非冪等 建立資源 PUT 不安全 冪等 更新資源 DELETE 不安全 冪等 刪除資源 冪等/非冪等 依賴於服務端實現,這種方式是一種

Accept Content-Type

alt form 技術分享 協商 int val options ack printing 原文:Accept 與 Content-Type Accept Accept 表示請求方希望的資源類型,或者能

@RequestBodyContent-type

1、簡介 Spring Web MVC 是一種基於Java的實現了Web MVC設計模式的請求驅動型別的輕量級Web框架,自Spring MVC出現以來,Java服務端開發逐漸採用Spring MVC編寫Http介面。今天主要跟大家分享一個 @RequestBody 與

mime-typecontent-type

很長一段時間沒有搞清楚這兩個到底是什麼關係。 昨天網上搜索看到一個網友的留言豁然開朗,再次記錄一下,同事感謝那位仁兄。抱歉找不到原地址了,這裡就不貼了。 mimt-type說穿了其實指的就是檔案字尾名。 你向web伺服器請求一個檔案,伺服器會根據你的字尾名去匹配對應的值設

acceptcontent-Type區別

accept表示 客服端(瀏覽器)支援的型別,也是希望伺服器響應傳送回來的的資料型別。 例如:Accept:text/xml; ,也就是希望伺服器響應傳送回來的是xml文字格式的內容 區別: 1.Accept屬於請求頭, Content-Type屬於實體頭。  Http

javaweb和app的前後臺互動Content-Type理解得出的解決方案

  最近在做一個微信小程式,發現後臺獲取前臺傳送的資料,不能通過傳統的springmvc 直接對映獲取,通過參考案列小程式,發現獲取小程式引數是這樣的,程式碼如下。 StringBuilder sb = new StringBuilder();

Spring MVC 前後臺傳遞json格式數據 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

support style logs ica spring enc json格式數據 分享 技術 報錯如下: Content type ‘application/x-www-form-urlencoded;charset=UTF-8‘ not supported

詳解Http請求中Content-Type講解以及在Spring MVC中的應用

activit allow 視頻 標註 範圍 password length ted back 詳解Http請求中Content-Type講解以及在Spring MVC中的應用 引言: 在Http請求中,我們每天都在使用Content-type來指定不同格式的請求信息,但是

Http請求中Content-Type講解以及在Spring MVC中的應用

原文連結: https://blog.csdn.net/blueheart20/article/details/45174399 引言: 在Http請求中,我們每天都在使用Content-type來指定不同格式的請求資訊,但是卻很少有人去全面瞭解content-type中允許

解決Spring MVC Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

前言 今天在提交Ajax請求的時候出現下面異常 具體異常 org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;c

Spring MVC的@ResponseBody返回JSON串時Content-Type編碼問題

Xml程式碼 <bean class ="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" >      <property name="messa

Spring MVC Flash Attribute 的講解使用示例

取數據 這樣的 src artifact tag osc web.xml startup 刷新 Spring MVC 3.1版本加了一個很有用的特性,Flash屬性,它能解決一個長久以來缺少解決的問題,一個POST/Redirect/GET模式問題。 正常的MVC W

Maven和Spring mvc下的頁面的跳轉取值

servle 提交 輸入 接收 -m title style ofo pri (此處tomcat的端口設置為80) 例如:在testForm.jsp裏提交表單,在ok.jsp裏取值 testForm.jsp頁面代碼如下: <%@ page contentType="

Spring MVC的配置DispatcherServlet的分析

圖層 res 原理 success ota 靜態 source property dha   Spring MVC是一款Web MVC框架,是目前主流的Web MVC框架之一。   Spring MVC工作原理簡單來看如下圖所示:      接下來進行Spring MV

SpringMVC系列(十五)Spring MVCSpring整合時實例被創建兩次的解決方案以及Spring 的 IOC 容器和 SpringMVC 的 IOC 容器的關系

問題 nbsp frame ota 展示 not als pri exc 一、Spring MVC與Spring整合時實例被創建兩次的解決方案 1.問題產生的原因 Spring MVC的配置文件和Spring的配置文件裏面都使用了掃描註解<context:compon

Struts2如何實現MVCSpring MVC有什麽不同?

lte result map span 處理 view app pin resolve    Struts2采用filter充當前端控制器處理請求,filter會根據Struts.xml的配置,將請求分發給不同的業務控制器Action,再由Action處理具體的業務邏輯。A

spring bootspring mvc的區別是什麽?

安裝 基礎上 處理 tro str session 出了 描述 資源 Spring 框架就像一個家族,有眾多衍生產品例如 boot、security、jpa等等。但他們的基礎都是Spring 的 ioc和 aop ioc 提供了依賴註入的容器 aop ,解決了面向橫切面的編

spring mvc數據綁定表單標簽庫

odi 數據綁定 light col 表單標簽 頁面 ide Language true Book類為 package org.shaoxiu; public class Book { private String id; private String

Spring MVC入門(一)—— SpringMVC的執行流程常用註解

default 部分 它的 屬於 分享圖片 控制 mce AD http 一、什麽是SpringMVC SpringMVC就是類似於Struts2的mvc框架,屬於SpringFrameWork的後續產品。在模型層中與視圖層的交互部分。 springMVC執行流程:

Spring MVC溫故而知新 – 參數綁定、轉發重定向、異常處理、攔截器

單獨 UC exclude require 加載 pre buffered nts 節點 請求參數綁定 當用戶發送請求時,根據Spring MVC的請求處理流程,前端控制器會請求處理器映射器返回一個處理器,然後請求處理器適配器之心相應的處理器,此時處理器映射器會調用Spr