المقدمة
الـ CORS هي اختصار => Cross origin resource sharing وتعني أن origin ما يريد التواصل مع الـ server، و أشهر طريقة لذلك هى باستخدام HTTP request.
ما هو الـ Origin ؟
الـ Domain Name الذي يريد التواصل مع الـ Server، و ملاحظة جانبية عن الفرق بين Host and Origin في الـ HTTP Request Message:
الـ Host: الـ domain name الخاص بالـ server الذي نريد التواصل معه بدون إضافة الـ protocol سواء كان http أو https
الـ Origin: الـ domain name الذي أرسل الـ request للserver، و يجب كتابة الـ protocol
مثال http request message للتوضيح:
Host: api.backend.com
Origin: https://frontend.com
معنى الـ error الذي قابلته وأنا أذاكر من كتاب redux in action مع تطوير الـ FE و الـ BE للتدريب عليهما معا:
قبل شرح معنى الـ error يجب علينا معرفة بعض المصطلحات المذكورة فيه.
ما هى الـ Preflight Request ؟
هى request يقوم الـ browser بإرسالها للـ server قبل إرسال الـ request الأصلية التي نريد إرسالها للـ server إذا كانت الـ request الخاصة بنا ليست simple request.
و ما هى الـ simple request؟
هى request يجب أن تتوافر فيها عدة معايير (موجودة بالتفصيل في مقال MDN عن CORS المذكور في المصادر)، و إذا الـ request لم يتوفر فيه معيار واحد فقط لم يندرج تحت مسمى الـ simple request.
بالنسبة لمثالنا فالمشكلة في: Content-Type: application/json
و هذا ما يجعل الـ request not simple ، نعود للerror، بما أننا في الـ backend قد تعمدنا إرسال 404 كـ status code، فهذه إشارة للـ browser برفض الـ preflight request وبالتالي الـ browser لم يرسل الـ request الأصلية.
مقتطف من كود الـ BE:
// public/index.php
require __DIR__ . '/../vendor/autoload.php';
header("Access-Control-Allow-Origin: http://localhost:5173");
header("Access-Control-Allow-Headers: *");
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header("Content-Type: application/json");
http_response_code(404);
echo json_encode(['errorFromTheBackEnd' => 'this is an intentional error!']);
public/index.php file content that was supposed to work (before taking the preflight request into consideration)
لكننا كنا نريد فقط إرسال 404 ك response code للـ FE لنرى كيف سيتعامل الـ FE معه.
إذا كان الأمر كذلك، فعلينا الرد على الـ preflight request بطريقة سليمة من الـ server و بعد ذلك نرسل ما نريد للـ FE.
كيف يمكننا الرد على الـ preflight request أولا حتى يتمكن الـ browser من إرسال الactual request ؟
الـ preflight request لها request method خاصة بها و هى الـ OPTIONS request method، فيمكننا كتابة condition للتأكد من إرسال الـ response الذي ينتظره الـ browser للتأكد من أن الـ server جاهز لاستقبال الـ request الخاصة بنا.
مثال على الـ condition:
require __DIR__ . '/../vendor/autoload.php';
header("Access-Control-Allow-Origin: http://localhost:5173");
header("Access-Control-Allow-Headers: *");
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header("Content-Type: application/json");
// This will deal with the preflight request
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
// return a successful response to the browser
}
http_response_code(404);
echo json_encode(['errorFromTheBackEnd' => 'this is an intentional error!']);
content of public/index.php with adding a condition to deal with preflight request
حسنا سنكتب الcondition، لكن كيف يجب علينا الرد على الbrowser ؟
- يجب أن يكون الstatus code في ال2xx format.
- يجب الresponse headers ألا تستخدم ال* لكن يجب علينا تحديد الaccess origin, methods and headers وإلا سيتم رفض الrequest من قِبَل الbrowser بسبب CORS (الCORS هى feature في الbrowser و ليس الserver)
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
// It is important that you exit the script so what is sent to the browser
// is the successful response only
exit();
}
لماذا لم أواجه هذه المشكلة في laravel من قبل؟
لأن laravel framework يتعامل تلقائيا مع الـ CORS، و يمكننا تعديل ملف config/cors.php إذا أردنا بعض التعديلات من أجل use cases أخرى.
وهذا مثال لكود laravel من ملف config/cors.php
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['GET, POST, PUT, DELETE, OPTIONS'],
'allowed_origins' => ['http://localhost:3000'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['Content-Type', 'X-Auth-Token', 'Authorization', 'Origin'],
];
في الختام
الaccepted answer في السؤال الموجود في المصادر من stack overflow بها شرح وافٍ للموضوع، و في المقال أجبنا عن بعض التساؤلات التي واجهتني أثناء التعامل مع هذه المشكلة.
المصادر


Computer networking top down approach, ch2 application layer (doesn’t explain cors but explains important concepts like internet protocols, DNS and IPs)
Discussion