Xử lý CORS trong Express

Cách cho phép các yêu cầu trên nhiều trang web bằng cách thiết lập CORS

Một ứng dụng JavaScript chạy trong trình duyệt thường chỉ có thể truy cập các tài nguyên HTTP từ cùng một miền (nguồn gốc) phân phát chúng.

Tải hình ảnh hoặc tập lệnh / kiểu từ cùng một nguồn gốc luôn hoạt động. Ngoài ra, tải Phông chữ Web bằng@font-facecó chính sách 'cùng nguồn gốc' được đặt theo mặc định. Tương tự như vậy với những thứ khác, ít phổ biến hơn (như kết cấu WebGL vàdrawImagetài nguyên được tải trong Canvas API).

Tuy nhiên, các cuộc gọi XHR và Tìm nạp đến máy chủ bên ngoài, bên thứ 3 sẽ không thành công. Đó là trừ khi máy chủ của bên thứ 3 triển khai cơ chế cho phép thực hiện kết nối và tải xuống và sử dụng tài nguyên được yêu cầu.

Cơ chế này được gọi làCORS,Chia sẻ tài nguyên đa nguồn gốc.

Một điều rất quan trọng cần CORS làMô-đun ES, được giới thiệu gần đây trong các trình duyệt hiện đại.

Nếu bạn không thiết lập chính sách CORStrên máy chủcho phép nó phân phát nguồn gốc của bên thứ ba, yêu cầu sẽ không thành công.

Tìm nạp ví dụ:

Fetch failed because of CORS policy

Ví dụ XHR:

XHR request failed because of CORS policy

Tài nguyên Cross-Origin không thành công nếu đó là:

  • khác nhaumiền
  • khác nhautên miền phụ
  • khác nhauHải cảng
  • khác nhaugiao thức

CORS ở đó để bảo mật cho bạn, để ngăn người dùng độc hại khai thác bất kỳ Nền tảng web nào bạn đang sử dụng.

Nếu bạn kiểm soát cả hai máy chủkhách hàng, bạn biết rằng cả hai bên đều đáng tin cậy và do đó có lý do chính đáng để cho phép chia sẻ tài nguyên.

Làm sao?

Nó phụ thuộc vào ngăn xếp phía máy chủ của bạn.

Hỗ trợ trình duyệt

Khá tốt (về cơ bản là tất cả ngoại trừ IE <10):

CORS browser support

Ví dụ với Express

Nếu bạn đang sử dụng Node.js và Express làm khuôn khổ, hãy sử dụngGói phần mềm trung gian CORS.

Đây là cách triển khai đơn giản của máy chủ Express Node.js:

const express = require('express')
const app = express()

app.get(’/without-cors’, (req, res, next) => { res.json({ msg: ‘😞 no CORS, no party!’ }) })

const server = app.listen(3000, () => { console.log(‘Listening on port %s’, server.address().port) })

Nếu bạn đánh/without-corsvới một yêu cầu tìm nạp từ một nguồn gốc khác, nó sẽ làm nảy sinh vấn đề CORS.

Tất cả những gì bạn cần làm để mọi thứ diễn ra suôn sẻ, là yêu cầucorsgói được liên kết ở trên và chuyển nó vào dưới dạng một chức năng phần mềm trung gian cho một trình xử lý yêu cầu điểm cuối:

const express = require('express')
const cors = require('cors')
const app = express()

app.get(’/with-cors’, cors(), (req, res, next) => { res.json({ msg: ‘WHOAH with CORS it works! 🔝 🎉’ }) })

/* the rest of the app */

Tôi đã tạo một ví dụ Glitch đơn giản, đây là mã của nó:https://glitch.com/edit/#!/flavio-cors-client.

Đây là máy chủ Node.js Express:https://glitch.com/edit/#!/flaviocopes-cors-example-express

Lưu ý cách yêu cầu không thành công do máy chủ không xử lý chính xác các tiêu đề CORS, vẫn được nhận. Như bạn có thể thấy trong bảng điều khiển Mạng, nơi bạn có thể thấy thông báo mà máy chủ đã gửi:

No response from CORS

Chỉ cho phép các nguồn gốc cụ thể

Tuy nhiên, ví dụ này có một vấn đề: BẤT KỲ yêu cầu nào sẽ được máy chủ chấp nhận dưới dạng nguồn gốc chéo.

Như bạn có thể thấy trong bảng điều khiển Mạng, yêu cầu được chuyển có tiêu đề phản hồiaccess-control-allow-origin: *:

The CORS response header

Bạn cần phải định cấu hình máy chủ để chỉ cho phép một nguồn cung cấp và chặn tất cả các nguồn khác.

Sử dụng giống nhaucorsThư viện nút, đây là cách bạn sẽ làm điều đó:

const cors = require('cors')

const corsOptions = { origin: https://yourdomain.com, }

app.get(’/products/:id’, cors(corsOptions), (req, res, next) => { //… })

Bạn cũng có thể phục vụ nhiều hơn:

const whitelist = ['http://example1.com', 'http://example2.com']
const corsOptions = {
  origin: function (origin, callback) {
    if (whitelist.indexOf(origin) !== -1) {
      callback(null, true)
    } else {
      callback(new Error('Not allowed by CORS'))
    }
  },
}

Preflight

Có một số yêu cầu được xử lý theo cách "đơn giản". Tất cảGETyêu cầu thuộc nhóm này.

Cũng thếmột số POSTHEADyêu cầu cũng vậy.

POSTcác yêu cầu cũng nằm trong nhóm này, nếu chúng đáp ứng yêu cầu sử dụng Loại-Nội dung của

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Tất cả các yêu cầu khác phải chạy qua giai đoạn phê duyệt trước, được gọi là preflight. Trình duyệt thực hiện việc này để xác định xem nó có quyền thực hiện một hành động hay không, bằng cách đưa ra mộtOPTIONSyêu cầu.

Yêu cầu preflight chứa một số tiêu đề mà máy chủ sẽ sử dụng để kiểm tra quyền (các trường không liên quan bị bỏ qua):

OPTIONS /the/resource/you/request
Access-Control-Request-Method: POST
Access-Control-Request-Headers: origin, x-requested-with, accept
Origin: https://your-origin.com

The server will respond with something like this (irrelevant fields omitted):

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://your-origin.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE

We checked for POST, but the server tells us we can also issue other HTTP request types for that particular resource.

Following the Node.js Express example above, the server must also handle the OPTIONS request:

var express = require('express')
var cors = require('cors')
var app = express()

//allow OPTIONS on just one resource app.options(’/the/resource/you/request’, cors())

//allow OPTIONS on all resources app.options(’*’, cors())

Download my free Express.js Handbook


More express tutorials: