preface

I have come into contact with many HTTP requests before, but this is the first time for me to do options request. When I just came to the company, I conducted debugging and found that in the NetWork, each request would be sent an options request before it is sent, and the second one is a normal request. Let’s take a look at the official explanation of MDN.

MDN

The HTTP OPTIONS method is used to get the communication OPTIONS supported by the destination resource. The client can use the OPTIONS method for a specific URL or for the entire site (by setting the URL to “*”).

Function:

  1. Detect the request method supported by the server
  2. Preflight Request in CORS

A reason for

The same origin policy and cross-domain resource sharing (CORS) are the same origin policy for the browser

The same-origin policy

If the protocol, port, and host of two urls are the same, the URL is homologous. Take the example of http://music.javaswing.cn/home/index.html:

URL The results of why
http://music.javaswing.cn/static/other.html homologous
http://music.javaswng.cn/inner/start.html homologous
http://music.javaswing.cn:8000/other.html Different source Different ports
https://music.javaswing.cn/inner/start.html Different source Agreement is different
http://api.javaswing.cn/start.html Different source The host different

Role: The same origin policy exists primarily as an important security policy for limiting how documents and the scripts it loads can interact with another resource.

For example, if your local project at http://localhost:3000 visits a project at http://localhost:8000, it appears that it has been blocked by CORS policy

Access to XMLHttpRequest at 'http://localhost:3000/' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Copy the code

CORS

Cross-domain resource sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to allow Web applications running on one Origin (domain) to access specified resources from different source servers. When a resource requests a resource from a different domain, protocol, or port than the server on which the resource itself resides, the resource makes a cross-domain HTTP request.

Simply put: CORS is two different domains, protocols, or ports (that is, not in the same origin) that services can access each other.

The OPTIONS request

After the same-origin policy and CORS, OPTIONS requests. In CORS mechanism, if A domain name A wants to access the service of domain name B, under some special complex requests (simple requests do not make pre-requests), the browser must first make A preflight request using OPTIONS request to obtain whether B service allows cross-domain requests. After the service confirms, To make a real HTTP request. In the return of the precheck request, the server side can also inform the client whether it needs to carry identity credentials (including Cookies and HTTP authentication related data).

A simple request

  1. The HTTP method is one of the following:
  • GET
  • HEAD
  • POST
  1. HTTP headers do not exceed the following fields:
  • Accept
  • Accept-Language
  • Content-Language
  • Content-type (Additional restrictions need to be noted)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width
  1. The value of the content-type is limited to one of the following:
  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

The instance

Environment: Node 10.x KOa vue

Browser: Firefox

Local VUE environment: localhost:8080

Local KOA address: localhost:3000

  1. First, set the background of KOA to allow CORS. Here, I did not use middleware and handled it by myself.
// CORS is processed across domains
app.use(async (ctx, next) => {
    // Allow all domain name requests
    ctx.set('Access-Control-Allow-Origin'.The '*')
    
 // A method to allow HTTP requests  ctx.set('Access-Control-Allow-Methods'.'OPTIONS,DELETE,GET,PUT,POST')   // Indicates that the server supports all header fields  ctx.set('Access-Control-Allow-Headers'.'x-requested-with, accept, origin, content-type, token')   // Content-type Specifies the media Type in a specific request  ctx.set('Content-Type'.'application/json; charset=utf-8')   await next() }) Copy the code
  • Vue level is simpleGETrequest
export function getList () {
    return axios({
        url: `//localhost:3000`.        method: 'get'
    })
} Copy the code

The results are shown below:

image

It can be seen from the figure that the result is a normal request, and no OPTIONS request is issued for pre-detection.

  • Add a header.token field to vue:
export function getList () {
    return axios({
        url: `//localhost:3000`.        method: 'get'.        headers: {token: 'test'}
 }) } Copy the code

The GET request, with the token field set in the HEADE, is not a simple request. So the OPTIONS request is issued. However, if you set only the header of the CTX, you will find that the request will still report an error

Access to XMLHttpRequest at 'http://localhost:3000/' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Copy the code
image

Preflight Request doesn’t pass access control check. The options request did not respond correctly.

There are two solutions:

  1. tooptionsRequest to set up a normal response
router.options('/'.async (ctx, next) => {
    console.log('options');
    ctx.body = ' '
})
Copy the code
  1. use@koa-routerIn theallowedMethodsmethods
app.use(router.allowedMethods())
Copy the code

Look at allowedMethods from the source

Router.prototype.allowedMethods = function (options) {
  options = options || {};
  var implemented = this.methods;

  return function allowedMethods(ctx, next) {
 return next().then(function() {  var allowed = {};   if(! ctx.status || ctx.status ===404) {  ctx.matched.forEach(function (route) {  route.methods.forEach(function (method) {  allowed[method] = method;  });  });   var allowedArr = Object.keys(allowed);   if(! ~implemented.indexOf(ctx.method)) { // The server does not support this method  if (options.throw) {  var notImplementedThrowable;  if (typeof options.notImplemented === 'function') {  notImplementedThrowable = options.notImplemented();  } else {  notImplementedThrowable = new HttpError.NotImplemented();  }  throw notImplementedThrowable;  } else {  // Response 501 Not Implemented  ctx.status = 501;  ctx.set('Allow', allowedArr.join(', '));  }  } else if (allowedArr.length) {  if (ctx.method === 'OPTIONS') {  // Gets the set of methods supported by the server for this route path  ctx.status = 200;  ctx.body = ' ';  ctx.set('Allow', allowedArr.join(', '));  } else if(! allowed[ctx.method]) { if (options.throw) {  var notAllowedThrowable;  if (typeof options.methodNotAllowed === 'function') {  notAllowedThrowable = options.methodNotAllowed();  } else {  notAllowedThrowable = new HttpError.MethodNotAllowed();  }  throw notAllowedThrowable;  } else {  // Respond 405 Method Not Allowed  ctx.status = 405;  ctx.set('Allow', allowedArr.join(', '));  }  }  }  }  });  }; };  Copy the code

You can see that in this method normal return processing is performed when the request is OPTIONS.

After setting this method, make the request:

image
  1. usecurlCommand to request
$ curl -X OPTIONS http://localhost:3000/ -i
HTTP / 1.1 200 OKAccess-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS,DELETE,GET,PUT,POST
Access-Control-Allow-Headers: x-requested-with, accept, origin, content-type, to
ken Content-Type: application/json; charset=utf-8Content-Length: 0 Allow: HEAD, GET, POST, PUT Date: Sat, 01 Aug 2020 11:14:53 GMT Connection: keep-alive Copy the code

HTTP header field description table

CORS request-related fields, all beginning with Access-Control-

The field name grammar role
Access-Control-Allow-Origin Access – Control – Allow – Origin, or * Orgin specifies the URL to allow access to the resource, or any if it is set to *
Access-Control-Allow-Methods Access-Control-Allow-Methods: [, ]* Used to pre-detect the request response and tell the browser what the actual request supports
Access-Control-Allow-Headers Access-Control-Allow-Headers: [, ]* Used to pre-detect the request response and tell the browser what fields are allowed in the actual request
Access-Control-Max-Age Access-Control-Max-Age: Specifies how long browser preflight requests can be cached, in seconds.
Access-Control-Allow-Credentials Access-Control-Allow-Credentials: true Whether to allow the browser to read the response when the browser credentials are set to true. inXMLHttpRequestWith withCredentials set to true, and when this property is set, identity Cookies are brought. If access-Control-allow-Origin is set to *, all Settings are invalid.

How to optimize

You can set access-Control-max-age if you don’t want each CORS complex request to be issued twice. Allow the browser to cache, and no options requests will be sent for the duration of the cache. Optimize performance.

app.use(async (ctx, next) => {
    // Allow all domain name requests
    ctx.set('Access-Control-Allow-Origin'.The '*')

    // A method to allow HTTP requests
 ctx.set('Access-Control-Allow-Methods'.'OPTIONS,DELETE,GET,PUT,POST')   // Indicates that the server supports all header fields  ctx.set('Access-Control-Allow-Headers'.'x-requested-with, accept, origin, content-type, token')   // Set the request preflight cache time in seconds  ctx.set('Access-Control-Max-Age'.10)  }) Copy the code

Other problems

There is a problem in my testing: the options request is displayed in Firefox, but not in Chrome. I wonder why

conclusion

In the current development mode of front and back end separation, cross-domain problems are often encountered. OPTIONS is just a pre-detection request in CORS mechanism. And this request is controlled by the entire CORS mechanism, not by code on the front end. Main functions:

  1. Check the request method supported by the server
  2. Precheck requests in CORS

Curl command tool: reqbin.com/req/jecm0tq…

reference

  • MDN OPTIONS request
  • HTTP Access Control (CORS)
  • Description of cross-domain resource sharing CORS N/A Description of cross-domain resource sharing CORS
  • Play Koa — KoA-Router

This article is formatted using MDNICE