代码之家  ›  专栏  ›  技术社区  ›  jgilmore

我需要帮助尝试用Javascript捕获网络::ERR_NAME_NOT_RESOLVED

  •  0
  • jgilmore  · 技术社区  · 6 月前

    我试图允许用户将端点列表放入网页中,以测试其有效性。 我以为我可以使用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。

    接下来,我认为加载图像不会导致COR错误,所以我尝试了以下操作:

    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_已解决 在控制台中,但不在任何错误处理程序可访问的变量中。

    Webassembly是否会提供一种方法来实现这一点?

    1 回复  |  直到 6 月前
        1
  •  1
  •   Zeros-N-Ones    6 月前

    我使用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 ,这种方法提供了一种更具编程性的方法来检测和处理不同浏览器之间的此类错误。

    我希望这能有所帮助。