programing

크로스 오리진(CORS) 오류 검출 방법Javascript의 XMLHttpRequest()에 대한 기타 오류 유형

minimums 2023. 3. 4. 14:26
반응형

크로스 오리진(CORS) 오류 검출 방법Javascript의 XMLHttpRequest()에 대한 기타 오류 유형

XMLHttpRequest()가 부정한 요구가 아닌 Cross Origin Error로 인해 실패했을 때를 검출하려고 합니다.예를 들어 다음과 같습니다.

    ajaxObj=new XMLHttpRequest()
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);

url에 대해 4가지 경우를 고려합니다.

케이스 1:url은 access-control-allow-origin이 올바르게 설정되어 있는 유효한 주소입니다.

  • ::http://192.168.8.35「 」를 .Access-Control-Allow-Origin: *
  • 이는 ajaxObj.readyState==4 및 ajaxObj.status==200으로 쉽게 감지됩니다.

케이스 2: url은 기존 서버의 잘못된 주소입니다.

  • ::http://xyz.google.com가 .
  • 따라서 ajaxObj.readyState==4 및 ajaxObj.status==0이 됩니다.

케이스 3:url은 존재하지 않는 서버의 IP 주소입니다.

  • ::http://192.168.8.6할 것이
  • 따라서 ajaxObj.readyState==4 및 ajaxObj.status==0이 됩니다.

케이스 4:url은 access-control-allow-origin이 설정되어 있지 않은 유효한 주소입니다.

  • ::http://192.168.8.247서버에는, Access-Control-Allow-Origin: *
  • 따라서 ajaxObj.readyState==4 및 ajaxObj.status==0이 됩니다.

문제는 다음과 같습니다.케이스 4(access-control-allow-origin 오류)와 케이스2&3을 구별하려면 어떻게 해야 하나요?

케이스 4의 경우 Chrome 디버깅콘솔에 다음 오류가 표시됩니다.

XMLHttpRequest cannot load http://192.168.8.247/. Origin http://localhost is not allowed by Access-Control-Allow-Origin.

Javascript에서 어떻게 그 오류를 알릴 수 있습니까?

나는 어떤 징후를 찾으려 했다.ajaxObj케이스 2와 케이스 3과 다른 점은 없는 것 같습니다.

사용한 간단한 테스트는 다음과 같습니다.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CORS Test</title>
<script type="text/javascript">
function PgBoot()
{
//  doCORS("http://192.168.8.35");   // Case 1
//  doCORS("http://xyz.google.com"); // Case 2
    doCORS("http://192.168.8.6");    // Case 3
//  doCORS("http://192.168.8.247");  // Case 4
}

function doCORS(url)
{
    document.getElementById("statusDiv").innerHTML+="Processing url="+url+"<br>";
    var ajaxObj=new XMLHttpRequest();
    ajaxObj.overrideMimeType('text/xml');
    ajaxObj.onreadystatechange = function()
    {
        var stat=document.getElementById("statusDiv");
        stat.innerHTML+="readyState="+ajaxObj.readyState;
        if(ajaxObj.readyState==4)
            stat.innerHTML+=", status="+ajaxObj.status;
        stat.innerHTML+="<br>";
    }
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);
}
</script>
</head>
<body onload="PgBoot()">
<div id="statusDiv"></div>
</body>
</html>

Chrome을 사용한 결과:

Processing url=http://192.168.8.35
readyState=1
readyState=2
readyState=3
readyState=4, status=200

Processing url=http://xyz.google.com
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.6
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.247
readyState=1
readyState=4, status=0

아니요, W3C 사양에 따르면 차이를 구별할 방법이 없습니다.

CORS 사양은 다음과 같이 단순한 크로스 오리진 요구 절차를 규정하고 있습니다.

요청 작성 단계를 적용하고 요청을 작성할 때 다음 요청 규칙을 준수하십시오.

수동 리다이렉트플래그가 설정되어 있지 않고 응답의 HTTP 상태 코드가 301, 302, 303, 307 또는 308인 경우: 리다이렉트 단계를 적용합니다.

최종 사용자가 요청을 취소하는 경우: 중단 단계를 적용합니다.

네트워크 오류가 있는 경우:DNS 오류, TLS 네고시에이션 실패 또는 기타 유형의 네트워크 오류의 경우 네트워크 오류 단계를 적용합니다.최종 사용자의 조작을 요구하지 않음...

그렇지 않은 경우:자원 공유 체크를 실행합니다.실패가 반환되면 네트워크 오류 단계를 적용합니다.

네트워크 접속에 실패했을 경우 또는 CORS 교환에 실패했을 경우, 네트워크 에러 순서가 적용되기 때문에, 문자 그대로 2개의 케이스를 구별할 수 없습니다.

하는 입니다.들어 웹몇 가지 정보( 등할 수 ./8 ★★★★★★★★★★★★★★★★★」/16라우터는 CORS 헤더를 송신하지 않기 때문에(또는 송신하지 않는) 스크립트는 아무것도 학습하지 않습니다.

혹시나 도움이 될지도 모르니까...코르스와 네트워크 오류 사이의 차이를 처리하는 다른 방법 중 하나...Chrome 또는 Firefox를 사용할 수 있습니다.(단, 완벽한 솔루션은 아닙니다)

var external = 'your url';

if (window.fetch) {
    // must be chrome or firefox which have native fetch
    fetch(external, {'mode':'no-cors'})
        .then(function () {
            // external is reachable; but failed due to cors
            // fetch will pass though if it's a cors error
        })
        .catch(function () {
            // external is _not_ reachable
        });
} else {
    // must be non-updated safari or older IE...
    // I don't know how to find error type in this case
}

CORS 위반을 실패한 다른 AJAX 요청과 구별하기 위해 서버 측 코드를 사용하여 HEAD 요청 응답 헤더를 검사하고 결과를 클라이언트페이지로 되돌릴 수 있습니다.예를 들어, AJAX 요구가 실패했을 경우(상태 0), 이 스크립트를 호출할 수 있습니다(이 스크립트를 호출합니다).cors.php에 '알겠습니다'가 포함되어 있는지 Access-Control-*머리글을 클릭합니다.

예:

cors?url=http://ip.jsontest.com
google.comcors.http?url=http://www.google.com
.1http://.0.1htp?url=http://10.0.1

돌아온다

OK *HTTP/1.1 200 OK: *
HTTP/1.1 302 」
하지 않은 입니다.


cors.php - 필요에 따라 맞춤

<?php /* cors.php */
$url = $_GET["url"];
if(isset($url)) {
    $headers = getHeaders($url);
    header("Access-Control-Allow-Origin: *");

    if(count($headers) == 0) {
        die("Invalid request"); // cURL returns no headers on bad urls
    } else {
        echo $headers[0];       // echo the HTTP status code
    }

    // Include any CORS headers
    foreach($headers as $header) {
        if(strpos($header, "Access-Control") !== false) {
            echo " " . $header;
        }
    }
}

function getHeaders($url, $needle = false) {
    $headers = array();
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4);        // Timeout in seconds
    curl_setopt($ch, CURLOPT_TIMEOUT, 4);               // Timeout in seconds
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');    // HEAD request only
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use(&$headers) {
        array_push($headers, $header);
        return strlen($header);
    });
    curl_exec($ch);
    return $headers;
} /* Drakes, 2015 */

클라이언트 측 테스트 하니스:

function testCORS(url, $elem) {
    $.ajax({
      url: url,
      timeout: 4000
    })
    .fail(function(jqXHR, textStatus) {
       if(jqXHR.status === 0) {
           // Determine if this was a CORS violation or not
           $.ajax({
              context: url,
              url: "http://myserver.com/cors.php?url=" + escape(this.url),
           })
           .done(function(msg) {
              if(msg.indexOf("HTTP") < 0) {
                $elem.text(url + " - doesn't exist or timed out");
              } else if(msg.indexOf("Access-Control-Allow-Origin") >= 0) {
                $elem.text(url + " - CORS violation because '" + msg + "'");
              } else {
                $elem.text(url + " - no Access-Control-Allow-Origin header set");
              }
           });
       } else {
           // Some other failure (e.g. 404), but not CORS-related
           $elem.text(url + " - failed because '" + responseText + "'");
       }
    })
    .done(function(msg) {
      // Successful ajax request
      $elem.text(this.url + " - OK");
    }); /* Drakes, 2015 */
}

하니스 드라이버:

// Create a div and append the results of the URL calls
$div = $("<div>"); 
$("body").append($div);

var urls = ["http://ip.jsontest.com", "http://google.com", "http://10.0.0.1"];
urls.map( function(url) {
   testCORS(url, $div.append("<h4>").children().last());
});

결과:

http://ip.jsontest.com - OK

http://google.com - Access-Control-Allow-Origin 헤더 세트 없음

http://10.0.0.1 - 존재하지 않거나 시간 초과되었습니다.

의외로 이미지에 대해서도 이 작업을 수행할 수 있습니다(다른 특정 콘텐츠 유형에도 유사한 솔루션이 있을 수 있습니다).문제는 가 CORS를 고려하지 않는다는 것입니다.따라서 "url failed to load by XHR and url successs to load by img src"라는 문장은 URL이 기능하지만 CORS가 차단되어 있음을 나타냅니다.

이것은 확실히 모든 경우에 도움이 되는 것은 아닙니다만, 경우에 따라서는(예를 들면, CORS를 적절히 통과하고 있는 경우의 유틸리티 검출) 효과가 있는 경우가 있습니다.예를 들어 백엔드의 URL이 올바르게 설정되어 있고 서버에 잘못 설정되어 있는 CORS가 어플리케이션(프런트엔드와 백엔드의 별도)에 설치되어 있는 경우 콘솔에 경고를 표시하고 싶었기 때문에 테스트하는 이미지를 제공할 수 있기 때문에 이 정도면 충분했습니다.

function testCors(url) {
    var myRequest = new XMLHttpRequest();
    myRequest.open('GET', url, true);
    myRequest.onreadystatechange = () => {
        if (myRequest.readyState !== 4) {
            return;
        }
        if (myRequest.status === 200) {
            console.log(url, 'ok');
        } else {
            var myImage = document.createElement('img');
            myImage.onerror = (...args) => {console.log(url, 'other type of error', args);}
            myImage.onload = () => {console.log(url, 'image exists but cors blocked');}
            myImage.src = url;
            console.log(url, 'Image not found');
        }
    };
    myRequest.send();
}

리모트 서버의 변경(페이지 추가)이 필요한 경우는 있습니다만, 다음과 같습니다.

  1. iframe에서 사용할 probe.html 페이지를 작성했습니다(원격 발신기지에서는 CORS를 사용하여 문의하려고 합니다).
  2. 페이지에서 window.onmessage 핸들러를 등록합니다.
  3. 안전한 iframe을 사용하여 probe.html 페이지를 페이지에 로드합니다.
  4. iframe onload 이벤트의 마이페이지에서 window.postMessage()를 사용하여 iframe에 메시지를 보냅니다.
  5. probe.html 페이지는 (iframe 내의 같은 발신기지에서) URL을 프로브합니다.
  6. probe.html 페이지에서 xhr.status와 xhr.status가 전송됩니다.window.post Message()를 사용한 텍스트 응답 상세
  7. 상태가 오류인 경우 상태 및 상태를 기록합니다.로깅 서비스 텍스트

다만, 어느 쪽이든 파라미터를 건네는 경우(누구나 iframe을 삽입할 수 있고, 다른 파티가 페이지 또는 iframe에 post Message를 발생시킬 수 있음)에는, 시큐어하게 하는 것은 매우 어려운 일이므로 주의해 주세요.

또한 완벽하지 않습니다(일시적인 일회성이 아닌 오류만 감지합니다).

하지만 많은 작업이 필요하지만 오류 원인에 대한 자세한 정보를 얻을 수 있습니다.사용자의 브라우저에 직접 액세스할 수 없는 경우 매우 유용합니다.

PS: 이것은, (a) 통신 장해의 원인을 진단하는데 별로 도움이 되지 않는 리모트 서버와 대화할 필요가 있는 것, (b) PHP로부터의 컬을 사용하는 것은, 단지, 서버에 진지하게 PHP를 실시해 달라고 하는 것에 지나지 않는 것을 나타내는 PHP의 답변과는 전혀 다릅니다.

언급URL : https://stackoverflow.com/questions/19325314/how-to-detect-cross-origin-cors-error-vs-other-types-of-errors-for-xmlhttpreq

반응형