Javascript 确定 ajax 调用是否由于不安全的响应或连接被拒绝而失败
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31058764/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Determine if ajax call failed due to insecure response or connection refused
提问by taxicala
I've been doing a lot of research and could not find a way to handle this. I'm trying to perform a jQuery ajax call from an https server to a locahost https server running jetty with a custom self signed certificate. My problem is that I cannot determine whether the response is a connection refused or a insecure response (due to the lack of the certificate acceptance). Is there a way to determine the difference between both scenarios? The responseText
, and statusCode
are always the same in both cases, even though in the chrome console I can see a difference:
我一直在做很多研究,但找不到处理这个问题的方法。我正在尝试从 https 服务器到使用自定义自签名证书运行 jetty 的 locahost https 服务器执行 jQuery ajax 调用。我的问题是我无法确定响应是连接被拒绝还是不安全响应(由于缺乏证书接受)。有没有办法确定两种情况之间的区别?的responseText
,并且statusCode
总是在这两种情况下是相同的,即使在Chrome控制台我可以看到一个区别:
net::ERR_INSECURE_RESPONSE
net::ERR_CONNECTION_REFUSED
responseText
is always "" and statusCode
is always "0" for both cases.
responseText
statusCode
对于这两种情况,始终为 "" 并且始终为 "0"。
My question is, how can I determine if a jQuery ajax call failed due to ERR_INSECURE_RESPONSE
or due to ERR_CONNECTION_REFUSED
?
我的问题是,如何确定 jQuery ajax 调用失败是由于ERR_INSECURE_RESPONSE
还是由于ERR_CONNECTION_REFUSED
?
Once the certificate is accepted everything works fine, but I want to know whether the localhost server is shut down, or its up and running but the certificate has not yet been accepted.
一旦证书被接受,一切正常,但我想知道 localhost 服务器是否已关闭,或者它已启动并正在运行但证书尚未被接受。
$.ajax({
type: 'GET',
url: "https://localhost/custom/server/",
dataType: "json",
async: true,
success: function (response) {
//do something
},
error: function (xhr, textStatus, errorThrown) {
console.log(xhr, textStatus, errorThrown); //always the same for refused and insecure responses.
}
});
Even performing manually the request I get the same result:
即使手动执行请求,我也会得到相同的结果:
var request = new XMLHttpRequest();
request.open('GET', "https://localhost/custom/server/", true);
request.onload = function () {
console.log(request.responseText);
};
request.onerror = function () {
console.log(request.responseText);
};
request.send();
采纳答案by ecarrizo
There is no way to differentiate it from newest Web Browsers.
没有办法将它与最新的 Web 浏览器区分开来。
W3C Specification:
W3C规范:
The steps below describe what user agents must do for a simple cross-origin request:
Apply the make a request steps and observe the request rules below while making the request.
If the manual redirect flag is unset and the response has an HTTP status code of 301, 302, 303, 307, or 308Apply the redirect steps.
If the end user cancels the requestApply the abort steps.
If there is a network errorIn case of DNS errors, TLS negotiation failure, or other type of network errors, apply the network error steps. Do not request any kind of end user interaction.
Note: This does not include HTTP responses that indicate some type of error, such as HTTP status code 410.
OtherwisePerform a resource sharing check. If it returns fail, apply the network error steps. Otherwise, if it returns pass, terminate this algorithm and set the cross-origin request status to success. Do not actually terminate the request.
下面的步骤描述了用户代理必须为一个简单的跨域请求做什么:
应用发出请求步骤并在发出请求时遵守以下请求规则。
如果手动重定向标志未设置且响应的 HTTP 状态代码为 301、302、303、307 或 308,则应用重定向步骤。
如果最终用户取消请求,则应用中止步骤。
如果出现网络错误 如果出现DNS 错误、TLS 协商失败或其他类型的网络错误,请应用网络错误步骤。不要请求任何类型的最终用户交互。
注意:这不包括指示某种类型错误的 HTTP 响应,例如 HTTP 状态代码 410。
否则执行资源共享检查。如果返回失败,请应用网络错误步骤。否则,如果返回pass,则终止该算法并将跨域请求状态设置为成功。不要实际终止请求。
As you can read, network errors does not include HTTP response that include errors, that is why you will get always 0 as status code, and "" as error.
正如您所读到的,网络错误不包括包含错误的 HTTP 响应,这就是为什么您将始终获得 0 作为状态代码,而“”作为错误。
Note: The following examples were made using Google Chrome Version 43.0.2357.130 and against an environment that I've created to emulate OP one. Code to the set it up is at the bottom of the answer.
注意:以下示例是使用 Google Chrome 版本 43.0.2357.130 制作的,并针对我创建的用于模拟 OP 的环境。设置它的代码位于答案的底部。
I though that an approach To work around this would be make a secondary request over HTTP instead of HTTPS as This answerbut I've remembered that is not possible due that newer versions of browsers block mixed content.
我认为解决这个问题的方法是通过 HTTP 而不是 HTTPS 作为这个答案发出辅助请求,但我记得这是不可能的,因为较新版本的浏览器会阻止混合内容。
That means that the Web Browser will not allow a request over HTTP if you are using HTTPS and vice versa.
这意味着如果您使用 HTTPS,Web 浏览器将不允许通过 HTTP 发出请求,反之亦然。
This has been like this since few years ago but older Web Browser versions like Mozilla Firefox below it versions 23 allow it.
几年前就一直是这样,但较旧的 Web 浏览器版本(如低于 23 版的 Mozilla Firefox)允许这样做。
Evidence about it:
关于它的证据:
Making a HTTP request from HTTPS usign Web Broser console
从 HTTPS 使用 Web Broser 控制台发出 HTTP 请求
var request = new XMLHttpRequest();
request.open('GET', "http://localhost:8001", true);
request.onload = function () {
console.log(request.responseText);
};
request.onerror = function () {
console.log(request.responseText);
};
request.send();
will result in the following error:
将导致以下错误:
Mixed Content: The page at 'https://localhost:8000/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://localhost:8001/'. This request has been blocked; the content must be served over HTTPS.
混合内容:位于“ https://localhost:8000/”的页面已通过 HTTPS 加载,但请求了不安全的 XMLHttpRequest 端点“ http://localhost:8001/”。此请求已被阻止;内容必须通过 HTTPS 提供。
Same error will appear in the browser console if you try to do this in other ways as adding an Iframe.
如果您尝试以添加 Iframe 的其他方式执行此操作,则浏览器控制台中将出现相同的错误。
<iframe src="http://localhost:8001"></iframe>
Using Socket connection was also Posted as an answer, I was pretty sure that the result will be the same / similar but I've give it a try.
使用套接字连接也作为答案发布,我很确定结果将是相同/相似的,但我已经试过了。
Trying to Open a socket connection from the Web Broswer using HTTPS to a non Secure socket endpoint will end in mixed content errors.
尝试使用 HTTPS 打开从 Web Broswer 到非安全套接字端点的套接字连接将以混合内容错误告终。
new WebSocket("ws://localhost:8001", "protocolOne");
1) Mixed Content: The page at 'https://localhost:8000/' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://localhost:8001/'. This request has been blocked; this endpoint must be available over WSS.
2) Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
1) 混合内容:“ https://localhost:8000/”页面已通过 HTTPS 加载,但尝试连接到不安全的 WebSocket 端点“ws://localhost:8001/”。此请求已被阻止;此端点必须可通过 WSS 使用。
2) 未捕获的 DOMException:无法构造“WebSocket”:可能无法从通过 HTTPS 加载的页面启动不安全的 WebSocket 连接。
Then I've tried to connect to a wss endpoint too see If I could read some information about network connection errors:
然后我也尝试连接到 wss 端点,看看是否可以阅读有关网络连接错误的一些信息:
var exampleSocket = new WebSocket("wss://localhost:8001", "protocolOne");
exampleSocket.onerror = function(e) {
console.log(e);
}
Executing snippet above with Server turned off results in:
在服务器关闭的情况下执行上面的代码片段会导致:
WebSocket connection to 'wss://localhost:8001/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
WebSocket 连接到“wss://localhost:8001/”失败:连接建立错误:net::ERR_CONNECTION_REFUSED
Executing snippet above with Server turned On
在服务器打开的情况下执行上面的代码片段
WebSocket connection to 'wss://localhost:8001/' failed: WebSocket opening handshake was canceled
WebSocket 连接到“wss://localhost:8001/”失败:WebSocket 打开握手被取消
But again, the error that the "onerror function" output to the console have not any tip to differentiate one error of the other.
但同样,“onerror 函数”输出到控制台的错误没有任何提示来区分另一个错误。
Using a proxy as this answer suggestcould work but only if the "target" server has public access.
使用代理作为这个答案的建议可以工作,但前提是“目标”服务器具有公共访问权限。
This was not the case here, so trying to implement a proxy in this scenario will lead Us to the same problem.
这不是这里的情况,所以在这种情况下尝试实现代理会导致我们遇到同样的问题。
Code to create Node.js HTTPS server:
创建 Node.js HTTPS 服务器的代码:
I've created two Nodejs HTTPS servers, that use self signed certificates:
我创建了两个 Nodejs HTTPS 服务器,它们使用自签名证书:
targetServer.js:
目标服务器.js:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('./certs2/key.pem'),
cert: fs.readFileSync('./certs2/key-cert.pem')
};
https.createServer(options, function (req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.writeHead(200);
res.end("hello world\n");
}).listen(8001);
applicationServer.js:
应用服务器.js:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('./certs/key.pem'),
cert: fs.readFileSync('./certs/key-cert.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
To make it work you need to have Nodejs Installed, Need to generate separated certificates for each server and store it in the folders certs and certs2 accordingly.
要使其工作,您需要安装 Nodejs,需要为每个服务器生成单独的证书并将其相应地存储在文件夹 certs 和 certs2 中。
To Run it just execute node applicationServer.js
and node targetServer.js
in a terminal (ubuntu example).
运行它只是执行node applicationServer.js
和node targetServer.js
在终端(ubuntu的例子)。
回答by acupajoe
As of now: There is no way to differentiate this event between browers. As the browsers do not provide an event for developers to access.(July 2015)
截至目前:无法在浏览器之间区分此事件。由于浏览器不提供事件供开发人员访问。(2015 年 7 月)
This answer merely seeks to provide ideas for a potential, albiet hacky and incomplete, solution.
这个答案只是试图为一个潜在的、虽然笨拙且不完整的解决方案提供想法。
Disclaimer:this answer is incomplete as it doesn't completelysolve OP's issues (due to cross-origin policies). However the idea itself does has some merit that is further expanded upon by: @artur grzesiak here, using a proxy and ajax.
免责声明:这个答案是不完整的,因为它没有完全解决 OP 的问题(由于跨域策略)。然而,这个想法本身确实有一些优点,通过以下方式进一步扩展:@artur grzesiak here,使用代理和 ajax。
After quite a bit of research myself, there doesn't seem to be any form of error checking for the difference between connection refused and an insecure response, at least as far as javascript providing a response for the difference between the two.
经过我自己的大量研究,似乎没有任何形式的错误检查来检查连接被拒绝和不安全响应之间的差异,至少就 javascript 为两者之间的差异提供响应而言。
The general consensus of my research being that SSL certificates are handled by the browser, so until a self-signed certificate is accepted by the user, the browser locks down all requests, including those for a status code. The browser could (if coded to) send back it's own status code for an insecure response, but that doesn't really help anything, and even then, you'd have issues with browser compatibility (chrome/firefox/IE having different standards... once again)
我研究的普遍共识是 SSL 证书由浏览器处理,因此在用户接受自签名证书之前,浏览器会锁定所有请求,包括对状态代码的请求。浏览器可以(如果编码为)发回它自己的状态代码以获得不安全的响应,但这并没有真正的帮助,即便如此,您也会遇到浏览器兼容性问题(chrome/firefox/IE 具有不同的标准。 .. 再来一次)
Since your original question was for checking the status of your server between being up versus having an unaccepted certificate, could you not make a standard HTTP request like so?
由于您最初的问题是检查服务器在启动与未接受证书之间的状态,您不能像这样发出标准的 HTTP 请求吗?
isUp = false;
isAccepted = false;
var isUpRequest = new XMLHttpRequest();
isUpRequest.open('GET', "http://localhost/custom/server/", true); //note non-ssl port
isUpRequest.onload = function() {
isUp = true;
var isAcceptedRequest = new XMLHttpRequest();
isAcceptedRequest.open('GET', "https://localhost/custom/server/", true); //note ssl port
isAcceptedRequest.onload = function() {
console.log("Server is up and certificate accepted");
isAccepted = true;
}
isAcceptedRequest.onerror = function() {
console.log("Server is up and certificate is not accepted");
}
isAcceptedRequest.send();
};
isUpRequest.onerror = function() {
console.log("Server is down");
};
isUpRequest.send();
Granted this does require an extra request to verify server connectivity, but it should get the job done by process of elimination. Still feels hacky though, and I'm not a big fan of the doubled request.
当然,这确实需要一个额外的请求来验证服务器连接,但它应该通过消除过程来完成工作。尽管如此,我仍然感觉很笨拙,而且我不是双重请求的忠实粉丝。
回答by artur grzesiak
@Schultzie's answer is pretty close, but clearly http
- in general - will not work from https
in the browser environment.
@Schultzie 的答案非常接近,但很明显http
- 一般来说 -https
在浏览器环境中不起作用。
What you can do though is to use an intermediate server (proxy) to make the request on your behalf. The proxy should allow either to forward http
request from https
origin or to load content from self-signed origins.
您可以做的是使用中间服务器(代理)代表您发出请求。代理应该允许http
从https
源转发请求或从自签名源加载内容。
Having your own server with proper certificate is probably an overkill in your case -- as you could use this setting instead of the machine with self-signed certificate -- but there is a plenty of anonymous open proxy servicesout there.
在您的情况下,拥有带有适当证书的自己的服务器可能是一种矫枉过正——因为您可以使用此设置而不是带有自签名证书的机器——但是那里有很多匿名开放代理服务。
So two approaches that come to my mind are:
所以我想到的两种方法是:
- ajax request-- in such a case the proxy has to use appropriate CORS settings
- use of an iframe-- you load your script (probably wrapped in html) inside an iframe via the proxy. Once the script loaded it sends a message to its
.parentWindow
. If your window received a message you can be sure the server is running (or more precisely was running a fraction of second before).
- ajax 请求——在这种情况下,代理必须使用适当的 CORS 设置
- 使用 iframe——您通过代理将脚本(可能包含在 html 中)加载到 iframe 中。脚本加载后,它会向其
.parentWindow
. 如果您的窗口收到一条消息,您可以确定服务器正在运行(或者更准确地说是在几分之一秒之前运行)。
If you are only interested in your local environment you can try to run chrome with --disable-web-security
flag.
如果您只对本地环境感兴趣,可以尝试使用--disable-web-security
标志运行 chrome 。
Another suggestion: did you try to load an image programatically to find out if more info is present there?
另一个建议:您是否尝试以编程方式加载图像以查看那里是否存在更多信息?
回答by Varshaan
Check out jQuery.ajaxError()Taken reference from : jQuery AJAX Error Handling (HTTP Status Codes)It catches global Ajax errors which you can handle in any number of ways over HTTP or HTTPS:
查看jQuery.ajaxError()参考自:jQuery AJAX 错误处理(HTTP 状态代码)它捕获全局 Ajax 错误,您可以通过 HTTP 或 HTTPS 以多种方式处理这些错误:
if (jqXHR.status == 501) {
//insecure response
} else if (jqXHR.status == 102) {
//connection refused
}
回答by maratbn
Unfortunately the present-day browser XHR API does not provide an explicit indication for when the browser refuses to connect due to an "insecure response", and also when it does not trust the website's HTTP/SSL certificate.
不幸的是,当前的浏览器 XHR API 没有明确指示浏览器何时因“不安全响应”而拒绝连接,以及何时不信任网站的 HTTP/SSL 证书。
But there are ways around this problem.
但是有办法解决这个问题。
One solution I came up with to determine when the browser does not trust the HTTP/SSL certificate, is to first detect if an XHR error has occurred (using the jQuery error()
callback for instance), then check if the XHR call is to an 'https://' URL, and then check if the XHR readyState
is 0, which means that the XHR connection has not even been opened (which is what happens when the browser does not like the certificate).
我想出的一个解决方案是确定浏览器何时不信任 HTTP/SSL 证书,首先检测是否发生了 XHR 错误(例如使用 jQueryerror()
回调),然后检查 XHR 调用是否为“https” ://' URL,然后检查 XHRreadyState
是否为 0,这意味着 XHR 连接还没有打开(浏览器不喜欢证书时会发生这种情况)。
Here's the code where I do this: https://github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88
这是我执行此操作的代码:https: //github.com/maratbn/RainbowPayPress/blob/e9e9472a36ced747a0f9e5ca9fa7d96959aeaf8a/rainbowpaypress/js/le_requirejs/public/model_info__transaction_details.js#L88
回答by gafi
I don't think there's currently a way to detect these error messages, but a hack that you can do is to use a server like nginx in front of your application server, so that if the application server is down you'll get a bad gateway error from nginx with 502
status code which you can detect in JS. Otherwise, if the certificate is invalid you'll still get the same generic error with statusCode = 0
.
我认为目前没有检测这些错误消息的方法,但是你可以做的一个技巧是在你的应用程序服务器前面使用一个像 nginx 这样的服务器,这样如果应用程序服务器宕机,你会得到一个坏消息来自 nginx 的网关错误,带有502
您可以在 JS 中检测到的状态代码。否则,如果证书无效,您仍然会遇到与statusCode = 0
.