CORS là gì?Tạo truy vấn CORS bằng XMLHttpRequestCấu hình máy chủ hỗ trợ CORSHỗ trợ CORS của các framework

Cors là gì? Vai trò của Cors? Nó là một nhu cầu rất thông dụng với các developer web đó là truy truy vấn qua API. Tuy nhiên, việc truy vấn và xử lý dữ liệu từ API nhiều khi cũng rất khó khăn.

Bạn đang xem: What is cors?

Rất nhiều lập trình viên phải đối mặt với các vấn đề liên quan đến CORS. Vậy CORS là gì? Và lý do tại sao chúng ta cần CORS?

CORS là gì?

CORS là một cơ chế cho phép nhiều tài nguyên khác nhau (fonts, Javascript, v.v…) của một trang web có thể được truy vấn từ domain khác với domain của trang đó. CORS là viết tắt của từ Cross-origin resource sharing.

no ‘access-control-allow-origin’ header is present on the requested resource

Đây chính là lỗi CORS policy mà bất cứ lập trình viên nào cũng sẽ gặp phải. Khi call API tới server mà không có headerAccess-Control-Allow-Originhoặc giá trị của nó không hợp lệ thì sẽ phát sinh lỗi này và không lấy được dữ liệu từ API. Cách khắc phục lỗi trên là phải config enableCORSlên để phía client có thể gọi được dữ liệu. Các bạn có thể tham khảo cách để enable với các ngôn ngữ tại đâyEnable CORS on Server

Tại sao chúng ta cần CORS

CORS được sinh ra là vìsame-origin policy, là một chính sách liên quan đến bảo mật được cài đặt vào toàn bộ các trình duyệt hiện nay. Chính sách này ngăn chặn việc truy cập tài nguyên của các domain khác một cách vô tội vạ.

Ta có ví dụ một kịch bản như sau:

Bạn truy cập một trang web có mã độc. Trang web đó sử dụng Javascript để truy cập tin nhắn Facebook của bạn ở địa chỉhttps://facebook.com/messages.Nếu bạn đã đăng nhập Facebook từ trước rồi. Nếu không có same-origin policy, trang web độc hại kia có thể thoải mái lấy dữ liệu của bạn và bất cứ điều gì chúng muốn.

Same-origin policy chính là để ngăn chặn những kịch bản như trên để bảo vệ người dùng, giúp an toàn hơn khi lướt web. Bạn có thể thử trên web console và sẽ nhận được lỗi ngay:

$.get("https://facebook.com/messages")Access to XMLHttpRequest at "https://facebook.com/messages" fromorigin "xxx" has been blocked by CORS policy: No"Access-Control-Allow-Origin" header is present on the requestedresource.Truy cập URL trên từ bất kỳ domain nào ngoàifacebook.combạn cũng sẽ nhận được lỗi như vậy. Đó chính là nhờ same-origin policy.

Thế nhưng trong thế giới web, lập trình viên thường xuyên phải thực hiện truy vấn đến các domain khác, đặc biệt là khi làm việc với các API.

Đó là lúc chúng ta cần đếnCORS.CORSsử dụng các HTTP header để “thông báo” cho trình duyệt rằng, một ứng dụng web chạy ở origin này (thường là domain này) có thể truy cập được các tài nguyên ở origin khác (domain khác).

Một ứng dụng web sẽ thực hiện truy vấn HTTP cross-origin nếu nó yêu cầu đến các tài nguyên ở origin khác với origin nó đang chạy (khác giao thức, domain, port). Sự khác biệt về giao thức ở đây là khác biệt kiểu như HTTP với FTP chứ không phải HTTP và HTTPS (dù nhiều trình duyệt không cho phép trộn lẫn các tài nguyên truy cập bằng HTTP và HTTPS nhưng đó là vấn đề khác, không liên quan đến CORS).


*

Các trường hợp cần đến CORS rất phổ biến trong thực tế. Một ví dụ rất điển hình như sau: một ứng dụng web chạy ở domainfoo.comvà nó cần truy vấn đếnbar.comđể lấy một vài dữ liệu (thường được thực hiện bởi JavaScript bằng cách sử dụngXMLHttpRequest).

Các trình duyệt đều cài đặt same-origin policy và tuân thủ nó rất chặt chẽ. Cài đặt XMLHttpRequest và kể cảFetch APIcũng đều tuân thủ chính sách này. Do đó những truy vấn như ở trên sẽ không thu được kết quả gì, trừ khi máy chủ trả về response có các header CORS phù hợp.

Như vậy, bằng việc sử dụng CORS, chúng ta có thể thúc đẩy việc giao tiếp trong ứng dụng web dễ dàng hơn rất nhiều.

Các truy vấn dùng CORS

Các truy vấn sau bắt buộc phải sử dụng CORS, theo tiêu chuẩnquốc tế.

Các truy vấn bằng XMLHttpRequest hoặc Fetch API đến một domain khác.Web fonts truy vấn đến domain khác qua 
fontface của CSS, trong đó trang web chỉ có thể sử dụng font dạng True Type nếu được cho phép.

Làm thế nào để sử dụng CORS

Một hiểu lầm khá phổ biến, nhất là với các lập trình viên mới làm việc với API lại được làm việc với API của các hãng lớn, tài liệu đầy đủ, đó là cho rằng CORS là công việc của frontend. Nhưng thực ra CORS hoàn toàn là công việc của backend.

Về phía máy chủ, sau khi có được thông tin về nguồn gốc của truy vấn, nó có thể lựa chọn không phải hồi truy vấn đó, trả về lỗi hoặc trả về dữ liệu cần thiết. Trong trường hợp trả về dữ liệu, máy chủ cần thiết lập các HTTP header sao cho trình duyệt hiểu rằng truy vấn đó đã được chấp nhận.

Như vậy, chúng ta có thể thấy rằng, CORS giúp thúc đấy quá trình trao đổi dữ liệu giữa trình duyệt và máy chủ. CORS hoàn toàn không có liên quan gì đến việc trao đổi trực tiếp giữa ứng dụng web mà một máy chủ web khác, ví dụ backend của ứng dụng đó truy cập đến tài nguyên trên một origin khác, nó cũng không cần đến CORS.

Tạo truy vấn CORS bằng XMLHttpRequest

Trong phần này chúng ta sẽ tìm hiểu cách tạo ra các truy vấn CORS bằng JavaScript. CORS được hỗ trợ bởi hầu hết các trình duyệt hiện đại. Riêng với IE, nó chỉ hỗ trợ từ IE 8 trở lên mà thôi.

*

Tạo truy vấn

Các trình duyệt Chrome, Firefox, Safari đều sử dụng version mới của XMLHttpRequest do đó việc truy vấn CORS diễn ra hết sức thuận lợi. IE thì sử dụng XDomainRequest, nó hoạt động gần giống với XMLHttpRequest nhưng có nhiều hạn chế hơn.

Chúng ta có thể bắt đầu bằng cách tạo ra các object cần thiết. Dưới đây là một đoạn code như thế:

const createCORSRequest = (method, url) => { let xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { // Kiểm tra XMLHttpRequest object có thuộc tính// withCredentials hay không // Thuộc tính này chỉ có ở XMLHttpRequest2 xhr.open(method, url, true); } else if (typeof XDomainRequest != "undefined") { // Kiểm tra XDomainRequest // Đây là đối tượng chỉ có ở IE và// là cách để IE thực hiện truy vấn CORS xhr = new XDomainRequest(); xhr.open(method, url); } else { xhr = null; } return xhr;}const request = createCORSRequest("GET", "https://jsonplaceholder.typicode.com/posts/1");if (!request) { throw new Error("CORS is not supported");}Sau khi tạo được đối tượng XMLHttpRequest rồi thì chúng ta cần một số event handler, trong trường hợp này, chúng ta chỉ cần quan tâm 2 eventonloadvàonerrorlà đủ. Ngoài ra còn một số event khác nhưontimeout,onprogresskhông được sử dụng nhiều lắm.

request.onload = () => { const responseText = request.responseText; console.log(responseText);}request.onerror = () => { console.log("Error");}Thực ra các trình duyệt khác nhau lại có cách cài đặt rất khác nhau với eventonerror. Ví dụ, Firefox trả về status là 0 vàstatusTextluôn rỗng với mọi lỗi. Ngoài ra, các trình duyệt cũng thường không cho phép truy cập đến nội dung cụ thể của lỗi đã xảy ra, chúng ta chỉ biết rằng đã có lỗi mà thôi.

withCredentials

Mặc định, các truy vấn CORS không gửi hoặc thiết lập bất cứ cookie nào trên trình duyệt. Nếu muốn sử dụng cookie trong truy vấn đó, chúng ta phải đặt thuộc tínhwithCredentialscủa truy vấn bằngtrue:

xhr.withCredentials = true;Tuy nhiên, đó cũng mới chỉ là một nửa mà thôi. Nửa còn lại thuộc về phía máy chủ, đó là HTTP headerAccess-Control-Allow-Credentialsphải là true (chúng ta sẽ tìm hiểu ở phần sau).

Xem thêm: Địa Hình Karst Là Gì - Nghĩa Của Từ Karst Trong Tiếng Việt

Với giá trịwithCredentialsbằngtrue, cookie sẽ được tự động thêm vào cũng như thiết lập nếu có phản hồi từ máy chủ. Lưu ý rằng, cookie trong trường hợp này là third-party cookie và việc lưu trữ, truy cập cookie vẫn hoàn toàn thuân theo same-origin policy, do đó, chúng ta không thể truy cập cookie bằngdocument.cookieđược. Nó hoàn toàn được xử lý tự động bởi trình duyệt.

Gửi truy vấn

Sau khi mọi việc đã hoàn tất, việc cuối cùng chúng ta cần làm là gửi truy vấn đi nữa mà thôi:

request.send();Lúc này truy vấn sẽ được gửi đến máy chủ, và nếu máy chủ đó chấp nhận CORS thì nó sẽ trả về response tương ứng. Hoạt động của truy vấn lúc này hoàn toàn giống với truy vấn có chúng origin thông thường.

Tạo truy vấn CORS bằng jQuery

Hàm$.ajaxcủa jQuery có thể được sử dụng cho các truy vấn thông thường lẫn truy vấn CORS (cookie cũng được hỗ trợ mặc định). Do đó nếu sử dụng jQuery thì công việc của lập trình viên cũng khá dễ dàng. Tuy nhiên, cần lưu ý một số điều như sau:

Truy vấn CORS của jQuery không hỗ trợ object XDomainRequest của IE, chúng ta cần sử dụng thêm plugin để hỗ trợ việc này.Giá trị$.support.corssẽ được gán làtruenếu trình duyệt hỗ trợ CORS (với IE sẽ làfalse). Giá trị này có thể được sử dụng để kiểm tra xem CORS có được hỗ trợ hay không.

Dưới đây là một đoạn code sử dụng jQuery để tạo truy vấn CORS:

$.ajax({type: "GET",url: "https://jsonplaceholder.typicode.com/posts/1",success: data => {console.log(data);},error: () => {console.log("Error");}})

Tạo truy vấn CORS bằng Fetch API

Chúng ta cũng có thể sử dụng Fetch API để tạo truy vấn CORS. Tuy nhiên,fetchmới chỉ xuất hiện từES6nên nhiều trình duyệt vẫn chưa hỗ trợ nó (cụ thể là IE tất cả các phiên bản đều không hỗ trợ).

Lập trình với fetch rất đơn giản, thậm chí còn đơn giản hơn của với jQuery:

fetch("https://jsonplaceholder.typicode.com/posts/1").then(response => response.json()).then(console.log)

Cấu hình máy chủ hỗ trợ CORS

Đây là phần phức tạp nhất, cũng là phần quan trọng nhất đối với CORS. Như đã nói ở trên, thực ra việc hỗ trợ CORS hay không phụ thuộc hoàn toàn vào máy chủ chứ không phải client.

Có hai loại truy vấn CORS: loại truy vấn “đơn giản” và “không đơn giản”.

Một truy vấn đơn giản hoàn toàn không cần đến CORS preflight. Một truy vấn sẽ được gọi là đơn giản nếu nó thoả mãn những điều kiện sau:

Phương thức của truy vấn là một trong các loạiGET,HEAD,POST.Giá trị củaContent-Typephải là một trong số các loạiapplication/x-www-form-urlencoded,multipart/form-data,text/plain.Không có event handler nào với eventXMLHttpRequest.upload.Không sử dụng đối tượngReadableStreamtrong truy vấn.Các HTTP header sau phải khớp:AcceptAccept-LanguageContent-LanguageLast-Event-ID

Những truy vấn này được gọi là “đơn giản” bởi chúng có thể được coi là truy vấn thông thường từ trình duyệt mà không cần đến CORS, giống như submit một form HTML thông thường chẳng hạn.

Những truy vấn không phải “đơn giản” sẽ là truy vấn không đơn giản, và chúng cần CORS preflight. CORS preflight có nghĩa là trước khi truy vấn được gửi, nó cần phải gửi một truy vấn trước bằng phương thứcOPTIONS. Mục đích của truy vấn “preflight” này là nhằm kiểm tra xem truy vấn thực sự có an toàn để gửi và nhận hay không.

Đối với truy vấn đơn giản

Một truy vấn CORS đơn giản như đã nói ở trên, có thể có gói tin HTTP dạng như sau:

GET /cors HTTP/1.1Origin: https://api.topdevvn.comHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/...Với các phương thức khác, gói tin HTTP cũng tương tự như vậy. Lưu ý rằng, một truy vấn CORS hợp lệ luôn luôn có Origin ở trong header. Giá trị của header này hoàn toàn được thiết lập tự động bởi trình duyệt, và không ai có thể thay đổi nó được. Giá trị của header này sẽ bao gồm scheme (http), domain (api.bob.com) và cổng (trong trường hợp dùng cổng mặc định thì không cần, ví dụ http dùng cổng 80). Giá trị của header chính là biểu thị nguồn gốc của truy vấn.

Ví dụ, Firefox không có header này cho các truy vấn same origin nhưng Chrome và Safari vẫn thêm header nay khi truy vấn same origin nhưng sử dụng các phương thứcPOST,PUThoặcDELETE. Đây là một diểm cần lưu ý với cáclập trình viên backend, vì nếu không để ý có thể không thêm origin của chính app trong danh sách các domain được chấp nhận, điều đó khiến cho chính truy vấn same origin lại gặp lỗi.

Dưới đây là response của máy chủ phản hồi cho một truy vấn CORS hợp lệ:

Access-Control-Allow-Origin: https://api.topdevvn.comAccess-Control-Allow-Credentials: trueAccess-Control-Expose-Headers: FooBarTất cả các header liên quan đến CORS đều có phần đầu tiên làAcess-Control-. Ý nghĩa của từng header như sau:

Access-Control-Allow-Origin(bắt buộc): đây là header phải có trong mọi response cho một truy vấn CORS hợp lệ. Nếu không có header này, truy vấn sẽ bị lỗi, giá trị của nó có thể là giá trị của headerOriginđược gửi lên hoặc*biểu thị cho mọi origin.Access-Control-Allow-Credentials(tuỳ chọn): Mặc định, cookie sẽ không được sử dụng trong các truy vấn CORS. Header này sẽ biểu thị giá trị logic rằng có thể sử dụng cookie hay không. Giá trị duy nhất của header này làtrue. Nếu không muốn sử dụng cookie thì thông thường người ta sẽ bỏ header này trong response chứ không phải đặt giá trị nó làfalse. Lưu ý rằng, header này chỉ hoạt động nếu phía client cũng đặt giá trịwithCredentials=truenhư đã nói ở phần trước.Access-Control-Expose-Headers(tuỳ chọn): Một đối tượng XMLHttpRequest có một phương thứcgetResponseHeader, phương thức này sẽ trả về giá trị của một header cụ thể trong response. Với các truy vấn CORS, phương thức này chỉ có thể truy cập được một số header đơn giản mà thôi. Nếu muốn phương thức này có thể truy cập nhiều header hơn, chúng ta cần đến giá trị của header này. Giá trị của header này là một danh sách các header có thể truy cập được, ngăn cách bằng dấu phẩy.

Đối với truy vấn cần preflight

Không phải truy vấn nào cũng là đơn giản do việc trao đổi dữ liệu giữa trình duyệt và máy chủ diễn ra rất đa dạng. Các phương thứcPUThayDELETEcũng thường xuyên được sử dụng. Ngoài ra kiểu dữ liệuJSON(Content-Type:application/json) cũng là lựa chọn của nhiều lập trình viên. Trong những trường hợp như vậy, trước khi truy vấn chính được thực hiện thì một truy vấn gọi là preflight sẽ được gửi đi trước.

Ở phía frontend, các truy vấn đơn giản hay phức tạp đều trông sẽ giống nhau. Truy vấn preflight hoàn toàn được thực hiện ngầm và trong suốt với người dùng. Truy vấn preflight sẽ được gửi đi trước nhằm xác định xem truy vấn thực sự có thể thực hiện được hay không.

Sau khi có được phản hồi tích cực, trình duyệt sẽ gửi truy vấn thực sự. Kết quả của truy vấn preflight có thể được cache nên nó không cần phải thực hiện cho mọi truy vấn.

Dưới đây là một gói tin HTTP cho truy vấn preflight:

OPTIONS /cors HTTP/1.1Origin: https://api.topdevvn.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Custom-HeaderHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/...Tương tự như truy vấn đơn giản, truy vấn này cũng tự động được thêm headerOrigin. Truy vấn preflight sẽ được thực hiện bằng phương thứcOPTIONSvới một số header đặc thù:

Access-Control-Request-Method: Đây là phương thức HTTP dùng trong truy vấn thực sự. Giá trị của header luôn luôn phải có, ngay cả khi các phương thức đó cũng là phương thức của một truy vấn đơn giản.Access-Control-Request-Headers: Đây là danh sách (ngăn cách bằng dấu phẩy) các header được thêm vào truy vấn.

Truy vấn preflight là một cách để hỏi máy chủ rằng, liệu truy vấn thực sự có thể thực hiện được hay không. Mà máy chủ dựa vào hai header này để quyết định xem có chấp nhận truy vấn hay không. Nếu chấp nhận, máy chủ sẽ phản hồi như sau:

Access-Control-Allow-Origin: https://api.topdevvn.comAccess-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderContent-Type: text/html; charset=utf-8Trong đó, response có thể có những header như sau:

Access-Control-Allow-Origin(bắt buộc): Tương tự như trường hợp truy vấn CORS đơn giản.Access-Control-Allow-Methods(bắt buộc): Là một danh sách (ngăn cách bằng dấu phẩy) các phương thức HTTP được chấp nhận. Dù truy vấn preflight có hỏi về một phương thức cụ thể của truy vấn tiếp theo, giá trị của header này trong responses có thể bao gồm tất cả các phương thức được chấp nhận.Access-Control-Allow-Headers(bắt buộc nếu truy vấn có headerAccess-Control-Request-Headers): Là danh sách các header (ngăn cách bằng dấu phẩy) được hỗ trợ. Tương tự như header trước, giá trị của header này cũng có thể bao gồm tất cả các header được chấp nhận.Access-Control-Allow-Credentials(tuỳ chọn): Tương tự như trường hợp truy vấn CORS đơn giản.Access-Control-Max-Age(tuỳ chọn): Truy vấn preflight không nhất thiết phải được thực hiện cho mọi truy vấn, mà kết quả của nó có thể cache được. Giá trị của header này chính là số giây mà giá trị của truy vấn preflight có thể được cache.

Một khi truy vấn preflight có được phản hồi và được chấp nhận, trình duyệt sẽ thực hiện truy vấn thực sự. Truy vấn lúc này tương tự như truy vấn CORS đơn giản và quá trình xử lý cũng như phản hồi hoàn toàn tương tự như vậy.

Về phía client, nếu trong trường hợp không thực hiện được truy vấn, eventonerrorsẽ được gọi. Tuy nhiên, như đã nó ở trên, trình duyệt cũng không thể truy cập được nhiều thông tin về lỗi đó, chỉ đơn giản là biết có lỗi mà thôi.

Hỗ trợ CORS của các framework

Laravel CORS

Khi chúng ta code vài ứng dụng dưới local mà có connect tới Laravel backed, thì bạn sẽ nhận cái thông báo error CORS ngay. Vì vậy cần tạo mộtmiddlewaresau:

$ php artisan make:middleware CorsSau đó update header trongapp/Http/Middleware/Cors.php

header(‘Access-Control-Allow-Origin’, ‘*’) ->header(‘Access-Control-Allow-Methods’, ‘GET, POST, PUT, DELETE, OPTIONS’) ->header(‘Access-Control-Allow-Headers’, ‘X-Requested-With, Content-Type, X-Token-Auth, Authorization’); }}Sau đó, đăng ký middleware trongapp/Http/kernel.php

protected $routeMiddleware = < ‘auth’ => \App\Http\Middleware\Authenticate::class, ‘auth.basic’ => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, ‘guest’ => \App\Http\Middleware\RedirectIfAuthenticated::class, ‘cors’ => \App\Http\Middleware\Cors::class, // Nếu bạn muốn bật CORS bất kỳ route nào, chỉ cần add middleware này trong route registration. Ngoài ra bạn có thể dùng packgage ngoài tạibarryvdh/laravel-cors.

CORS Nodejs

Chúng ta sử dụng dòng code dưới đây để set một header trên response của bạn để bật CORS:

res.header("Access-Control-Allow-Origin", "*");Bật CORS cho toàn bộ resource trên server

app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next();});Chỉ định file nào đó

app.get("/file", function(req, res){ var file = __dirname + "/file.zip"; res.download(file); // Set disposition and send it.});Ví dụ đầy đủ:

var express = require("express");var app = express();app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next();});app.get("/", function (req, res) { var data = { "SmartPhone": < "iPhone", "Samsung" > }; res.json(data);});app.get("/file", function(req, res){ var file = __dirname + "/file.zip"; res.download(file); // Set disposition and send it.});

Ruby on Rails

Ruby on Rails cho phép chúng ta thiết lập cũng như thay đổi các header trong response khá dễ dàng, do đó, muốn chấp nhận truy vấn CORS, chúng ta có thể đơn giản là làm như sau:

skip_before_filter :verify_authenticity_tokenbefore_action :cors_preflight_checkafter_action :cors_set_access_control_headersdef cors_set_access_control_headers headers<"Access-Control-Allow-Origin"> = "*" headers<"Access-Control-Allow-Methods"> = "GET, POST" headers<"Access-Control-Max-Age"> = "1728000"enddef cors_preflight_check headers<"Access-Control-Allow-Origin"> = "*" headers<"Access-Control-Allow-Methods"> = "GET, POST" headers<"Access-Control-Allow-Headers"> = "X-Requested-With, X-Prototype-Version" headers<"Access-Control-Max-Age"> = "1728000"end

Django

Với Django chúng ta phải sử dụng thêm một package, đó làDjango CORS headers. Package này sẽ giúp chúng ta thiết lập các header cần thiết cho một truy vấn CORS, đồng thời cho chúng ta khả năng cấu hình URL nào cho phép CORS, URL nào thì không.

Với package này, chúng ta có thể cấu hình sao cho chỉ có API mới hỗ trợ CORS như sau:

CORS_ORIGIN_ALLOW_ALL = TrueCORS_URLS_REGEX = r"^/api/v1/.*$"Ngoài ra còn rất nhiều cấu hình khác nữa, cho phép chúng ta chỉ chấp nhận truy vấn CORS từ một vài origin nhất định chẳng hạn (CORS_ORIGIN_REGEX_WHITELIST). Nội dung chi tiết xin mới các bạn xem cụ thể tại README của package đó.

Flask

Tương tự như Django, với Flask, chúng ta cũng phải sử dụng thêm một package, đó làFlask-CORSmới có thể chấp nhận các truy vấn CORS được.

from flask import Flaskfrom flask.ext.cors import CORS, cross_originapp = Flask(__name__)app.config<"SECRET_KEY"> = "The quick brown fox jumps over the lazy dog"app.config<"CORS_HEADERS"> = "Content-Type"cors = CORS(app, resources={r"/foo/*": {"origins": "*"}})

Bài viết liên quan

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *