发现一个特别有意思的问题, JS发起ajax请求时,参数设置的位置不同,在服务器有可能getParamter时取不到。

源码一:

<html>
  <head>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
      $(document).ready(function(){
        $(document).ajaxSend(function(e, req, settings) {
            settings.data = "hello=world"
            console.log("ajaxSend")
          });
        $("button").click(function(){
          $.ajax({
            method:"post",
               url: "index.jsp"
           })
        });
      });
    </script>
  </head>
  <body>
    <button>改变内容</button>
  </body>
</html>

点击页面按钮【改变内容】,打开chrome开发者工具发现headers中如下, content-type=text/plain,并且request方式为payload。 这时服务器端getParamter得到值为空,只能够通过读取request的输入流来获取。

Response Headers
Content-Type:text/html;charset=UTF-8
Date:Wed, 20 Apr 2016 14:36:15 GMT
Server:Apache-Coyote/1.1
Transfer-Encoding:chunked
Request Headers
view source

Request Headers
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
Connection:keep-alive
Content-Length:11
Content-Type:text/plain;charset=UTF-8
Cookie:JSESSIONID=1CC8EEE67B6FD157AD5621075DA4DBE5
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/test.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
X-Requested-With:XMLHttpRequest

Request Payload
hello=world

源码二:

<html>
  <head>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
      $(document).ready(function(){
        $(document).ajaxSend(function(e, req, settings) {
            console.log("ajaxSend")
          });
        $("button").click(function(){
          $.ajax({
            method:"post",
            data:"hello=world",
               url: "index.jsp"
           })
        });
      });
    </script>
  </head>
  <body>
    <button>改变内容</button>
  </body>
</html>

点击页面按钮【改变内容】,打开chrome开发者工具发现headers中如下, content-type=application/x-www-form-urlencoded,并且request方式为Form Data。 这时服务器端getParamter得到值为world,当然也可以通过读取request的输入流。

Response Headers
view source
Content-Type:text/html;charset=UTF-8
Date:Wed, 20 Apr 2016 14:50:37 GMT
Server:Apache-Coyote/1.1
Transfer-Encoding:chunked

Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
Connection:keep-alive
Content-Length:11
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Cookie:JSESSIONID=1CC8EEE67B6FD157AD5621075DA4DBE5
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/test.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
X-Requested-With:XMLHttpRequest

Form Data
hello:world

答疑

(Content-Type不是application/x-www-form-urlencoded)数据格式不固定,不一定是名值对的方式,所以服务器无法知道具体的处理方式,所以只能通过获取原始数据流的方式来进行解析。

那jQuery通过xhr发送请求时是如何设置content-type的呢?我们以jQuery1.12.3为例解释:

片段一:jQuery.ajax line 9688~9750

// 发起请求时如果有设定data并且没有设定contentType,则设定请求contentType=ajaxSettings的默认设置 "application/x-www-form-urlencoded; charset=UTF-8"。 设定到ajax方法中的局部变量responseHeaders当中。

.......
// 调用用户的beforeSend处理事件
if ( s.beforeSend &&
  ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
  // Abort if not done already and return
  return jqXHR.abort();
}

.......
  // 调用全局处理事情ajaxSend事件处理函数
  if ( fireGlobals ) {
    globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
  }
.......
    // 调用jQuery.ajaxTransport.options.send方法
    transport.send( requestHeaders, done );
......

片段二: jQuery.ajaxTransport.options.send line 10201~

send: function( headers, complete ) {
..........
    // 设定Content-Type如果上面调用代码传过来的话
		for ( i in headers ) {
			if ( headers[ i ] !== undefined ) {
				xhr.setRequestHeader( i, headers[ i ] + "" );
			}
		}
		// 向服务器发起请求
		xhr.send( ( options.hasContent && options.data ) || null );
.......

参考: AJAX POST请求中参数以form data和request payload形式在servlet中的获取方式

Comments