• spring mvc 下,ajax调用后台controller方法时报415 (Unsupported Media Type)错误


    错误: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

  • 相关阅读:
    【python】@property装饰器
    使用pycharm专业版创建虚拟环境
    scrapy爬虫框架入门实战
    LoadRunner10个用户并发测试时分别取不同的参数运行脚本
    selenium grid原理
    使用 PHP 过滤器(Filter)进行严格表单验证
    ios之gcd
    ios之runloop笔记
    ios之block笔记
    iOS APP网络分析之rvictl(可以捕捉除了Wifi以外的网络类型)
  • 原文地址:https://www.cnblogs.com/xiaozhuyuan/p/7473176.html
Copyright © 2020-2023  润新知