Report `Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.` error solution

I used promise, but an Uncaught (in promise) error was reported during use. This is the first time I encountered this error, so I recorded it here to facilitate solving the problem in the future.
Insert image description here

Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource.Errors usually occur when using the fetch API to initiate a network request and the exception is thrown when the resource cannot be successfully obtained. In order to solve this problem, you can try the following methods:

  1. Check whether the network connection is normal. If the network is unstable or there are other problems, the fetch API may not be able to successfully obtain resources, causing this exception.

  2. Check whether the request address is correct. If the request address is wrong or does not exist, the fetch API will also be unable to obtain the resource, thus causing this exception.

  3. Check for cross-domain issues. In some cases, browsers will prohibit cross-domain requests, so CORS (Cross-Origin Resource Sharing) needs to be set up on the server to allow cross-domain requests.

  4. Add error handling logic in the fetch API, such as using the catch() method to catch exceptions and perform appropriate error handling.


This error is usually caused by an inability to obtain the requested resource. You can try adding .catch() between the d3.json() method and its callback function for better exception handling. In addition, in order to avoid cross-domain request problems, it is recommended to place the map file in the same directory as the HTML file and use relative paths for reference.

Before changing

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Map Visualization</title>
    <style>
        #map {
    
    
            width: 800px;
            height: 600px;
            border: solid 1px #ccc;
        }
    </style>
</head>
<body>
    <div id="map"></div>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://d3js.org/topojson.v3.min.js"></script>
    <script>
	// 定义地图容器宽高
var width = 800;
var height = 600;

// 创建SVG元素
var svg = d3.select("#map")
            .append("svg")
            .attr("width", width)
            .attr("height", height);

// 创建投影函数
var projection = d3.geoMercator()
                   .center([105, 38])
                   .scale(750)
                   .translate([width/2, height/2]);

// 创建路径生成器
var path = d3.geoPath()
             .projection(projection);

// 加载中国地图数据
d3.json("china.json").then(function(json) {
    
    
    // 将TopoJSON转换为GeoJSON
    var features = topojson.feature(json, json.objects.china).features;
  
    // 绘制地图
    svg.selectAll("path")
       .data(features)
       .enter()
       .append("path")
       .attr("d", path)
       .style("fill", "#ccc")
       .style("stroke", "#fff")
       .style("stroke-width", 1);
  
    // 处理用户交互
    svg.selectAll("path")
       .on("mouseover", function(d) {
    
    
           d3.select(this).style("opacity", "0.7");
       })
       .on("mouseout", function(d) {
    
    
           d3.select(this).style("opacity", "1");
       })
       .on("click", function(d) {
    
    
           var region = d.properties.name; // 获取区域名称
           var color = prompt("请输入" + region + "的颜色:"); // 弹出输入框获取用户输入的颜色
           d3.select(this).style("fill", color); // 修改区域颜色
       });
});

	</script>
</body>
</html>

After the change

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Map Visualization</title>
    <style>
        #map {
    
    
            width: 800px;
            height: 600px;
            border: solid 1px #ccc;
        }
    </style>
</head>
<body>
    <div id="map"></div>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://d3js.org/topojson.v3.min.js"></script>
    <script>
	// 定义地图容器宽高
    var width = 800;
    var height = 600;

    // 创建SVG元素
    var svg = d3.select("#map")
                .append("svg")
                .attr("width", width)
                .attr("height", height);

    // 创建投影函数
    var projection = d3.geoMercator()
                       .center([105, 38])
                       .scale(750)
                       .translate([width/2, height/2]);

    // 创建路径生成器
    var path = d3.geoPath()
                 .projection(projection);

    // 加载中国地图数据
    d3.json("china.json").then(function(json) {
    
    
        // 将TopoJSON转换为GeoJSON
        var features = topojson.feature(json, json.objects.china).features;

        // 绘制地图
        svg.selectAll("path")
           .data(features)
           .enter()
           .append("path")
           .attr("d", path)
           .style("fill", "#ccc")
           .style("stroke", "#fff")
           .style("stroke-width", 1)
           .on("mouseover", function(d) {
    
    
               d3.select(this).style("opacity", "0.7");
           })
           .on("mouseout", function(d) {
    
    
               d3.select(this).style("opacity", "1");
           })
           .on("click", function(d) {
    
    
               var region = d.properties.name; // 获取区域名称
               var color = prompt("请输入" + region + "的颜色:"); // 弹出输入框获取用户输入的颜色
               d3.select(this).style("fill", color); // 修改区域颜色
           });
    }).catch(function(error){
    
    
        console.log("数据加载失败:" + error);
    });
	</script>
</body>
</html>


The main problem is that a catch was added to print error, but then an error was still reported: data loading failed:TypeError: NetworkError when attempting to fetch resource.

catch(function(error){
    
    
        console.log("数据加载失败:" + error);
    });

emm, so I need to think about it again.


Let’s first understand what cors is?

Cross-origin resource sharing (CORS) (or colloquially translated as cross-origin resource sharing) is a mechanism that uses additional HTTP headers to tell the browser to allow a web application running on one origin to access a different origin. Selected resources. When a web application initiates an HTTP request that is different from its own origin (domain, protocol, and port), it initiates a cross-origin HTTP request.

An example of a cross-origin HTTP request: JavaScript code running at http://domain-a.com uses XMLHttpRequest to initiate a request to https://domain-b.com/data.json.

For security reasons, browsers restrict cross-origin HTTP requests initiated within scripts. For example, XMLHttpRequest and Fetch API follow the same origin policy. This means that web applications using these APIs can only request HTTP resources from the same domain where the application is loaded unless the response contains the correct CORS response headers.
Insert image description here

The Cross-Origin Resource Sharing (CORS) mechanism allows web application servers to perform cross-origin access control so that cross-origin data transmission can be carried out securely. Modern browsers support the use of CORS within API containers (such as XMLHttpRequest or Fetch) to reduce the risks posed by cross-origin HTTP requests.


Functional Overview

The cross-origin resource sharing standard adds a new set of HTTP header fields that allow the server to declare which origin sites have permission to access which resources through the browser. In addition, the specification requires that for those HTTP request methods that may have side effects on server data (especially HTTP requests other than GET, or POST requests with certain MIME types), the browser must first use the OPTIONS method to initiate a preflight request ( preflight request) to learn whether the server allows the cross-origin request. After the server confirms permission, it initiates the actual HTTP request. In the return of the preflight request, the server can also notify the client whether it needs to carry identity credentials (including cookies and HTTP authentication related data).

A failed CORS request will generate an error, but for security reasons, it is impossible to know exactly what went wrong at the JavaScript code level. You can only check your browser's console to see exactly where the error occurred.

The following content will discuss related scenarios and analyze the HTTP header fields involved in this mechanism.

Several access control scenarios

Here, we use three scenarios to explain how the cross-origin resource sharing mechanism works. These examples all use the XMLHttpRequest object.

The JavaScript code snippets in this article are all available at http://arunranga.com/examples/access-control/. In addition, use a browser that supports cross-origin XMLHttpRequest to access the address to see the actual running results of the code.

For a discussion of server-side support for cross-origin resource sharing, see this article: Server-Side_Access_Control (CORS).
simple request

Some requests will not trigger CORS preflight requests. This article calls such a request a "simple request"; note that this term does not belong to the Fetch (where CORS is defined) specification. A request is considered a "simple request" if it meets all of the following conditions:

Use one of the following methods:

  1. GET
  2. HEAD
  3. POST

In addition to the header fields automatically set by the user agent (such as Connection, User-Agent) and other headers defined as disabled header names in the Fetch specification, the fields that are allowed to be set manually are the set of CORS-safe header fields defined by the Fetch specification.

The set is:
Accept
Accept-Language
Content-Language
Content-Type (additional restrictions need to be noted)
DPR
Downlink
Save-Data
Viewport-Width The value of
Width
Content-Type is limited to one of the following three:

  1. text/plain
  2. multipart/form-data
  3. application/x-www-form-urlencoded

Note: These cross-site requests are no different from other cross-site requests made by the browser. If the server does not return the correct response headers, the requester will not receive any data. Therefore, sites that do not allow cross-site requests need not worry about this new HTTP access control feature.

Suppose the web application of the site http://foo.example wants to access the resources of http://bar.other. The web page at http://foo.example may contain JavaScript code similar to the following:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
    
function callOtherDomain() {
    
    
  if(invocation) {
    
        
    invocation.open('GET', url, true);
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

CORS header fields are used between client and server to handle permissions:
Insert image description here

View the request message and response message separately:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[XML Data]

Lines 1~10 are the request headers. The request header field Origin in line 10 indicates that the request comes from http://foo.example.

Lines 13~22 are server responses from http://bar.other. The response carries the response header field Access-Control-Allow-Origin (line 16). The simplest access control can be accomplished using Origin and Access-Control-Allow-Origin. In this example, the Access-Control-Allow-Origin: * returned by the server indicates that the resource can be accessed by any external domain. If the server only allows access from http://foo.example, the content of this header field is as follows:

Access-Control-Allow-Origin: http://foo.example

Now, no other external domain can access this resource except http://foo.example (this policy is defined by the ORIGIN field in the request header, see line 10). Access-Control-Allow-Origin should be * or contain the domain name specified by the Origin header field.

Preflight request

Different from the aforementioned simple requests, "requests requiring preflight" require that you must first use the OPTIONS method to initiate a preflight request to the server to know whether the server allows the actual request. The use of "preflight request" can avoid cross-domain requests from having unexpected effects on user data on the server.

The following is an HTTP request that needs to perform a preflight request:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';

function callOtherDomain(){
    
    
  if(invocation)
    {
    
    
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body);
    }
}

The above code uses a POST request to send an XML document, which contains a custom request header field (X-PINGOTHER: pingpong). In addition, the Content-Type of the request is application/xml. Therefore, the request needs to first initiate a "preflight request".

Insert image description here

0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
预检请求完成之后,发送实际请求:

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8                    
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

The browser has detected that requests made from JavaScript need to be preflighted. From the above message, we see that lines 1 to 12 send a "preflight request" using the OPTIONS method. OPTIONS is a method defined in the HTTP/1.1 protocol to obtain more information from the server. This method has no impact on server resources. The preflight request also carries the following two header fields:

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

The header field Access-Control-Request-Method tells the server that the actual request will use the POST method. The header field Access-Control-Request-Headers tells the server that the actual request will carry two custom request header fields: X-PINGOTHER and Content-Type. The server decides based on this whether the actual request is allowed.

Lines 14 to 26 are responses to preflight requests, indicating that the server will accept subsequent actual requests. Focus on lines 17~20:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

The header field Access-Control-Allow-Methods indicates that the server allows the client to initiate requests using the POST, GET and OPTIONS methods. This field is similar to the HTTP/1.1 Allow: response header, but is limited to use in scenarios that require access control.

The header field Access-Control-Allow-Headers indicates that the server allows the fields X-PINGOTHER and Content-Type to be carried in the request. Like Access-Control-Allow-Methods, the value of Access-Control-Allow-Headers is a comma-separated list.

Finally, the header field Access-Control-Max-Age indicates that the response is valid for 86400 seconds, which is 24 hours. Within the validity period, the browser does not need to initiate a preflight request again for the same request. Please note that the browser itself maintains a maximum validity time. If the value of this header field exceeds the maximum validity time, it will not take effect.
Preflight requests and redirects

Most browsers do not support redirects for preflight requests. If a redirection occurs for a preflight request, the browser will report an error:

The request was redirected to ‘https://example.com/foo’, which is
disallowed for cross-origin requests that require preflight

Request requires preflight, which is disallowed to follow
cross-origin redirect

CORS originally required this behavior, but this requirement was deprecated in subsequent revisions.

Before browser implementations catch up with the specifications, there are two ways to avoid the above error reporting behavior:

  1. Remove the redirection of preflight requests on the server side;
  2. Turn the actual request into a simple request.

If the above two methods are difficult to achieve, we still have other methods:

  1. Make a simple request (using Response.url or XHR.responseURL) to determine what address a real preflight request will return.
  2. Make another request (a real request), using the URL obtained in the previous step via Response.url or XMLHttpRequest.responseURL.

However, this method will not work if the request caused a preflight request due to the presence of the Authorization field. This situation can only be changed by the server.

Request with credentials

An interesting feature of XMLHttpRequest or Fetch with CORS is that credentials can be sent based on HTTP cookies and HTTP authentication information. Generally speaking, browsers do not send credential information for cross-origin XMLHttpRequest or Fetch requests. If you want to send credential information, you need to set a special flag of XMLHttpRequest.

In this example, a script at http://foo.example initiates a GET request to http://bar.other and sets Cookies:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';

function callOtherDomain(){
    
    
  if(invocation) {
    
    
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}

Line 7 sets the XMLHttpRequest's withCredentials flag to true, thereby sending Cookies to the server. Because this is a simple GET request, the browser does not initiate a "preflight request" for it. However, if the server-side response does not carry Access-Control-Allow-Credentials: true, the browser will not return the response content to the sender of the request.

Insert image description here

An example of client-server interaction is as follows:

GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain


[text/plain payload]

Even though Cookie related information is specified in line 10, if Access-Control-Allow-Credentials: true (line 17) is missing from the response of bar.other, the response content will not be returned to the originator of the request.
Requests with Credentials and Wildcards

Access-Control-Allow-OriginThe server MUST NOT set the value of "*" for requests accompanied by credentials .

This is because the request header carries Cookie information. If Access-Control-Allow-Originthe value is "*", the request will fail. And if Access-Control-Allow-Originthe value of is set to http://foo.example, the request will be executed successfully.

In addition, the response header also carries the Set-Cookie field, and attempts to modify the Cookie are attempted. If the operation fails, an exception will be thrown.

Third-party cookies

Note that the general third-party cookie policy applies to cookies set in CORS responses. In the above example, the page is foo.exampleloading, but the cookie on line 20 is being bar.othersent. If the user has set their browser to reject all third-party cookies, it will not be saved.

HTTP response header fields

This section lists the response header fields defined by the specification. In the previous section, we have seen how these header fields work in practical scenarios.

Access-Control-Allow-Origin

The response header can carry an Access-Control-Allow-Origin field, whose syntax is as follows:

Access-Control-Allow-Origin: <origin> | *

Among them, the value of the origin parameter specifies the external domain URI that is allowed to access the resource. For requests that do not need to carry identity credentials, the server can specify the value of this field as a wildcard, indicating that requests from all domains are allowed.

For example, the following field value will allow requests from http://mozilla.com:

Access-Control-Allow-Origin: http://mozilla.com

If the server specifies a specific domain name other than "*", then the value of the Vary field in the response header must contain Origin. This will tell the client that the server returns different content for different origin sites.

Access-Control-Expose-Headers

Translator's note: During cross-origin access, the getResponseHeader() method of the XMLHttpRequest object can only get some of the most basic response headers, Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma. If you want To access other headers, the server needs to set this response header.

The Access-Control-Expose-Headers header allows the server to whitelist the headers that the browser is allowed to access, for example:

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

In this way, the browser can access the X-My-Custom-Header and X-Another-Custom-Header response headers through getResponseHeader.

Access-Control-Max-Age

The Access-Control-Max-Age header specifies how long the results of the preflight request can be cached. Please refer to the preflight example mentioned earlier in this article.

Access-Control-Max-Age: <delta-seconds>

The delta-seconds parameter indicates how many seconds the result of the preflight request is valid.

Access-Control-Allow-Credentials

The Access-Control-Allow-Credentials header specifies whether the browser is allowed to read the response content when the browser's credentials are set to true. When used in the response to a preflight request, it specifies whether the actual request can use credentials. Please note: Simple GET requests are not preflighted; if the response to such a request does not contain this field, the response will be ignored and the browser will not return the corresponding content to the page.

Access-Control-Allow-Credentials: true

Requests accompanied by credentials have been discussed above.

Access-Control-Allow-Methods

The Access-Control-Allow-Methods header field is used in responses to preflight requests. It specifies the HTTP methods allowed for the actual request.

Access-Control-Allow-Methods: <method>[, <method>]*

See here for relevant examples.

Access-Control-Allow-Headers

The Access-Control-Allow-Headers header field is used in responses to preflight requests. It specifies the header fields allowed to be carried in the actual request.

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

Pre-checking process

When the preflight request reaches the server, the server will not actually execute the logic of the request. It will only return some HTTP Headers on the request to tell the client whether to send a real request.

If the server tells the client that the request is allowed to be sent, then the real request will be sent.

For example: I sent a request for the domain name conardli.top under the origin a.com.

Then the browser will first send a preflight to conardli.top. The preflight request will not actually execute the request for this domain name, but will return some CORS Headers, such as Access-Control-Allow-Origin: a.com

At this time, the browser discovers that the request for conardli.top is allowed to be sent under a.com, and then the request is actually issued. At this time, the server will actually execute the logic of the request interface.

So, will all requests be preflighted? of course not.
Simple requests and complex requests

Although the preflight request will not actually execute logic on the server, it is still a request. Considering the overhead of the server, not all requests will send preflight.

Once the browser determines that the request is a simple request, the browser will not send the preflight.

To determine whether a request is a simple request, the browser must meet the following four conditions:

Use one of the following methods:
GET
HEAD
POST

Only the following safe headers are used, other headers are not allowed to be artificially set
text/plain
multipart/form-data
application/x-www-form-urlencoded
Accept
Accept-Language
Content-Language

The value of Content-Type is limited to one of the following three:

  1. No event listeners are registered for any XMLHttpRequest objects in the request; the XMLHttpRequest objects can be accessed using the XMLHttpRequest.upload property.
  2. No ReadableStream object was used in the request.

Therefore, if you send a simple request, regardless of whether it is subject to cross-domain restrictions, as long as it is sent, it will be executed on the server side. The browser just hides the return value.

Summarize

Finally, let’s summarize the key points:

  1. Simple request: No matter whether it is cross-domain or not, as long as it is sent, it will definitely reach the server and be executed. The browser will only hide the return value.
  2. Complex requests: preflight first. The preflight will not actually execute the business logic. The real request will be sent and executed on the server only after the preflight passes.

Guess you like

Origin blog.csdn.net/weixin_43233219/article/details/130081051