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 表示請求方希望的資源類型,或者能
@RequestBody與Content-type
1、簡介
Spring Web MVC 是一種基於Java的實現了Web MVC設計模式的請求驅動型別的輕量級Web框架,自Spring MVC出現以來,Java服務端開發逐漸採用Spring MVC編寫Http介面。今天主要跟大家分享一個 @RequestBody 與
mime-type與content-type
很長一段時間沒有搞清楚這兩個到底是什麼關係。
昨天網上搜索看到一個網友的留言豁然開朗,再次記錄一下,同事感謝那位仁兄。抱歉找不到原地址了,這裡就不貼了。
mimt-type說穿了其實指的就是檔案字尾名。
你向web伺服器請求一個檔案,伺服器會根據你的字尾名去匹配對應的值設
accept 和 content-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 MVC與Spring整合時實例被創建兩次的解決方案以及Spring 的 IOC 容器和 SpringMVC 的 IOC 容器的關系
問題 nbsp frame ota 展示 not als pri exc 一、Spring MVC與Spring整合時實例被創建兩次的解決方案
1.問題產生的原因
Spring MVC的配置文件和Spring的配置文件裏面都使用了掃描註解<context:compon
Struts2如何實現MVC,與Spring MVC有什麽不同?
lte result map span 處理 view app pin resolve Struts2采用filter充當前端控制器處理請求,filter會根據Struts.xml的配置,將請求分發給不同的業務控制器Action,再由Action處理具體的業務邏輯。A
spring boot與spring 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