jQuery AJAX调用结果显示错误状态码403。

16

使用jQuery AJAX向Web服务进行查询。我的查询如下:

var serviceEndpoint = 'http://example.com/object/details?version=1.1';
$.ajax({
  type: 'GET', 
  url: serviceEndpoint,
  dataType: 'jsonp',
  contentType: 'jsonp',
  headers: { 'api-key':'myKey' },
  success: onSuccess,
  error: onFailure
});
当我执行这个时,我得到了一个状态错误403。我不明白为什么我的调用会导致状态码为403的结果。我控制着我的服务安全,它被标记为完全开放。我知道密钥是有效的,因为我在另一个调用中使用它,那个调用是成功的。这是可以运行的调用:
var endpoint = 'http://example.com/object/data/item?version=1.1';
$.ajax({ 
  type: 'POST', 
  url: endpoint, 
  cache: 'false',
  contentType:'application/json',
  headers: {
    'api-key':'myKey',
    'Content-Type':'application/json'
  },
  data: JSON.stringify({
    id: 5,
    count:true
  }),
  success: onDataSuccess,
  error: onDataFailure
});

我知道这是两个不同的端点。但我百分之百地相信这不是服务器端身份验证或权限错误。再次强调,服务器端一切都是公开的。这意味着我在客户端请求上犯了一些错误。

我感觉我应该说明这个请求是在开发期间进行的。所以,我是从http://localhost:3000运行这个请求的。因此,我立即认为这是一个CORS问题。但是一切看起来都是正确的。我的POST请求可以工作,但我的GET却不行,这让我非常沮丧。我有什么遗漏吗?可能是什么原因导致的?


2
你有没有尝试直接在浏览器中打开那个URL?你是否缺少与有效URL匹配的“/data/”部分? - charlietfl
1
请注意,您无法为 jsonp 请求发送头信息,因为它是一个脚本请求。您确定要使用 jsonp 而不是 json 吗?另外,为什么要对头信息使用 JSON.stringify()?由于没有发送任何正文内容,GET 请求没有请求 contentType。您有许多问题,其中任何一个都可能是问题的原因。 - charlietfl
jsonp是一种不同于ajax的请求类型,它不允许使用头部信息。从所展示的内容中,我们对问题了解得还不够。 - charlietfl
我正在使用Chrome浏览器。所有内容都在index.html文件中。我通过lite-server(https://github.com/johnpapa/lite-server)运行页面。 - Some User
@user1917363 - 没有安装广告拦截器。 - Some User
显示剩余5条评论
3个回答

24

403错误的原因是您没有发送标头。由于您正在进行 CORS 请求,除非服务器通过添加Access-Control-Allow-Headers到响应中启用这些标头,否则您不能发送任何自定义标头。

预检请求中,客户端向服务器发出两个请求。第一个是预检(使用OPTIONS方法),第二个是实际请求。服务器会将Access-Control-Allow-Headers 标头作为预检请求的响应发送。因此,它启用了一些标头以便发送。通过这种方式,您的POST请求可以工作,因为POST请求是预检请求。但对于GET请求,没有预检来收集Access-Control-Allow-Headers 标头,在这种情况下浏览器不会发送您的自定义标头。

此问题的解决方法:

作为解决方法,将您的dataTypecontentType设置为 json,如以下代码所示:

var serviceEndpoint = 'http://example.com/object/details?version=1.1';
$.ajax({
  type: 'GET', 
  url: serviceEndpoint,
  dataType: 'json',
  contentType: 'json',
  headers: { 'api-key':'myKey' },
  success: onSuccess,
  error: onFailure
});

通过这种方式,您的GET请求将成为一个预检请求。如果您的服务器使用Access-Control-Allow-Headers头启用了api-key,它将起作用。

上述请求的示例服务器配置(使用express.js编写):

res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', '*');
res.setHeader('Access-Control-Allow-Headers', 'api-key,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);

新增:

实际上,在进行 JSONP 请求时,contentType 应该是 application/javascriptapplication/json,而不是 jsonp


res.setHeader代码应该写在哪里?另外,res是什么类型的对象? - Zoran777
@Zoran777,这将在服务器端进行。express 服务器中 res 的文档在此处:http://expressjs.com/en/api.html#app.get.method。 - ykaragol

1
如果您查看jQuery的Ajax调用的API页面,它在Content-Type部分中提到以下内容:
注意:对于跨域请求,将内容类型设置为application/x-www-form-urlencoded、multipart/form-data或text/plain以外的任何内容都会触发浏览器向服务器发送预检OPTIONS请求。
该页面并没有真正提到“预检OPTIONS请求”是什么,但我在查找这个短语时在网上找到了一些有趣的链接:
- Preflight Blob Request - HTML 5 Rocks Using CORS - W3C's Cross-Origin Resource Sharing规范 - Using CORS for Cross-Domain Ajax Requests 有趣的是在HTML5Rocks页面中的代码示例CORS图像。该图像展示了JavaScript代码如何通过浏览器向服务器发起Ajax调用,以及这些响应如何在三者之间往返传输。
我们通常认为JavaScript + 浏览器 = 客户端,但在这个例子中,作者解释了Web开发人员的代码与浏览器开发人员的代码之间的区别,前者使用JavaScript编写,而后者则使用C、C++或C#编写。
一个好的数据包分析工具是Fiddler,它类似于Wireshark。这两个工具中的任何一个都应该显示从浏览器发送到服务器的预检请求。很可能,你的Ajax请求被服务器阻止在那里,并显示403 Forbidden error错误。

0

参见这篇文章,其中讨论了Google翻译扩展程序将多余的HTML附加到某些页面元素的问题。

例如,在上面链接的案例中,Google翻译添加了以下这些行:

<p>&nbsp;</p>
<div id="gtx-trans" style="position: absolute; left: -28px; top: -8px;">&nbsp;</div>

将用户输入的数据发送到后端时,其中包含了Google翻译的内容,导致403错误。请注意将其从TinyMCE编辑器内容中删除。


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