ใช้ CORS ใน .NET Core 2.0

ปัญหาเวลาทำ frontend กับ backend แยกออกจากกันคือ ถ้าทั้ง 2 ฝั่งอยู่คนละ domain ไม่ว่าจะคนละ subdomain คนละ port หรือ คนละ scheme (เช่น http กับ https) ก็ตาม จะเจอปัญหาเรื่อง cross origin ซึ่งเกิดจากที่ตัวบราวเซอร์เองไม่อนุญาตให้ใช้ ajax เรียกเนื้อหาที่อยู่บน domain อื่น เรียกว่า same-origin policy เพื่อป้องกันการเข้าถึงข้อมูลที่สำคัญข้าม domain กัน

วิธีหนึ่งที่สามารถแก้ได้คือ เอา reverse proxy มาคั่นกลาง แล้วทำให้ backend มาใช้ domain เดียวกันกับ frontend แต่ถ้าไม่สามารถทำแบบนี้ได้ ก็มีอีกวิธีคือการกำหนดที่ backend ให้เปิดรับ request จาก origin ที่ต้องการ เรียกว่า Cross Origin Resource Sharing หรือ CORS

วิธีเปิดใช้ CORS ใน Web API บน .NET Core 2.0 ให้ไปที่ไฟล์ Startup.cs ที่ method ConfigureServices เพิ่ม services.AddCors() ลงไป

public void ConfigureServices (IServiceCollection services) {
    services.AddCors (options => {
        options.AddPolicy ("MyCORSPolicy", builder => builder
            .AllowAnyHeader ()
            .AllowAnyOrigin ()
            .AllowAnyMethod ());
        });
    services.AddMvc ();
}

ตรง MyCORSPolicy เราสามารถตั้งชื่อเองได้เลย จะตั้งเป็นอะไรก็ได้ มันทำหน้าที่เป็น Key ของ Policy ที่เรากำหนดขึ้นมา ส่วนตรง AllowAnyHeader, AllowAnyOrigin และ AllowAnyMethod เราสามารถกำหนดเองได้ว่าจะให้ Header, Origin หรือ Method ไหนบ้าง ด้วย method WithHeaders, WithOrigins และ WithMethods

เสร็จแล้ว ใน method Configure ให้เรียกใช้งาน CORS Policy ที่เราสร้างขึ้นด้วย app.UseCors() ก่อนที่จะเรียก app.UseMvc()

public void Configure (IApplicationBuilder app, IHostingEnvironment env) {
    if (env.IsDevelopment ()) {
        app.UseDeveloperExceptionPage ();
    }

    app.UseCors ("MyCORSPolicy");

    app.UseMvc ();
}

ที่เหลือก็เป็นการกำหนดว่าจะให้ที่ไหนบ้างใช้ CORS Policy ที่ตั้งไว้ เช่น อยากให้ทั้ง controller ใช้งาน CORS Policy ที่ตั้งขึ้นนี้ ก็ใส่ไว้ที่ด้านบน class เลย

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Chonla.Math;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using MyApp.Models;

namespace MyApp.Controllers
{
    [Route("api/[controller]")]
    [EnableCors("MyCORSPolicy")]
    public class AdderController : Controller
    {
        // POST api/values
        [HttpPost]
        public JsonResult Post([FromBody]AdderArg value)
        {
            int addResult = SumService.Sum(new int[]{value.Operand1, value.Operand2});
            var result = new {
                result = addResult
            };
            return Json(result);
        }
    }
}

หรือถ้าอยากได้ในระดับ endpoint ก็ให้ใส่ไว้ที่ด้านบน method ที่ต้องการ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Chonla.Math;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using MyApp.Models;

namespace MyApp.Controllers
{
    [Route("api/[controller]")]
    public class AdderController : Controller
    {
        // POST api/values
        [HttpPost]
        [EnableCors("MyCORSPolicy")]
        public JsonResult Post([FromBody]AdderArg value)
        {
            int addResult = SumService.Sum(new int[]{value.Operand1, value.Operand2});
            var result = new {
                result = addResult
            };
            return Json(result);
        }
    }
}

หรือถ้าประกาศใช้ทั้งหมดในทุก ๆ controller

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Configure<MvcOptions>(options =>
    {
        options.Filters.Add(new CorsAuthorizationFilterFactory("MyCORSPolicy"));
    });
}

ถ้าต้องการไม่ใช้เป็นบางอัน ก็ใช้ [DisableCors]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Chonla.Math;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using MyApp.Models;

namespace MyApp.Controllers
{
    [Route("api/[controller]")]
    [EnableCors("MyCORSPolicy")]
    public class AdderController : Controller
    {
        // POST api/values
        [HttpPost]
        public JsonResult Post([FromBody]AdderArg value)
        {
            int addResult = SumService.Sum(new int[]{value.Operand1, value.Operand2});
            var result = new {
                result = addResult
            };
            return Json(result);
        }

        // GET api/values
        [HttpGet]
        [DisableCors]
        public JsonResult Get()
        {
            return Json(int[] { 1, 2, 3});
        }

    }
}

[DisableCors] สามารถใช้กับในระดับ Controller ได้ด้วย ถ้าหากเราประกาศใช้ CORS ทั้ง global แล้ว และเราต้องการ disable เป็นบาง controller ก็ประกาศด้านบน controller ได้เลย

แค่นี้แหละ

รายละเอียดเพิ่มเติม ดูได้ที่ https://docs.microsoft.com/en-us/aspnet/core/security/cors

Leave a Reply