我试图允许用户将端点列表拖放到网页中以测试它们的有效性。 我以为我可以使用 AJAX,但事实证明这是有问题的。
我知道我可以在服务器端执行此操作,但我预计我会收到数百个请求,并且我更愿意让每个用户的浏览器处理此问题。起初我认为这是可能的,因为 Edge 和 Chrome 都在控制台中显示 net::ERR_NAME_NOT_RESOLVED 。我认为这个特定的错误可能在浏览器中级别太低,并且无法在 Javascript 层访问。
以下是我的第一次尝试
var req = new XMLHttpRequest();
req.open("get", "http://xxgdvgshd.com"); // non existent endpoint
req.onerror = (e) => {
// gets invoked but no indication of the actual error
console.log(e);
console.log(req.readyState,e.readyState);
console.log(req.status,e.status,req.statusText,e.statusText);
}
req.onreadystatechange = (e) => {
console.log(e);
console.log(req.readyState,e.readyState);
console.log(req.status,e.status,req.statusText,e.statusText);
}
req.onload = (e) => { // never gets invoked
console.log(e);
console.log(req.readyState,e.readyState);
console.log(req.status,e.status,req.statusText,e.statusText);
}
try {
req.send();
} catch(e) {
// Won't be executed during asychronous call.
console.log(e);
console.log(req.readyState,e.readyState);
console.log(req.status,e.status,req.statusText,e.statusText);
}
我没有收到任何指示 DNS 错误的信息。具体来说,Firefox 给了我一个 COR 错误。我无法使用该方法,因为它懒得查看端点是否有与其关联的 DNS。
接下来,我认为加载图像不会导致CORs错误,所以我尝试了这个:
var img = new Image(10, 10);
img.src = "http://baddomain.ddd";
img.onerror = (e) => {
// Edge & Chrome writes 'net::ERR_NAME_NOT_RESOLVED' to the console, but is not accessible in any error handlers
// Firefox essentially gives no indication there's any problem other than invoking this handler.
console.log(e);
}
相同的结果:我在控制台中看到 net::ERR_NAME_NOT_RESOLVED,但在任何错误处理程序可访问的变量中都看不到。
WebAssembly 会提供一种方法来做到这一点吗?
我使用带有超时机制的 Fetch API 创建了一个基于浏览器的端点有效性检查器,以测试跨浏览器的端点有效性。尝试一下,看看是否有效:
class EndpointValidator {
/**
* Check if an endpoint is valid (resolvable and responds)
* @param {string} url - The URL to test
* @param {number} timeout - Timeout in milliseconds (default 5000)
* @returns {Promise<object>} Validation result
*/
static async checkEndpoint(url, timeout = 5000) {
try {
// Ensure URL has a protocol
const fullUrl = url.startsWith('http://') || url.startsWith('https://')
? url
: `http://${url}`;
// Create an AbortController to manage timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(fullUrl, {
method: 'HEAD',
mode: 'no-cors',
signal: controller.signal
});
clearTimeout(timeoutId);
return {
valid: true,
url: fullUrl,
status: response.status
};
} catch (error) {
clearTimeout(timeoutId);
// Detailed error checking
if (error.name === 'AbortError') {
return {
valid: false,
url: fullUrl,
error: 'Timeout',
details: 'Request took too long to respond'
};
}
// Attempt to diagnose specific issues
return this.diagnoseEndpointError(fullUrl, error);
}
} catch (generalError) {
return {
valid: false,
url,
error: 'Unhandled Error',
details: generalError.message
};
}
}
/**
* Attempt to provide more detailed error diagnosis
* @param {string} url - The URL being tested
* @param {Error} error - The caught error
* @returns {object} Detailed error information
*/
static diagnoseEndpointError(url, error) {
// Try to provide more context about the error
const errorLowerCase = error.message.toLowerCase();
const errorTypes = [
{
pattern: /failed to fetch|network error/i,
type: 'Network Error',
details: 'Unable to resolve domain or connect to server'
},
{
pattern: /cors|cross-origin/i,
type: 'CORS Error',
details: 'Cross-Origin Resource Sharing restriction'
},
{
pattern: /dns/i,
type: 'DNS Error',
details: 'Domain name could not be resolved'
}
];
const matchedError = errorTypes.find(err => err.pattern.test(errorLowerCase));
return {
valid: false,
url,
error: matchedError ? matchedError.type : 'Connection Error',
details: matchedError ? matchedError.details : error.message
};
}
/**
* Validate multiple endpoints
* @param {string[]} urls - Array of URLs to test
* @param {number} concurrency - Maximum concurrent checks
* @returns {Promise<object[]>} Array of validation results
*/
static async validateEndpoints(urls, concurrency = 5) {
// Use Promise.all with limited concurrency
const results = [];
for (let i = 0; i < urls.length; i += concurrency) {
const batch = urls.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map(url => this.checkEndpoint(url))
);
results.push(...batchResults);
}
return results;
}
}
// Example usage
async function testEndpoints() {
const endpoints = [
'google.com',
'nonexistentdomain.xyz',
'http://example.com'
];
const results = await EndpointValidator.validateEndpoints(endpoints);
console.log(results);
}
// Expose for global use if needed
window.EndpointValidator = EndpointValidator;
它使用
fetch()
和 no-cors
来绕过 CORS 限制,并提供超时以防止无限期等待。它还尝试诊断不同类型的连接错误。它支持完整的 url 和域名,并允许批量处理端点。
要使用它,您只需拨打:
// Check a single endpoint
EndpointValidator.checkEndpoint('google.com')
.then(result => console.log(result));
// Validate multiple endpoints
EndpointValidator.validateEndpoints(['google.com', 'example.com'])
.then(results => console.log(results));
根据您对
net::ERR_NAME_NOT_RESOLVED
的观察,这种方法提供了一种更具编程性的方法来跨不同浏览器检测和处理此类错误。
我希望这有帮助。