无法在jQuery.ajax中设置内容类型为'application/json'

120

当我有以下代码时

$.ajax({
    type: 'POST',
    //contentType: "application/json",
    url: 'http://localhost:16329/Hello',
    data: { name: 'norm' },
    dataType: 'json'
});

在 Fiddler 中,我可以看到以下原始请求

POST http://localhost:16329/Hello HTTP/1.1
Host: localhost:16329
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:14693/WebSite1/index.html
Content-Length: 9
Origin: http://localhost:14693
Pragma: no-cache
Cache-Control: no-cache

name=norm

但是我尝试的是将内容类型从application/x-www-form-urlencoded更改为application/json。 但是这段代码

$.ajax({
    type: "POST",
    contentType: "application/json",
    url: 'http://localhost:16329/Hello',
    data: { name: 'norm' },
    dataType: "json"
});

产生了奇怪的请求(可以在 Fiddler 中看到)

OPTIONS http://localhost:16329/Hello HTTP/1.1
Host: localhost:16329
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Origin: http://localhost:14693
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Pragma: no-cache
Cache-Control: no-cache
为什么会这样?当应该是POST的时候,为什么是OPTIONS?我的content-type设置为application/json在哪里?请求参数因某种原因而丢失。
更新1
服务器端我有一个非常简单的RESTful服务。
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class RestfulService : IRestfulService
{
    [WebInvoke(
        Method = "POST",
        UriTemplate = "Hello",
        ResponseFormat = WebMessageFormat.Json)]
    public string HelloWorld(string name)
    {
        return "hello, " + name;
    }
}

但出于某种原因,我无法使用参数调用此方法。

更新2

很抱歉没有回答那么久。

我已将这些标头添加到我的服务器响应中。

 Access-Control-Allow-Origin: *
 Access-Control-Allow-Headers: Content-Type
 Access-Control-Allow-Methods: POST, GET, OPTIONS

无济于事,我从服务器收到了方法不允许的错误。

这是我的Fiddler所显示的内容:

enter image description here

现在我可以确定如果响应头如我所预期地工作,那么我的服务器将接受POST、GET、OPTIONS请求方式。但为什么会出现“方法不允许”的错误呢?

在WebView中,来自服务器的响应(您可以在上面的图片中看到Raw响应)如下所示:

enter image description here


3
你应该尝试使用JSON.stringfy()方法。 - Amritpal Singh
看这里。这对我非常有效:https://dev59.com/qmkw5IYBdhLWcg3wn8CO#18740041 - Fanda
1
嗨@VitaliiKorsakov。你解决了你的问题吗?我遇到了同样的问题,即无法修改contentType。 - worldterminator
同样的问题。需要一些帮助。 - Marvin W
1
我曾经遇到过同样的问题,现在已经解决了。解决方案在这个页面的答案中:https://dev59.com/pmIj5IYBdhLWcg3waEZe 。总结一下:“当使用contentType: 'application/json'时,您将无法依赖于$_POST被填充。 $_POST仅对表单编码的内容类型进行填充。因此,您需要从PHP原始输入中读取数据。”我现在看到您在服务器端没有使用php,但希望这些信息能以某种方式帮助到您。 - Sarah
显示剩余3条评论
11个回答

105
看起来从 URL 选项中删除 http:// 可以确保发送正确的 HTTP POST 标头。 我认为你不需要完全限定主机名称,只需使用以下形式的相对 URL。
   $.ajax({
      type: "POST",
      contentType: "application/json",
      url: '/Hello',
      data: { name: 'norm' },
      dataType: "json"
   });

一个我自己写的可行示例:

        $.ajax({
            type: "POST",
            url: siteRoot + "api/SpaceGame/AddPlayer",
            async: false,
            data: JSON.stringify({ Name: playersShip.name, Credits: playersShip.credits }),
            contentType: "application/json",
            complete: function (data) {
            console.log(data);
            wait = false;
        }
    });

可能相关: jQuery $.ajax(),在Firefox中将$.post发送的“OPTIONS”作为REQUEST_METHOD

经过更多研究,我发现OPTIONS头用于确定是否允许来自原始域的请求。使用Fiddler,我将以下内容添加到我的服务器响应标头中。

 Access-Control-Allow-Origin: *
 Access-Control-Allow-Headers: Content-Type
 Access-Control-Allow-Methods: POST, GET, OPTIONS

浏览器接收到响应后,它会使用JSON数据发送正确的POST请求。似乎默认的form-urlencoded内容类型被认为是安全的,因此不需要进行额外的跨域检查。

看起来您需要将先前提到的头部添加到服务器对OPTIONS请求的响应中。当然,您应该将其配置为仅允许特定域名的请求,而不是所有域名。

我使用了以下jQuery代码来测试这一点。

$.ajax({
   type: "POST",
   url: "http://myDomain.example/path/AddPlayer",
   data: JSON.stringify({
      Name: "Test",
       Credits: 0
   }),
   //contentType: "application/json",
   dataType: 'json',
   complete: function(data) {
       $("content").html(data);
  }
});​

参考资料:


我想要在客户端和服务器之间实现松耦合。服务器是RESTful服务,所有使用该服务的客户端都应该知道它的URL。 - Vitalii Korsakov
你能在帖子中提供更多关于这个问题场景的细节吗?如果你的客户将在不同的域上,你可能会遇到同源策略问题。 - Mike Wade
@Spike,感谢你提供的预检信息。在我看来,这个答案应该被接受。 - Ray Toal
请看我帖子中的“更新2”。@RayToal,我无法接受这个答案,因为它对我不起作用。 - Vitalii Korsakov
JSON.stringify()帮了我大忙——我已经为此问题努力工作了一段时间,幸运的是我从你的工作示例中找到了它!没有这个函数,我的请求格式不正确——不知道为什么,尝试了每种可能的JSON语法变化,只有它起了作用。 - Nathan Hornby
显示剩余3条评论

45

我可以展示给你我如何使用它

  function GetDenierValue() {
        var denierid = $("#productDenierid").val() == '' ? 0 : $("#productDenierid").val();
        var param = { 'productDenierid': denierid };
        $.ajax({
            url: "/Admin/ProductComposition/GetDenierValue",
            dataType: "json",
            contentType: "application/json;charset=utf-8",
            type: "POST",
            data: JSON.stringify(param),
            success: function (msg) {
                if (msg != null) {
                    return msg.URL;
                }
            }
        });
    }

和下一个答案一样。我无法指定托管所有服务功能的服务器的URL。 - Vitalii Korsakov
@VitaliiKorsakov 我离开了,你解决问题了吗? - Amritpal Singh
谢谢你的回答!我简直不敢相信其他地方没有明确说明这一点。看起来当你指定类型为“json”时,JQuery会发布json,但实际上并非如此... - Jason Goemaat
1
@JasonGoemaat,jQuery中的dataType参数仅用于解析返回的响应主体。如果您阅读文档,就会发现它甚至不需要。dataType的默认值是智能猜测。您的问题在于jquery中的数据属性不可配置。您无法告诉jquery如何解析数据对象。这就是为什么您必须先序列化json的原因。因为jquery只能序列化为url-form-encode。 - Loïc Faure-Lacroix

12

所以,要使这个方法起作用,你需要添加:

headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
}
将其作为您的帖子请求的字段,并且它会正常工作。

如果您查看文档,它会指定默认为“application/x-www-form-urlencoded; charset=UTF-8”(这就是为什么会发生这种情况的原因。我不知道为什么仅设置contentType不起作用。如果您使用的是旧版本,请检查jQuery的版本并进行更新)。 - Cody Jacques
这个不行。虽然我已经设置了 type: "POST",它仍然发送的是OPTIONS - user9645

6
如果您使用这个:
contentType: "application/json"

AJAX不会将GET或POST参数发送到服务器...不知道为什么。

今天我花了几个小时学习它。

只需使用:

$.ajax(
  { url : 'http://blabla.example/wsGetReport.php',
    data : myFormData, type : 'POST', dataType : 'json',
    // contentType: "application/json",
    success : function(wsQuery) { }
  }
)

1
很遗憾,对我来说正确的答案是:省略contentType,只使用dataType来绕过CORS OPTIONS垃圾,因为许多服务都没有正确实现。真是令人恼火。 - Umopepisdn

5

我认识那些屏幕,我正在使用CodeFluentEntities,而且我也有解决方案可以让它为我工作。

我正在使用这个结构:

$.ajax({
   url: path,
   type: "POST",
   contentType: "text/plain",
   data: {"some":"some"}
}

正如您所见,如果我使用

contentType: "",

或者

contentType: "text/plain", //chrome

一切都正常。

我不确定这是否是你所需要的全部内容,因为我还更改了标题。


2
我在这里找到了解决此问题的方法。点击这里查看。请勿忘记在IIS应用程序服务处理程序上允许OPTIONS动词。

运行良好。谢谢André Pedroso。:-)


1
请勿发布“仅链接答案”的回复(https://meta.stackexchange.com/questions/225370/your-answer-is-in-another-castle-when-is-an-answer-not-an-answer)。该链接位于登录墙后面,请在帖子本身中包含相关信息。帖子中不必说“谢谢”。 - ggorlen

2

我曾经遇到过同样的问题,原因是缺少了JSON.stringfy()。

data: JSON.stringfy({ name: 'norm' }),

希望这篇文章能为其他人节省大量时间!


1
我遇到了相同的问题。我在JBoss服务器上运行一个Java REST应用程序。但是我认为,对于ASP .NET Web应用程序,解决方案也是类似的。
Firefox向您的服务器/REST URL发出预调用以检查允许哪些选项。这就是您的服务器没有正确回复的“OPTIONS”请求。如果正确地回复了此OPTIONS调用,则会执行第二个调用,即实际的带有JSON内容的“POST”请求。
仅在执行跨域调用时才会发生这种情况。在您的情况下,调用“http://localhost:16329/Hello”而不是调用同一域下的URL路径“/Hello”。
如果您打算进行跨域调用,则必须使用支持“OPTIONS”HTTP请求的注释方法增强您的REST服务类。这是相应的Java实现:
@Path("/rest")
public class RestfulService {

    @POST
    @Path("/Hello")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.TEXT_PLAIN)
    public string HelloWorld(string name)
    {
        return "hello, " + name;
    }

//THIS NEEDS TO BE ADDED ADDITIONALLY IF MAKING CROSS-DOMAIN CALLS

    @OPTIONS
    @Path("/Hello")
    @Produces(MediaType.TEXT_PLAIN+ ";charset=utf-8")
    public Response checkOptions(){
        return Response.status(200)
        .header("Access-Control-Allow-Origin", "*")
        .header("Access-Control-Allow-Headers", "Content-Type")
        .header("Access-Control-Allow-Methods", "POST, OPTIONS") //CAN BE ENHANCED WITH OTHER HTTP CALL METHODS 
        .build();
    }
}

我想你在.NET中需要添加一个附有注解的额外方法。
[WebInvoke(
        Method = "OPTIONS",
        UriTemplate = "Hello",
        ResponseFormat = WebMessageFormat.)]

以下标头设置在哪里

.header("Access-Control-Allow-Origin", "*")
        .header("Access-Control-Allow-Headers", "Content-Type")
        .header("Access-Control-Allow-Methods", "POST, OPTIONS")

0

我找到了通过jQuery Ajax发送POST请求的JSON数据的解决方案。我使用了以下代码:

    var data = new Object();
    data.p_clientId = 4;
    data =  JSON.stringify(data);

    $.ajax({
      method: "POST",
      url: "http://192.168.1.141:8090/api/Client_Add",
      data: data,
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'text/plain'
    }
    })
      .done(function( msg ) {
        alert( "Data Saved: " + msg );
      });


        });
    });

我在头部使用了'Content-Type': 'text/plain'来发送原始的JSON数据。
因为如果我们使用Content-Type: 'application/json',请求方法会被转换为OPTION,但是使用Content-Type: 'test/plain',方法不会被转换,仍然保持为POST。 希望这能帮助到某些人。

3
它并不是真正地转换为选项,而是发送CORS预检请求来检查是否允许进行POST。当这个请求返回不正确时,POST请求就不会发生。 - Umopepisdn

0
jQuery.ajax文档中,contentType指定要发送到服务器的内容类型。 默认值为"application/x-www-form-urlencoded; charset=UTF-8"。 对于跨域请求,如果设置了除application/x-www-form-urlencodedmultipart/form-datatext/plain之外的内容类型,则将在请求头中发送OPTIONS方法。这是ajax通过CORS策略进行的检查,以检查您在您的域中向服务器请求的方法(在本例中为POST)是否被允许。 如果允许在托管API的服务器上使用OPTIONS方法,则最初发送到服务器的请求就会被发送,即发送POST请求。
如果您正在向同一域和端口发出请求,则不会发送OPTIONS方法。 如果是这种情况,则解决方案之一是使用"http://localhost:<port>..."或者只使用相对路径来发出请求。
但是,如果后端的REST API位于另一个域中,则有两个解决方案。
  1. 不要在ajax中加入contentType: "application/json",并将数据以JSON格式作为对象或字符串发送,或者作为带有要发送类型的参数的字符串key=value
  2. 在您的Rest API中启用CORS(即允许任何域或您发出请求的域使用OPTIONS、POST方法)。

问题在于,在某些情况下,有必要将要发送的内容类型设置为"application/json",因为托管API的服务器不接受其他类型的内容。

如果您对后端和REST API具有控制权,则建议在其中启用CORS。(如果是同一域但不同端口也适用。)

 Access-Control-Allow-Origin: *
 Access-Control-Allow-Headers: Content-Type
 Access-Control-Allow-Methods: POST, GET, OPTIONS

或者允许其他类型的内容,例如application/x-www-form-urlencoded,或以这种方式编码,使其也接受带有key=value参数的字符串。这可以防止jQuery在其请求头中发送OPTIONS

最后一件事:如果使用contentType: "application/json",并且服务器也期望"application/json",则应对data使用JSON.stringify(),因为在将请求发送到服务器时,它似乎将JSON视为字符串而不是对象。在我的情况下,不使用JSON.stringify()并使用contentType: "application/json"会返回状态500的服务器错误。


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