كيفية تمكين Cross-Origin Requests (CORS) في ASP.NET Core

-

كيفية تمكين Cross-Origin Requests (CORS)  في ASP.NET Core

في متصفحات الانترنت عادتا ما يتم منع الوصول الى صفحات الويب الموجدة في domain مختلف عن domain الحالي لأغراض الحماية، هذا النهج في الحماية يسمى same-origin policy. 

ومن الأمثلة على ذلك صفحات الدفع الإلكترونية حيث عادتا ما يتم استدعاء صفحات الدفع من بوابات الدفع وتكون في domain  مختلف . 


في الدرس الخاص باستدعاء Web API عن طريق JQuery عندما حاولنا استدعاء GetAllCourses لم نستطع بسبب هذه المشكلة وكان شكل الخطأ (الصورة)


من الصورة واضح ان الخطأ في CORS مشان هيك ما قدرنا نشغل GetAllCourses في هذه الصفحة. 

تمام 
لحسن الحظ ان في standard من W3C باسم Cross Origin Resource Sharing (CORS) ممكن من خلاله التخفيف من القيود لمشكلة same-origin policy وبالتالي ممكن الوصول الى صفحات الويب الموجودة في Domains مختلفة.
وبناء عليه مع استخدام هذا standard يمكن الوصول الى Web API الى كان اسمو GetAllCourses . 
طيب الان دور نفهم كيف بعمل CORS : 
كيف يعمل CORS؟
اول خطوة هي ان موقع الويب الي بنشتغل عليه نعمل فيو تمكين ل CORS ، وبالتالي طلبات HTTP في Header بتتعين بشكل تلقائي في cross-origin requests .وهذه HTTP header تسمى Access-Control-Allow-Origin . وبالتالي عندما تقوم صفحة الويب من أي مصدر خارجي بتقديم طلبات ل resource (مثل page, file, ... الخ) موجوده على server او Domain اخر، فإن هذا server  يستجيب بالقيمة ل Access-Control-Allow-Origin header.
وهذه القيمة في كثير من الأحيان بتكون * ، الامر الي  بيعني أن SERVER  سيشارك الموارد المطلوبة مع أي domain على الإنترنت. في بعض الأحيان هذه القيمة بتكون domain   محدد او مجموعة من domains ، الامر الي بيعني ان هذا server بيشارك  Resource الخاصة فيه مع domain/s المحددة.

وبهذه الطريقة ، يعمل CORS على التخفيف من سياسة الأصل نفسه.
تمام الان دور نفهم ونعرف كيف ممكن نفعل هذه CORS في التطبيقات.
في تطبيقات ASP.NET Core يمكن تمكين CORS بسهولة تامة بالخطوات التالية:

أولا : تثبيت حزمة Microsoft.AspNetCore.Cors من Nuget.
انقر يمين فوق المشروع واختار Manage Nuget Package 

وبعدها نبحث عن Microsoft.AspNetCore.Cors

واكيد بعدها Install 

ثانيا : تسجيل CORS في ملف program.cs.
تمام الان دور نسجل CORS لذا اضف الكود التالي في ملف program.cs 
 builder.Services.AddCors();
في Net6. الاعدادات مختلفة عن Net5. ، في Net6. الإعدادات في ملف واحد الى هو program.cs 

ثالثا: تمكين CORS باستخدام middleware  في ملف program.cs.
لتمكين ال CORS بنضف الكود التالي : 

app.UseCors(builder =>
{
    builder
    .AllowAnyOrigin()
    .AllowAnyMethod()
    .AllowAnyHeader();
});
في هذا الكود استخدمنا الامر AllowAnyOrigin() والي بيعني ان هذا التطبيق يقبل أي domain بيعمل طلب CORS.
طيب توضيح شو معني الطرق الثانية الموجودة:
1. AllowAnyMethod()- للسماح بجميع طرق HTTP.
2. AllowAnyHeader()- للسماح بجميع طلبات request headers.
3. AllowCredentials () - يجب أن يسمح server ب credentials.
تمام .
طيب اذا كان المطلوب السماح لعدد محدد من المواقع او موقع واحد مثلا: 
الأمور سهلة وبسيطة بنعمل تعديل بسيط على الكود وبكون: 
في حال السماح لموقع واحد فقط:

app.UseCors(builder =>
{
    builder
    .WithOrigins("https://yourdomain.com")
    .AllowAnyMethod()
    .AllowAnyHeader()
    .AllowCredentials();
});

السماح لمجموعة مواقع بكون شكل الكود: 

app.UseCors(builder =>
{
    builder
    .WithOrigins(new string[] { " https://yourdomain1.com ", " https://yourdomain2.com ", "https://yourdomain3.com" })
    .AllowAnyMethod()
    .AllowAnyHeader()
    .AllowCredentials();
});
وبهاذ الطريقة بتكون جاهز تستقبل الطلبات من المواقع المحددة.

يمكن أيضا تطبيق CORS على action او controller. ولعمل ذلك المطلوب نعرف Policy في ملف program.cs حيث يتم تحديد شو هو domain المسموح استخدامها: 
في ملف program.cs خلونا نعدل على الكود كالتالي : 
builder.Services.AddCors(options =>
{
    options.AddPolicy("CORSPolicy",
        builder => builder.WithOrigins("https://www.devkum.com"));
});


لاحظ ان عملنا policy خاصة اسمها CORSPolicy وحددنا domain واحد فيها. 
بعدها لازم نظيف الكود :
app.UseCors("CORSPolicy");
تمام الان الأمور جاهزة سمكن استخدام هذه policy في أي action او controller وطريقة الاستخدام بتكون : 
على مستوي controller 
  [EnableCors("CORSPolicy")]
   
    public class CourseController : ControllerBase
    {
     }
على مستوى action بتكون : 
[EnableCors("MyPolicy")]
        public async Task<ActionResult<IEnumerable<CoursesModel>>> GetCoursesModel()
{}
وممكن كمان الغاء هذه الخاصية على مستوى action او controller باستخدام الامر : 
[DisableCors]


وبناء عليه الكود الكامل ل ملف  program.csبكون : 


using Microsoft.EntityFrameworkCore;
using MyFirstAPIProject;
using MyFirstAPIProject.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddControllers(options => options.RespectBrowserAcceptHeader = true).AddXmlSerializerFormatters().AddXmlDataContractSerializerFormatters();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddSingleton< CoursesModel > ();
builder.Services.AddCors();
string connString = builder.Configuration.GetConnectionString("STDCS");

builder.Services.AddDbContext<STDDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("STDCS"));
});
var app = builder.Build();
//ILogger logger = app.Logger;
//IHostApplicationLifetime lifetime = app.Lifetime;
//IWebHostEnvironment env = app.Environment;
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseCors(builder =>
{
    builder
    .AllowAnyOrigin()
    .AllowAnyMethod()
    .AllowAnyHeader();
});
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();