Editor’s note: In addition to chuangyu front-end and author’s blog, this article is also published in Yuq.

Editor also click: the author is also digging gold oh, welcome to follow: @godotdotdot

preface

This article mainly describes some basic knowledge of Fetch and how we use it in production development. In order to have a better understanding of Fetch, we hope that you have some knowledge of the following points, if you have relevant development experience, that will be the best.

  • XHR
  • Promise
  • HTTP

There are links to some keywords in this article that you can use to enrich yourself if you don’t know enough about them or want to learn more about them. Some knowledge points in this article have been written in detail on MDN Fetch, so it has been skipped. It is hoped that students can read this article in comparison.

In this article, the idea of the first from the standard, the purpose is to let everyone understand more thoroughly, to know what is what is what.

In order to better master Fetch, the article also provides some sample codes for you to learn and use. We want you to have some knowledge of Node.js before using the sample code, but if not, you can follow the friendly tips in the sample to complete your learning experience.

Here’s what you’ll know after reading this article:

  • What is the Fetch
  • Some basic concepts of Fetch
  • How to use Fetch
  • What’s wrong with Fetch and how can we use it “gracefully”

I hope you have a basic understanding of Fetch after reading this article.

The Fetch profile

Fetch is a new technique for fetching resources that is being used to replace the technique we have been teasing for a long time (XHR).

Fetch is simple to use, it returns a Promise, and even if you have no XHR development experience you can get started quickly. With that said, let’s take a quick look at the sample code below.

fetch('https://github.com/frontend9/fe9-library', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error
});
Copy the code

Couldn’t be simpler? Ok, now that we have a simple understanding of Fetch, let’s understand the basic concept of Fetch.

Basic concepts of Fetch

There are four basic concepts in Fetch: Headers, Request, Response, and Body. To better understand Fetch, we need to take a brief look at these concepts.

All four concepts are included in a complete HTTP request. There is a request header and a request body in a request, and a response header and a response body in a response. So it’s important to understand these concepts.

Headers

Being able to modify the header is a very important ability to achieve flexibility in the header. Headers is a member of HTTP Headers. It is an abstract interface that allows you to add, modify, and delete HTTP request and response Headers.

Let’s take a look at what interfaces it has:

typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString>) HeadersInit;

[Constructor(optional HeadersInit init),
 Exposed=(Window,Worker)]
interface Headers {
  void append(ByteString name, ByteString value);
  void delete(ByteString name);
  ByteString? get(ByteString name);
  boolean has(ByteString name);
  void set(ByteString name, ByteString value); iterable<ByteString, ByteString>; }; interface Headers {void append(ByteString name, ByteString value);
  void delete(ByteString name);
  ByteString? get(ByteString name);
  boolean has(ByteString name);
  void set(ByteString name, ByteString value);
  iterable<ByteString, ByteString>;
};
/ / https://fetch.spec.whatwg.org/#headers-class
Copy the code

Interfaces defined in the specification can be viewed with reference to MDN. You can click here to see more visually what methods it has available to you.

Here we have an explanation of the Headers construction parameter. First, the argument type is HeadersInit. Let’s take a look at what types of values this type supports. The definition we can see from the specification is:

typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString>) HeadersInit;
Copy the code

The object can be an array or a key-value pair (that is, an object). For how to initialize these parameters, we can look at the process defined in the specification.

To fill a Headers object (headers) with a given object (object), run these steps:

  1. If object is a sequence, then for each header in object:
    1. If header does not contain exactly two items, then throw a TypeError.
    2. Append header’s first item/ Header’s second item to headers.
  2. Otherwise, object is a record, then for each key → value in object, append key/value to headers.

I need to make a note of this here, and I’ll touch on the use of fetch a little bit later and polyfill will help.

  • The first is an array, which throws an error if each item of data does not contain two items. And then the first entry in the array is thetaheaderThe name, the second term is the value. , and finally directly throughappendMethod add.
  • The second type: the key-value pair (in this case, objects), we loop directly to the key-value pair, and then throughappendMethod add.

The sample

Example code address: github.com/GoDotDotDot…

Open a browser and enter: http://127.0.0.1:4000/headers

So how do we use it? First we need to instantiate a Headers object with new Headers(), which returns an empty list. Now that we have an object instance, we can use the interface to do what we want. Let’s take a look at the following example:

  function printHeaders(headers) {
    let str = ' ';
    for (let header of headers.entries()) {
      str += `
          <li>${header[0]}: ${header[1]}</li>
          `;
      console.log(header[0] + ':' + header[1]);
    }
    return `<ul>
          ${str}
          </ul>`;
  }
  const headers = new Headers();
  // Let's print to see if an empty list is returned
  const before = printHeaders(headers); // There is no output here
  document.getElementById('headers-before').innerHTML = before;
  // We add a request header
  headers.append('Content-Type'.'text/plain');
  headers.append('Content-Type'.'text/html');
  headers.set('Content-Type'['a'.'b']);
  const headers2 = new Headers({
    'Content-Type': 'text/plain'.'X-Token': 'abcdefg'});const after = printHeaders(headers); // Output: content-type:
Copy the code

If you feel the need to append every time, you can also pass the specified header in the constructor, for example:

const headers2 = new Headers({
    'Content-Type': 'text/plain'.'X-Token': 'abcdefg'
});

printHeaders(headers2);
/ / output:
// content-type: text/plain
// x-token: abcdefg
Copy the code

Here I added a custom header x-Token, which is common and practical in real development. However, keep in mind that you need to meet the relevant specifications in CORS, otherwise cross-domain errors will occur.

You can modify the request header using the append, DELETE, SET, GET, and HAS methods. Here’s a special note for the set and append methods:

Set: If an operation is performed on an existing header, the new value will be replaced by the old value, and the old value will not exist. If the header does not exist, add the new header.

Append: If the header already exists, the new value is appended and the old value is retained.

For easy memorization, just remember that set overwrites and AppEnd appends.

Guard

Guard is a feature of Headers. It is a Guard. It affects whether methods such as Append, set, and DELETE can change the header.

It can be immutable, request, request-no-cors, response, or None.

You don’t have to worry about it here, just to give you an idea that there’s something that’s affecting us to set some Headers. You can’t manipulate it. It’s a proxy thing. For a simple example, we cannot insert a set-cookie into Response Headers.

For more details, see Concept-headers Guard and MDN Guard for specifications

Pay attention to

  • We need to satisfy when we assign a value to the headerAn acceptable collection of header fieldsOtherwise it will be reportedTypeError

Body

The Body is exactly a mixin, which stands for the Body of the Request or the Body of the Response, which is implemented by Response and Request.

Let’s see what interfaces it has:

interface mixin Body {
  readonly attribute ReadableStream? body;
  readonly attribute boolean bodyUsed;
  [NewObject] Promise<ArrayBuffer> arrayBuffer();
  [NewObject] Promise<Blob> blob();
  [NewObject] Promise<FormData> formData();
  [NewObject] Promise<any> json();
  [NewObject] Promise<USVString> text();
};
/ / https://fetch.spec.whatwg.org/#body
Copy the code

The interface defined in the specification can be viewed with reference to MDN. You can click here to see more intuitively what properties and methods it has for us to use.

Note here that these methods all return promises, which is important to remember in fetch based interface requests. Keeping this in mind will help us understand the use of fetch in a later article.

The sample

The example will be shown in Response.

Request

Request represents a Request class that needs to be instantiated to generate a Request object. This object describes a request in an HTTP request (typically with a request header and a request body). Since it is used to describe a request object, the request object should have ways to modify either the Headers or the Body of the request. Let’s take a look at what interfaces Request has in the specification:

typedef (Request or USVString) RequestInfo;

[Constructor(RequestInfo input, optional RequestInit init),
 Exposed=(Window,Worker)]
interface Request {
  readonly attribute ByteString method;
  readonly attribute USVString url;
  [SameObject] readonly attribute Headers headers;

  readonly attribute RequestDestination destination;
  readonly attribute USVString referrer;
  readonly attribute ReferrerPolicy referrerPolicy;
  readonly attribute RequestMode mode;
  readonly attribute RequestCredentials credentials;
  readonly attribute RequestCache cache;
  readonly attribute RequestRedirect redirect;
  readonly attribute DOMString integrity;
  readonly attribute boolean keepalive;
  readonly attribute boolean isReloadNavigation;
  readonly attribute boolean isHistoryNavigation;
  readonly attribute AbortSignal signal;

  [NewObject] Request clone(a);
};
Request includes Body;

dictionary RequestInit {
  ByteString method;
  HeadersInit headers;
  BodyInit? body;
  USVString referrer;
  ReferrerPolicy referrerPolicy;
  RequestMode mode;
  RequestCredentials credentials;
  RequestCache cache;
  RequestRedirect redirect;
  DOMString integrity;
  boolean keepalive;
  AbortSignal? signal;
  any window; // can only be set to null
};

enum RequestDestination { ""."audio"."audioworklet"."document"."embed"."font"."image"."manifest"."object"."paintworklet"."report"."script"."sharedworker"."style"."track"."video"."worker"."xslt" };
enum RequestMode { "navigate"."same-origin"."no-cors"."cors" };
enum RequestCredentials { "omit"."same-origin"."include" };
enum RequestCache { "default"."no-store"."reload"."no-cache"."force-cache"."only-if-cached" };
enum RequestRedirect { "follow"."error"."manual" };
/ / https://fetch.spec.whatwg.org/#request-class
Copy the code

The interface defined in the specification can be viewed according to MDN. You can click here to see more intuitively what properties and methods it has for us to use. We will not explain one by one here.

Note that the properties here are read-only. In the specification, we see that the first argument to the constructor is a Request object or a string. We usually take a string, which is the address of the resource to access (the HTTP interface address). The second argument takes a RequestInit optional object, which is a dictionary. In javascript, we can think of it as an object ({}). In RequestInit we can configure the initial properties that tell the Request some configuration information about the Request.

Here we need to pay special attention to the following attributes.

Mode is a RequestMode enumeration with values such as navigate, same-Origin, no-cors, cors. This indicates whether a request uses CORS or strict homology mode. When in cross-domain situations, you should set it to CORS. The default value for this value is CORS when initialized using Request. When launching embedded resources with tags, such as ,

The credentials are a RequestCredentials enumeration type and may include omit, same-origin, and include values. It indicates whether the request sends cookies across domains. So this should be familiar to those of you who are familiar with XHR. This is similar to the withCredentials in XHR. However, credentials have three optional values, which default to same-origin. When you need to pass cookie credentials across domains, set it to include. Note that there is one detail here. When set to include, make sure that access-Control-allow-origin in the Response Header cannot be * and specify the source (for example: http://127.0.0.1:4001), otherwise you will see the following error message on the console. Refer to the WHATWG specification or MDN for details.

The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’.

You can use the article provides code to start the cors example code, then enter http://127.0.0.1:4001/request in the browser, if no accident, you can see the above error message in the console.

Body is a BodyInit type. The values it takes are Blob,BufferSource, FormData, URLSearchParams, ReadableStream, USVString. For those of you who are careful, the common JSON object is not included. Therefore, if we need to pass json, we need to call json.stringify to help us convert it to a string.

A sample code is shown below.

The sample

Example code address: github.com/GoDotDotDot…

Open a browser and enter: http://127.0.0.1:4000/request

  / / the client
  const headers = new Headers({
    'X-Token': 'fe9'});const request = new Request('/api/request', {
    method: 'GET',
    headers,
  });
  console.log(request); / / Request {method: "GET", url: "http://127.0.0.1:4000/api/request", headers, headers, destination: "", the referrer: About: "the client",... }
  console.log(request.method); // GET
  console.log(request.mode); // cors
  console.log(request.credentials); // same-origin
  // if you want to printHeaders, call printHeaders(request.headers).
Copy the code

Here we start with a simple GET request. We pass a custom Headers request that specifies method as GET (the default is GET). In the interface specification above, we can get some common attributes from the Request object, such as method, URL, headers, body, and so on.

Response

Response is similar to Request. It is a Response data returned by a Request. Let’s first look at what interfaces are defined in the specification.

[Constructor(optional BodyInit? body = null, optional ResponseInit init), Exposed=(Window,Worker)]
interface Response {
  [NewObject] static Response error();
  [NewObject] static Response redirect(USVString url, optional unsigned short status = 302);

  readonly attribute ResponseType type;

  readonly attribute USVString url;
  readonly attribute boolean redirected;
  readonly attribute unsigned short status;
  readonly attribute boolean ok;
  readonly attribute ByteString statusText;
  [SameObject] readonly attribute Headers headers;
  readonly attribute Promise<Headers> trailer;

  [NewObject] Response clone(a);
};
Response includes Body;

dictionary ResponseInit {
  unsigned short status = 200;
  ByteString statusText = "";
  HeadersInit headers;
};

enum ResponseType { "basic"."cors"."default"."error"."opaque"."opaqueredirect" };
/ / https://fetch.spec.whatwg.org/#response-class
Copy the code

The interface defined in the specification can be viewed according to MDN. You can click here to see more intuitively what properties and methods it has for us to use. We will not explain one by one here.

The status and headers attributes are the most commonly used. The result of server request processing can be determined by status code, such as 200, 403 and so on. For example, when status is 401, it is possible to block a jump to the login page at the front end, which is particularly common in SPAs (single-page applications) these days. Headers can also be used to retrieve information that the server sends back to the front end, such as tokens.

If you look closely at the interface above, you can find Response Includes Body; Signs like this. Earlier we said that the Body is implemented by Request and Response. So all of the methods that Body has can be used in the Response instance, and this is a very important part. We use the methods provided by Body (specifically implemented by Response here) to process the data returned by the server.

Here’s an example of a simple usage:

The sample

Example code location: github.com/GoDotDotDot…

  / / the client
  const headers = new Headers({
    'X-Token': 'fe9-token-from-frontend'});const request = new Request('/api/response', {
    method: 'GET',
    headers,
  });

  // Here we make a request to try
  fetch(request)
    .then(response= > {
      const { status, headers } = response;
      document.getElementById('status').innerHTML = `${status}`;
      document.getElementById('headers').innerHTML = headersToString(headers);

      return response.json();
    })
    .then(resData= > {
      const { status, data } = resData;
      if(! status) {window.alert('An error has occurred! ');
        return;
      }
      document.getElementById('fetch').innerHTML = data;
    });
Copy the code

We ignore the fetch usage here, which will be covered in more detail in a later section. Let’s focus on what’s inside the first then method callback. You can see that a Response object is returned, which is an instance of our Response. The example takes status and headers, which I’ll put in HTML for convenience. Looking at the last line of this callback, we call a response.json() method (where the data returned from the back end is a JSON object, to make it easier to call JSON () directly). This method returns a Promise, and we return the processing result to the last then callback, In this way, the data after the final processing can be obtained.

Open the browser, type http://127.0.0.1:4000/response, if your sample generation running normally, you will see the following page:

(View the data returned by Response)

Editor’s note: This article is to be continued.


The text/GoDotDotDot

Less is More.

Sound/fluorspar

Other articles by author:

What a good front end should know: We should do as much optimization as we can

This article is authorized by chuangyu front-end author, copyright belongs to the author, chuangyu front-end production. Welcome to indicate the source of this article. The article links: blog.godotdotdot.com/2018/12/28/…

To subscribe for more sharing from the front line of KnownsecFED development, please search our wechat official account KnownsecFED. Welcome to leave a comment to discuss, we will reply as far as possible.

Thank you for reading.

Happy New Year 🙂