錯誤:ajax的post方法調用后台controller方法時報錯:415 (Unsupported Media Type)。下面是錯誤時的代碼
前端:
var url ="xxx/xxQuery.action";
var params={
year:'2017'
}
ajax(url,params,function(result){
console.log(result);
})
function ajax(url,params,callback,contentType){
var ajaxoptions={
url:url,
data:params,
type:"POST",
dataType:"json",
// contentType:",//application/x-www-form-urlencoded application/json"
success:function(res){
callback(res);
},
error:function(){
callback({success:false,description:"失敗"}); }
}
if(contentType!=undefined){
ajaxoptions=$.extend(ajaxoptions,{contentType:contentType})
}
$.ajax(ajaxoptions);
}
后台:
@RequestMapping(value = { "/xxx/xxQuery.action" })
@ResponseBody
public Map<String, Object> xxQuery(Model model, HttpServletRequest request,
@RequestBody ORParameter parameter) {
........
return result;
}
我的思路:對后台spring mvc不熟,只是有個概念服務器應該接收到了http請求,只是因為某種原因沒有進入controller,而提前被攔截處理了。然后就開始找http處理的源頭,笨辦法,從HttpServlet.service開始找 -> DispatcherServlet.doDispatch,找到關鍵的處理方法handle

拋出錯誤的位置
package org.springframework.web.servlet.mvc.method.annotation. RequestResponseBodyMethodProcessor;
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest);
RequestBody ann = methodParam.getParameterAnnotation(RequestBody.class);
if (!ann.required()) { //由於controller調用的這個方法參數加了@RequestBody,ann.required()這個為true就不走這里,
InputStream inputStream = inputMessage.getBody();
if (inputStream == null) {
return null;
}
else if (inputStream.markSupported()) {
inputStream.mark(1);
if (inputStream.read() == -1) {
return null;
}
inputStream.reset();
}
else {
final PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
int b = pushbackInputStream.read();
if (b == -1) {
return null;
}
else {
pushbackInputStream.unread(b);
}
inputMessage = new ServletServerHttpRequest(servletRequest) {
@Override
public InputStream getBody() {
// Form POST should not get here
return pushbackInputStream;
}
};
}
}
return super.readWithMessageConverters(inputMessage, methodParam, paramType);
}
package org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver(類);
@SuppressWarnings("unchecked")
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
MediaType contentType;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = methodParam.getDeclaringClass();
Map<TypeVariable, Type> map = GenericTypeResolver.getTypeVariableMap(contextClass);
Class<T> targetClass = (Class<T>) GenericTypeResolver.resolveType(targetType, map);
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetType + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return genericConverter.read(targetType, contextClass, inputMessage);
}
}
if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
}
}
}
//這里是報錯位置
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);//沒有找到參數對應的轉換器就報錯
}
原因:@RequestBody
a) 該注解用於讀取Request請求的body部分數據,根據request的header部分的Content-Type類型,匹配HttpMessageConverter進行解析,然后把相應的數據綁定到要返回的對象上;
b) 再把HttpMessageConverter返回的對象數據綁定到 controller中方法的參數上。
前台沒有注明后台解析參考的content-type,默認為application/x-www-form-urlencoded ,同時參數是對象也是不是json字符串,后台找不到解析方法。
解決:content-type為application/json格式的數據可以被jackson解讀(這里在canread判斷時用到),json對應jackson處理請求中的參數,jackson的參數應該是json字符串(enum JsonToken 這里有關於解析的字符串格式token的枚舉內容: START_OBJECT("{").....), 而不是application/x-www-form-urlencoded編碼的字符串“year=2017“。
所以前端請求需改為:
ajax(url,JSON.stringify(params),function(result){ console.log(result); },"application/json")

正確解析時,調用的轉換器是jackson。
備注:調用到參數解析的調用棧

可參考:https://blog.csdn.net/mingtianhaiyouwo/article/details/51445345
1.瀏覽器請求時沒有設置Content-Type為Json,對於瀏覽器來說就是要把POST的內容放到jsonData屬性中,而不是params。
2.第三方工具沒有設置Content-Type的功能,默認的是Content-Type: application/x-www-form-urlencoded
