Jquery - 如何使$.post()使用contentType=application/json?

354
我注意到在使用jquery的$.post()时,默认的contentType是application/x-www-form-urlencoded,但我的asp.net mvc代码需要有contentType=application/json(参见此问题以了解为什么必须使用application/json:ASPNET MVC - Why is ModelState.IsValid false "The x field is required" when that field does have a value?)。我该如何使$.post()发送contentType=application/json?我已经有大量的$.post()函数,所以不想改用$.ajax(),因为这需要太多时间。如果我尝试
$.post(url, data, function(), "json") 

我仍然拥有contentType=application/x-www-form-urlencoded。那么,如果“json”参数不能将contenttype更改为json,它到底是做什么的?

如果我尝试

$.ajaxSetup({
  contentType: "application/json; charset=utf-8"
});

那样做可以起作用,但会影响我所有的$.get和$.post请求,并导致一些请求出错。那么有没有办法改变$.post()的行为,使其发送contentType=application/json?
17个回答

428
$.ajax({
  url:url,
  type:"POST",
  data:data,
  contentType:"application/json; charset=utf-8",
  dataType:"json",
  success: function(){
    ...
  }
})

请查看:jQuery.ajax()


18
原始帖子提问:「那么有什么方法可以更改 $.post() 的行为以发送 contentType=application/json 吗?」但它也声明「这样做会影响我所有的 $.get 和 $.post 请求,并导致其中一些请求失败。」我理解问题是「如何实现与使用 $.post 相同的功能但发送正确的 contentType 而不破坏其他 $.get 和 $.post 请求」。这样理解是否有误? - Adrien
6
@x1a4 显然不理解.ajax是函数调用,而不是ajaxSetup。 - Walker
41
@Adrien,两年后的现在,我在谷歌搜索时找到了你提供的答案,这对我很有帮助。 - AwesomeTown
82
由于服务器期望收到一个JSON字符串,而jQuery会使用“与号”来连接键值对以进行form-urlencoded编码,因此必须使用JSON.stringify(data) - dragon
3
自jQuery v1.9版本开始,{ ... type: "POST" ... } 必须替换为 { ... method: "POST" ... }。 - glagola
显示剩余3条评论

106

最终我找到了解决方法,对我有效:

jQuery.ajax ({
    url: myurl,
    type: "POST",
    data: JSON.stringify({data:"test"}),
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    success: function(){
        //
    }
});

10
我一开始不明白为什么一直报错,后来发现需要将数据转换为字符串。 - zastrowm
10
我知道这个方法可以用,但是为什么需要进行字符串化呢?这是 jQuery 的一个 bug 吗?它似乎可以将你的 data 参数序列化为 x-www-form-urlencoded,但是如果你指定请求内容类型为 JSON,它仍然会坚持以不匹配的格式发送 data - Pavel Repin
好的。我没有深入挖掘它。我很高兴它能工作。 ;) 我的服务器需要JSON。 - vvkatwss vvkatwss
1
一样的情况。没有使用JSON.stringify就不起作用,我想知道为什么。 - John Simoes
JSON.stringify() 挽救了我。+1 - Alive to die - Anant

77

我认为你可能需要做以下修改:

1. 修改源代码,使得$.post总是使用JSON数据类型。实际上,它只是$.ajax的一个预配置快捷方式。

或者

2. 定义自己的实用程序函数,作为你想要使用的$.ajax配置的快捷方式

或者

3. 通过猴子补丁(monkey patching)覆盖$.post函数,使用你自己的实现。

在你的示例中,JSON数据类型指的是从服务器返回的数据类型,而不是发送到服务器的格式。


5
+1,我会选择定义一个新的方法或覆盖jQuery.post方法,它是一个非常简单的函数... - Christian C. Salvadó
3
不是个坏主意,只需创建一个名为$.mvcpost()的方法,它执行与$.post()相同的操作(通过复制链接的代码),并更改contenttype。然后,对于需要更改的所有$.post(),我只需在前面输入3个额外字符。这比重写为$.ajax()快得多。 - JK.
9
@PavelRepin,我不得不在有效载荷上调用JSON.stringify()。 - Ustaman Sangat
2
@dragon - 这里有三种解决方案可以让 $.post() 发送 contentType=application/json。哪一部分不是答案? - Russ Cam
2
重要的是要知道:$.ajax及其各种方法将尝试猜测contentType应该是什么(除非已经指定),这取决于您提供的数据。"mystring data"将是application/x-www-form-urlencoded;,而对象{ anyKey: "anyvalue and type" }将是application/json。许多读取json的服务器只允许对象或数组,而不是字符串——这就是为什么jquery以这种方式预测的原因。如果您有一个读取未包装在对象中的字符串、数字等的服务器,则必须像本答案中那样指定content-type。 - Ben Zuill-Smith
显示剩余2条评论

48

我最终在我的脚本中添加了以下jQuery方法:

jQuery["postJSON"] = function( url, data, callback ) {
    // shift arguments if data argument was omitted
    if ( jQuery.isFunction( data ) ) {
        callback = data;
        data = undefined;
    }

    return jQuery.ajax({
        url: url,
        type: "POST",
        contentType:"application/json; charset=utf-8",
        dataType: "json",
        data: data,
        success: callback
    });
};

并且使用它

$.postJSON('http://url', {data: 'goes', here: 'yey'}, function (data, status, xhr) {
    alert('Nailed it!')
});

这是通过简单地复制原始JQuery源代码中的“get”和“post”代码,并硬编码一些参数来强制执行JSON POST来完成的。

谢谢!


2
通常情况下,最佳答案总是最后才出现,并且得到的赞数最少。 ;( - nikib3ro
好的答案 - 需要一段时间才能意识到 $.post 不会“开箱即用”地完成这个功能。 - markp3rry
对我来说仍然需要在数据周围使用JSON.stringify - phyatt

27

只使用

jQuery.ajax ({
    url: myurl,
    type: "POST",
    data: mydata,
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    success: function(){
        //
    }
});

更新 @JK: 如果您在问题中只写了一个使用 $.post 的代码示例,那么您可以在答案中找到相应的示例。我不想重复那些您已经学过的内容:$.post 和 $.get 是 $.ajax 的简写形式。所以,只需使用 $.ajax,您就可以使用其全部参数集合,而无需更改任何全局设置。

顺便说一下,我不建议覆盖标准的 $.post 方法。这是我的个人意见,但对我来说很重要,不仅程序工作正常,而且所有读者都能以相同的方式理解您的程序。没有非常重要的原因就覆盖���准方法可能会导致程序代码的阅读误解。因此,我再次建议:只需使用 jQuery 的原始 $.ajax 形式,而不是 jQuery.getjQuery.post,您将得到完美运行且可被人们完全理解的程序。


1
很好的解释和指南 - Onic Team

17

猜猜看?@BenCreasy是完全正确的!!

从jQuery的 1.12.0版本开始,我们可以这样做:

$.post({
    url: yourURL,
    data: yourData,
    contentType: 'application/json; charset=utf-8'
})
    .done(function (response) {
        //Do something on success response...
    });

我刚刚测试了它,而且它有效!


10

这个简单的jQuery API扩展(来自:https://benjamin-schweizer.de/jquerypostjson.html)可以让$.postJSON()功能更加强大。你可以像使用其他本地的jQuery Ajax调用一样使用postJSON(),还可以附加事件处理程序等。

$.postJSON = function(url, data, callback) {
  return jQuery.ajax({
    'type': 'POST',
    'url': url,
    'contentType': 'application/json; charset=utf-8',
    'data': JSON.stringify(data),
    'dataType': 'json',
    'success': callback
  });
};

与其他Ajax API(如AngularJS的$http)一样,它将正确的contentType设置为application/json。 您可以直接传递您的JSON数据(JavaScript对象),因为它会在此处转换为字符串。 预期返回的dataType被设置为JSON。 您可以附加jquery的默认事件处理程序用于承诺,例如:

$.postJSON(apiURL, jsonData)
 .fail(function(res) {
   console.error(res.responseText);
 })
 .always(function() {
   console.log("FINISHED ajax post, hide the loading throbber");
 });

9
“json”数据类型是您可以作为post()方法的最后一个参数传递的。它指示函数在服务器响应中期望的数据类型,而不是请求中发送的数据类型。具体来说,它会设置“Accept”标头。
老实说,最好改用ajax()调用。post()函数旨在提供方便;对于简单的表单提交,它是ajax()调用的简化版本。但您不是这种情况。
如果您真的不想切换,您可以自己创建一个名为xpost()的函数,并将其简单地转换为jQuery ajax()调用的参数,同时设置内容类型。这样,您只需要将所有的post()函数更改为xpost()(或其他名称),而无需将其全部重写为ajax()函数。

只有调用asp.net mvc控制器方法的$.post()方法需要更改。纯jquery方法应该不变(自动完成、对话框、jqgrid等)。我希望能够对相关的$.post()做出简单的更改,但看起来我需要将它们转换为$.ajax()。这是一个非常ajax密集的大型应用程序,所以需要更改很多地方。 - JK.

6
我知道这是一个晚回答,实际上我有一个快捷的方法来发布/读取基于MS的服务。它适用于MVC以及ASMX等...
使用:
$.msajax(
  '/services/someservice.asmx/SomeMethod'
  ,{}  /*empty object for nothing, or object to send as Application/JSON */
  ,function(data,jqXHR) {
    //use the data from the response.
  }
  ,function(err,jqXHR) {
    //additional error handling.
  }
);

//sends a json request to an ASMX or WCF service configured to reply to JSON requests.
(function ($) {
  var tries = 0; //IE9 seems to error out the first ajax call sometimes... will retry up to 5 times

  $.msajax = function (url, data, onSuccess, onError) {
    return $.ajax({
      'type': "POST"
      , 'url': url
      , 'contentType': "application/json"
      , 'dataType': "json"
      , 'data': typeof data == "string" ? data : JSON.stringify(data || {})
      ,beforeSend: function(jqXHR) {
        jqXHR.setRequestHeader("X-MicrosoftAjax","Delta=true");
      }
      , 'complete': function(jqXHR, textStatus) {
        handleResponse(jqXHR, textStatus, onSuccess, onError, function(){
          setTimeout(function(){
            $.msajax(url, data, onSuccess, onError);
          }, 100 * tries); //try again
        });
      }
    });
  }

  $.msajax.defaultErrorMessage = "Error retreiving data.";


  function logError(err, errorHandler, jqXHR) {
    tries = 0; //reset counter - handling error response

    //normalize error message
    if (typeof err == "string") err = { 'Message': err };

    if (console && console.debug && console.dir) {
      console.debug("ERROR processing jQuery.msajax request.");
      console.dir({ 'details': { 'error': err, 'jqXHR':jqXHR } });
    }

    try {
      errorHandler(err, jqXHR);
    } catch (e) {}
    return;
  }


  function handleResponse(jqXHR, textStatus, onSuccess, onError, onRetry) {
    var ret = null;
    var reterr = null;
    try {
      //error from jqXHR
      if (textStatus == "error") {
        var errmsg = $.msajax.defaultErrorMessage || "Error retreiving data.";

        //check for error response from the server
        if (jqXHR.status >= 300 && jqXHR.status < 600) {
          return logError( jqXHR.statusText || msg, onError, jqXHR);
        }

        if (tries++ < 5) return onRetry();

        return logError( msg, onError, jqXHR);
      }

      //not an error response, reset try counter
      tries = 0;

      //check for a redirect from server (usually authentication token expiration).
      if (jqXHR.responseText.indexOf("|pageRedirect||") > 0) {
        location.href = decodeURIComponent(jqXHR.responseText.split("|pageRedirect||")[1].split("|")[0]).split('?')[0];
        return;
      }

      //parse response using ajax enabled parser (if available)
      ret = ((JSON && JSON.parseAjax) || $.parseJSON)(jqXHR.responseText);

      //invalid response
      if (!ret) throw jqXHR.responseText;  

      // d property wrap as of .Net 3.5
      if (ret.d) ret = ret.d;

      //has an error
      reterr = (ret && (ret.error || ret.Error)) || null; //specifically returned an "error"

      if (ret && ret.ExceptionType) { //Microsoft Webservice Exception Response
        reterr = ret
      }

    } catch (err) {
      reterr = {
        'Message': $.msajax.defaultErrorMessage || "Error retreiving data."
        ,'debug': err
      }
    }

    //perform final logic outside try/catch, was catching error in onSuccess/onError callbacks
    if (reterr) {
      logError(reterr, onError, jqXHR);
      return;
    }

    onSuccess(ret, jqXHR);
  }

} (jQuery));

注意:我还有一个JSON.parseAjax方法,是从json.org的JS文件修改而来的,它增加了对MS“/Date(...)/”日期的处理...

修改后的json2.js文件未包含在内,它在IE8的情况下使用基于脚本的解析器,因为在扩展数组和/或对象原型等情况下,本地解析器会出现错误。

我一直在考虑改进这段代码以实现promises接口,但它对我来说已经非常有效。


6

问题的核心在于,目前的 JQuery 版本中没有 postJSON 方法,而只有 getJSON 方法可以正确地执行任务。

postJSON 方法应该执行以下操作:

postJSON = function(url,data){
    return $.ajax({url:url,data:JSON.stringify(data),type:'POST', contentType:'application/json'});
};

并且可以像这样使用:

postJSON( 'path/to/server', my_JS_Object_or_Array )
    .done(function (data) {
        //do something useful with server returned data
        console.log(data);
    })
    .fail(function (response, status) {
        //handle error response
    })
    .always(function(){  
      //do something useful in either case
      //like remove the spinner
    });

注意!正如其名称所示,getJSON仅以JSON格式返回数据,但这并不意味着它以相同的格式发送数据。当然,在ASP.Net MVC和ASP.Net API中按预期工作,但尝试在具有[ScriptMethod(UseHttpGet = true)]属性的WebMethod(ASP.Net WebForms)中使用它,您会感到惊讶。 - Sagnalrac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接