๐Ÿšช API ็ฝ‘ๅ…ณ่ฎพ่ฎกๆŒ‡ๅ—

1. ไธบไป€ไนˆ้œ€่ฆ็ฝ‘ๅ…ณ๏ผŸ

1.1 ๆฒกๆœ‰็ฝ‘ๅ…ณ็š„้—ฎ้ข˜

ไผ ็ปŸๆžถๆž„๏ผˆๆ— ็ฝ‘ๅ…ณ๏ผ‰๏ผš

    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚  Client  โ”‚โ”€โ”€โ”€โ”€โ†’โ”‚  Service A  โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’โ”‚  Service B  โ”‚
         โ”‚           โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’โ”‚  Service C  โ”‚
                     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

้—ฎ้ข˜๏ผš
โ€ข ๅฎขๆˆท็ซฏ้œ€่ฆ็Ÿฅ้“ๆ‰€ๆœ‰ๆœๅŠกๅœฐๅ€
โ€ข ๆฏไธชๆœๅŠก้ƒฝ่ฆๅฎž็Žฐ่ฎค่ฏใ€้™ๆตใ€ๆ—ฅๅฟ—
โ€ข ่ทจๅŸŸใ€ๅ่ฎฎ่ฝฌๆข็ญ‰้‡ๅคๅค„็†
โ€ข ๆœๅŠกๅ˜ๆ›ดๅฎขๆˆท็ซฏ้œ€่ฆๅŒๆญฅไฟฎๆ”น
โ€ข ๅฎ‰ๅ…จ่พน็•Œๆจก็ณŠ๏ผŒๆ”ปๅ‡ป้ขๅคง

1.2 ็ฝ‘ๅ…ณ่งฃๅ†ณ็š„ๆ ธๅฟƒ้—ฎ้ข˜

้—ฎ้ข˜ ็ฝ‘ๅ…ณ่งฃๅ†ณๆ–นๆกˆ
็ปŸไธ€ๅ…ฅๅฃ ๅ•ไธ€่ฎฟ้—ฎ็‚น๏ผŒๅฑ่”ฝๅŽ็ซฏๅคๆ‚ๆ€ง
ๅฎ‰ๅ…จ่พน็•Œ ้›†ไธญ่ฎค่ฏๆŽˆๆƒ๏ผŒๅ‡ๅฐ‘ๆ”ปๅ‡ป้ข
ๆต้‡็ฎกๆŽง ้™ๆตใ€็†”ๆ–ญใ€่ดŸ่ฝฝๅ‡่กก
ๅ่ฎฎ่ฝฌๆข HTTP/gRPC/WebSocket ไบ’่ฝฌ
่ฏทๆฑ‚่šๅˆ BFF ๆจกๅผ๏ผŒๅ‡ๅฐ‘ๅฎขๆˆท็ซฏ่ฏทๆฑ‚ๆฌกๆ•ฐ
็›‘ๆŽงๅฎก่ฎก ็ปŸไธ€ๆ—ฅๅฟ—ใ€้“พ่ทฏ่ฟฝ่ธชใ€ๆŒ‡ๆ ‡้‡‡้›†
็ฐๅบฆๅ‘ๅธƒ ๆŒ‰่ง„ๅˆ™่ทฏ็”ฑๅˆฐไธๅŒ็‰ˆๆœฌ

1.3 ็ฝ‘ๅ…ณๆžถๆž„

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     API Gateway                         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚  โ”‚  ่ฎค่ฏ   โ”‚ โ”‚  ้™ๆต   โ”‚ โ”‚  ่ทฏ็”ฑ   โ”‚ โ”‚  ็›‘ๆŽง   โ”‚      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚  โ”‚  ็ผ“ๅญ˜   โ”‚ โ”‚  ็†”ๆ–ญ   โ”‚ โ”‚  ่ฝฌๆข   โ”‚ โ”‚  ๆ—ฅๅฟ—   โ”‚      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ–ผ               โ–ผ               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Service A  โ”‚ โ”‚  Service B  โ”‚ โ”‚  Service C  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2. ็ฝ‘ๅ…ณๅœจๅˆ†ๅธƒๅผ็ณป็ปŸไธญ็š„ๅบ”็”จ

2.1 ๆ ธๅฟƒๅŠŸ่ƒฝ

่ทฏ็”ฑไธŽ่ดŸ่ฝฝๅ‡่กก

// YARP ่ทฏ็”ฑ้…็ฝฎ็คบไพ‹
{
  "ReverseProxy": {
    "Routes": {
      "orders-route": {
        "ClusterId": "orders-cluster",
        "Match": { "Path": "/api/orders/{**catch-all}" }
      },
      "users-route": {
        "ClusterId": "users-cluster",
        "Match": { "Path": "/api/users/{**catch-all}" }
      }
    },
    "Clusters": {
      "orders-cluster": {
        "LoadBalancingPolicy": "RoundRobin",
        "Destinations": {
          "d1": { "Address": "http://orders-service-1:5000" },
          "d2": { "Address": "http://orders-service-2:5000" }
        }
      }
    }
  }
}

่ฎค่ฏไธŽๆŽˆๆƒ

// JWT ่ฎค่ฏไธญ้—ดไปถ
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://identity-server";
        options.Audience = "api-gateway";
    });

// ็ฝ‘ๅ…ณ็ปŸไธ€้ชŒ่ฏ๏ผŒไธ‹ๆธธๆœๅŠกไฟกไปป็ฝ‘ๅ…ณ
app.UseAuthentication();
app.UseAuthorization();

้™ๆตไธŽ็†”ๆ–ญ

// ไฝฟ็”จ Polly ๅฎž็Žฐ็†”ๆ–ญ
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(context =>
    {
        context.AddRequestTransform(async transformContext =>
        {
            // ๆทปๅŠ ้™ๆต้€ป่พ‘
            await _rateLimiter.WaitAsync(transformContext.HttpContext.RequestAborted);
        });
    });

่ฏทๆฑ‚่šๅˆ๏ผˆBFF ๆจกๅผ๏ผ‰

// GraphQL ่šๅˆๅคšไธชๆœๅŠก
public class Query
{
    public async Task<OrderWithUser> GetOrderDetail(
        [Service] IOrderService orderService,
        [Service] IUserService userService,
        int orderId)
    {
        var order = await orderService.GetOrderAsync(orderId);
        var user = await userService.GetUserAsync(order.UserId);
        
        return new OrderWithUser { Order = order, User = user };
    }
}

2.2 ๅธธ่งๆžถๆž„ๆจกๅผ

ๅ•ไธ€็ฝ‘ๅ…ณ

้€‚็”จ๏ผšไธญๅฐๅž‹็ณป็ปŸ
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚ Gateway  โ”‚
         โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ–ผ         โ–ผ         โ–ผ
  Users    Orders    Products

ๅคš็ฝ‘ๅ…ณ๏ผˆๆŒ‰ๅฎขๆˆท็ซฏๅˆ†๏ผ‰

้€‚็”จ๏ผšๅคš็ซฏๅบ”็”จ๏ผˆWeb/Mobile/็ฌฌไธ‰ๆ–น๏ผ‰

  Web Client    Mobile App    Partner API
       โ”‚             โ”‚             โ”‚
       โ–ผ             โ–ผ             โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Web GW   โ”‚  โ”‚Mobile GW โ”‚  โ”‚Partner GWโ”‚
โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ–ผ
            Internal Services

็ฝ‘ๅ…ณ + Service Mesh

้€‚็”จ๏ผšๅคงๅž‹ๅพฎๆœๅŠก็ณป็ปŸ

Client โ†’ API Gateway โ†’ [Service Mesh (Istio/Linkerd)]
                              โ”‚
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ–ผ               โ–ผ               โ–ผ
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚Sidecar โ”‚      โ”‚Sidecar โ”‚      โ”‚Sidecar โ”‚
         โ”‚   +    โ”‚      โ”‚   +    โ”‚      โ”‚   +    โ”‚
         โ”‚Service โ”‚      โ”‚Service โ”‚      โ”‚Service โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2.3 ็ฝ‘ๅ…ณ vs Service Mesh

ๅŠŸ่ƒฝ API Gateway Service Mesh
ๆต้‡ๅ…ฅๅฃ โœ… ๅ—ๅŒ—ๅ‘ๆต้‡ โŒ ไธป่ฆไธœ่ฅฟๅ‘
่ฎค่ฏๆŽˆๆƒ โœ… ๅค–้ƒจ่ฎค่ฏ โœ… ๆœๅŠก้—ด mTLS
้™ๆต็†”ๆ–ญ โœ… ๅ…ฅๅฃ็บงๅˆซ โœ… ๆœๅŠก็บงๅˆซ
ๅ่ฎฎ่ฝฌๆข โœ… โŒ
่ฏทๆฑ‚่šๅˆ โœ… โŒ
ๅฏ่ง‚ๆต‹ๆ€ง โœ… ๅ…ฅๅฃ็บงๅˆซ โœ… ๅ…จ้“พ่ทฏ
้ƒจ็ฝฒๅคๆ‚ๅบฆ ไฝŽ ้ซ˜

3. .NET ็ฝ‘ๅ…ณๅฎž็Žฐๆ–นๆกˆ

3.1 ๆ–นๆกˆๆฆ‚่งˆ

ๆ–นๆกˆ ็ฑปๅž‹ ็ปดๆŠค่€… ้€‚็”จๅœบๆ™ฏ
YARP ๅๅ‘ไปฃ็†ๅบ“ Microsoft ่‡ชๅฎšไน‰็ฝ‘ๅ…ณ
Ocelot API ็ฝ‘ๅ…ณๆก†ๆžถ ๅผ€ๆบ็คพๅŒบ ๅฟซ้€Ÿๆญๅปบ
Azure API Management ไบ‘ๆœๅŠก Microsoft Azure ้ƒจ็ฝฒ
Kong ็‹ฌ็ซ‹็ฝ‘ๅ…ณ Kong Inc ๅคš่ฏญ่จ€็Žฏๅขƒ
่‡ช็ ”๏ผˆASP.NET Core๏ผ‰ ่‡ชๅฎšไน‰ - ็‰นๆฎŠ้œ€ๆฑ‚

3.2 YARP๏ผˆYet Another Reverse Proxy๏ผ‰

ๅŒ…: Yarp.ReverseProxy

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();
app.MapReverseProxy();
app.Run();

appsettings.json:

{
  "ReverseProxy": {
    "Routes": {
      "api-route": {
        "ClusterId": "api-cluster",
        "Match": { "Path": "/api/{**catch-all}" },
        "Transforms": [
          { "PathRemovePrefix": "/api" }
        ]
      }
    },
    "Clusters": {
      "api-cluster": {
        "LoadBalancingPolicy": "RoundRobin",
        "HealthCheck": {
          "Active": {
            "Enabled": true,
            "Interval": "00:00:10",
            "Path": "/health"
          }
        },
        "Destinations": {
          "d1": { "Address": "http://backend-1:5000" },
          "d2": { "Address": "http://backend-2:5000" }
        }
      }
    }
  }
}

่‡ชๅฎšไน‰ไธญ้—ดไปถ:

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(context =>
    {
        // ๆทปๅŠ ่ฏทๆฑ‚ๅคด
        context.AddRequestHeader("X-Forwarded-Host", context.HttpContext.Request.Host.Value);
        
        // ่‡ชๅฎšไน‰่ฝฌๆข
        context.AddRequestTransform(async transformContext =>
        {
            var userId = transformContext.HttpContext.User.FindFirst("sub")?.Value;
            if (userId != null)
            {
                transformContext.ProxyRequest.Headers.Add("X-User-Id", userId);
            }
        });
    });

3.3 Ocelot

ๅŒ…: Ocelot

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddJsonFile("ocelot.json");
builder.Services.AddOcelot();

var app = builder.Build();
await app.UseOcelot();
app.Run();

ocelot.json:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/users/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        { "Host": "users-service", "Port": 5001 }
      ],
      "UpstreamPathTemplate": "/users/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Bearer"
      },
      "RateLimitOptions": {
        "EnableRateLimiting": true,
        "Period": "1s",
        "Limit": 100
      }
    },
    {
      "DownstreamPathTemplate": "/api/orders/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        { "Host": "orders-service", "Port": 5002 }
      ],
      "UpstreamPathTemplate": "/orders/{everything}",
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "https://api.example.com"
  }
}

ๆœๅŠกๅ‘็Žฐ๏ผˆConsul๏ผ‰:

builder.Services.AddOcelot()
    .AddConsul();
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "UpstreamPathTemplate": "/users/{everything}",
      "ServiceName": "users-service",
      "LoadBalancerOptions": { "Type": "RoundRobin" }
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "consul",
      "Port": 8500,
      "Type": "Consul"
    }
  }
}

3.4 ่‡ช็ ”็ฝ‘ๅ…ณ๏ผˆASP.NET Core๏ผ‰

// ็ฎ€ๅ•ๅๅ‘ไปฃ็†ๅฎž็Žฐ
public class ProxyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IHttpClientFactory _clientFactory;
    private readonly IServiceDiscovery _discovery;
    
    public ProxyMiddleware(RequestDelegate next, IHttpClientFactory clientFactory, 
        IServiceDiscovery discovery)
    {
        _next = next;
        _clientFactory = clientFactory;
        _discovery = discovery;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        var path = context.Request.Path.Value;
        
        // ่ทฏ็”ฑๅŒน้…
        var service = ResolveService(path);
        if (service == null)
        {
            await _next(context);
            return;
        }
        
        // ๆœๅŠกๅ‘็Žฐ
        var endpoint = await _discovery.GetEndpointAsync(service);
        
        // ่ฝฌๅ‘่ฏทๆฑ‚
        var client = _clientFactory.CreateClient();
        var request = CreateProxyRequest(context, endpoint);
        var response = await client.SendAsync(request);
        
        // ๅคๅˆถๅ“ๅบ”
        await CopyResponseAsync(context, response);
    }
    
    private HttpRequestMessage CreateProxyRequest(HttpContext context, string endpoint)
    {
        var request = new HttpRequestMessage
        {
            Method = new HttpMethod(context.Request.Method),
            RequestUri = new Uri($"{endpoint}{context.Request.Path}{context.Request.QueryString}")
        };
        
        // ๅคๅˆถ่ฏทๆฑ‚ๅคด
        foreach (var header in context.Request.Headers)
        {
            request.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
        }
        
        // ๅคๅˆถ่ฏทๆฑ‚ไฝ“
        if (context.Request.ContentLength > 0)
        {
            request.Content = new StreamContent(context.Request.Body);
        }
        
        return request;
    }
}

4. .NET ็ฝ‘ๅ…ณๆก†ๆžถๅฏนๆฏ”

4.1 ๅŠŸ่ƒฝๅฏนๆฏ”

ๅŠŸ่ƒฝ YARP Ocelot Azure APIM Kong
่ทฏ็”ฑ โœ… โœ… โœ… โœ…
่ดŸ่ฝฝๅ‡่กก โœ… โœ… โœ… โœ…
ๅฅๅบทๆฃ€ๆŸฅ โœ… โœ… โœ… โœ…
้™ๆต ้œ€ๆ‰ฉๅฑ• โœ… โœ… โœ…
็†”ๆ–ญ ้œ€ๆ‰ฉๅฑ• โœ… โœ… โœ…
่ฎค่ฏ ้œ€้›†ๆˆ โœ… โœ… โœ…
่ฏทๆฑ‚่šๅˆ โŒ โœ… โœ… ๆ’ไปถ
ๆœๅŠกๅ‘็Žฐ ้œ€ๆ‰ฉๅฑ• โœ… โœ… โœ…
็ผ“ๅญ˜ ้œ€ๆ‰ฉๅฑ• โœ… โœ… โœ…
WebSocket โœ… โœ… โœ… โœ…
gRPC โœ… โŒ โœ… โœ…
็ฎก็† UI โŒ โŒ โœ… โœ…

4.2 ๆ€ง่ƒฝๅฏนๆฏ”

ๆŒ‡ๆ ‡ YARP Ocelot Kong
ๅžๅ้‡ โญโญโญโญโญ โญโญโญ โญโญโญโญ
ๅปถ่ฟŸ < 1ms 2-5ms 1-2ms
ๅ†…ๅญ˜ๅ ็”จ ไฝŽ ไธญ ้ซ˜
CPU ๅ ็”จ ไฝŽ ไธญ ไธญ

YARP ๆ˜ฏ็›ฎๅ‰ .NET ็”Ÿๆ€ไธญๆ€ง่ƒฝๆœ€ๅฅฝ็š„ๆ–นๆกˆ๏ผŒๅพฎ่ฝฏๅ†…้ƒจๆœๅŠกๅคง้‡ไฝฟ็”จใ€‚

4.3 ไผ˜ๅŠฃๅŠฟๅฏนๆฏ”

YARP

ไผ˜ๅŠฟ ๅŠฃๅŠฟ
โœ… ๅพฎ่ฝฏๅฎ˜ๆ–น็ปดๆŠค๏ผŒ้•ฟๆœŸๆ”ฏๆŒ โŒ ๅŠŸ่ƒฝ้œ€่ฆ่‡ชๅทฑๆ‰ฉๅฑ•
โœ… ๆ€ง่ƒฝๆžไฝณ โŒ ๆฒกๆœ‰ๅ†…็ฝฎ้™ๆต/็†”ๆ–ญ
โœ… ้ซ˜ๅบฆๅฏๅฎšๅˆถ โŒ ๅญฆไน ๆ›ฒ็บฟ็จ้ซ˜
โœ… ๆ”ฏๆŒ gRPC/WebSocket โŒ ๆ— ็ฎก็†็•Œ้ข
โœ… ไธŽ ASP.NET Core ๆทฑๅบฆ้›†ๆˆ

Ocelot

ไผ˜ๅŠฟ ๅŠฃๅŠฟ
โœ… ๅผ€็ฎฑๅณ็”จ๏ผŒๅŠŸ่ƒฝๅฎŒๆ•ด โŒ ๆ€ง่ƒฝไธๅฆ‚ YARP
โœ… ้…็ฝฎ็ฎ€ๅ• โŒ ไธๆ”ฏๆŒ gRPC
โœ… ๅ†…็ฝฎ้™ๆต/็†”ๆ–ญ/็ผ“ๅญ˜ โŒ ็คพๅŒบๆดป่ทƒๅบฆไธ‹้™
โœ… ๆ”ฏๆŒๆœๅŠกๅ‘็Žฐ๏ผˆConsul/Eureka๏ผ‰ โŒ ๆ‰ฉๅฑ•ๆ€งๆœ‰้™
โœ… ๆ–‡ๆกฃๅฎŒๅ–„

Azure API Management

ไผ˜ๅŠฟ ๅŠฃๅŠฟ
โœ… ๅฎŒๆ•ด็š„็ฎก็†็•Œ้ข โŒ ๆˆๆœฌ่พƒ้ซ˜
โœ… ๅผ€ๅ‘่€…้—จๆˆท โŒ ไป…้™ Azure
โœ… ๆ‰˜็ฎกๆœๅŠก๏ผŒๆ— ้œ€่ฟ็ปด โŒ ๅ†ทๅฏๅŠจๅปถ่ฟŸ
โœ… ๅ†…็ฝฎๅˆ†ๆžๅ’Œ็›‘ๆŽง โŒ ่‡ชๅฎšไน‰ๅ—้™
โœ… ็‰ˆๆœฌ็ฎก็†ใ€API ๆ–‡ๆกฃ

Kong

ไผ˜ๅŠฟ ๅŠฃๅŠฟ
โœ… ่ฏญ่จ€ๆ— ๅ…ณ๏ผŒๅคš็Žฏๅขƒ้ƒจ็ฝฒ โŒ ไธๆ˜ฏ .NET ๅŽŸ็”Ÿ
โœ… ไธฐๅฏŒ็š„ๆ’ไปถ็”Ÿๆ€ โŒ ้ƒจ็ฝฒๅคๆ‚๏ผˆ้œ€่ฆๆ•ฐๆฎๅบ“๏ผ‰
โœ… ไผไธš็‰ˆๅŠŸ่ƒฝๅผบๅคง โŒ ไผไธš็‰ˆๆ”ถ่ดน
โœ… ็ฎก็†็•Œ้ข โŒ ไธŽ .NET ้›†ๆˆไธๅฆ‚ๅŽŸ็”Ÿๆ–นๆกˆ

4.4 ้€‰ๅž‹ๅปบ่ฎฎ

ๅœบๆ™ฏ ๆŽจ่ๆ–นๆกˆ ็†็”ฑ
้ซ˜ๆ€ง่ƒฝ้œ€ๆฑ‚ YARP ๆ€ง่ƒฝๆœ€ไฝณ๏ผŒๅฎ˜ๆ–นๆ”ฏๆŒ
ๅฟซ้€Ÿๆญๅปบ Ocelot ๅŠŸ่ƒฝๅฎŒๆ•ด๏ผŒ้…็ฝฎ็ฎ€ๅ•
Azure ้ƒจ็ฝฒ Azure APIM ๆ‰˜็ฎกๆœๅŠก๏ผŒๆ— ้œ€่ฟ็ปด
ๅคš่ฏญ่จ€ๅพฎๆœๅŠก Kong ่ฏญ่จ€ๆ— ๅ…ณ๏ผŒๆ’ไปถไธฐๅฏŒ
ๆทฑๅบฆๅฎšๅˆถ YARP + ่‡ชๅฎšไน‰ๆ‰ฉๅฑ• ๅฏๆŽงๆ€งๆœ€ๅผบ
ไธญๅฐๅž‹้กน็›ฎ Ocelot ๅคŸ็”จไธ”็ฎ€ๅ•

5. ไบ‘้ƒจ็ฝฒ็ฝ‘ๅ…ณ่ฎพ่ฎกๆณจๆ„ไบ‹้กน

5.1 ้ซ˜ๅฏ็”จ่ฎพ่ฎก

                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚  Load Balancer  โ”‚
                    โ”‚   (Azure LB /   โ”‚
                    โ”‚    AWS ALB)     โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                             โ”‚
            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
            โ–ผ                โ–ผ                โ–ผ
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚   Gateway    โ”‚ โ”‚   Gateway    โ”‚ โ”‚   Gateway    โ”‚
    โ”‚  (Zone A)    โ”‚ โ”‚  (Zone B)    โ”‚ โ”‚  (Zone C)    โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๅ…ณ้”ฎ็‚น๏ผš - โœ… ๅคšๅฎžไพ‹้ƒจ็ฝฒ๏ผŒ่ทจๅฏ็”จๅŒบ - โœ… ๆ— ็Šถๆ€่ฎพ่ฎก๏ผŒๆ”ฏๆŒๆฐดๅนณๆ‰ฉๅฑ• - โœ… ๅฅๅบทๆฃ€ๆŸฅ้…็ฝฎ - โœ… ไผš่ฏไฟๆŒ๏ผˆๅฆ‚้œ€่ฆ๏ผ‰

5.2 ๅฎ‰ๅ…จ่ฎพ่ฎก

// 1. HTTPS ๅผบๅˆถ
app.UseHttpsRedirection();
app.UseHsts();

// 2. ่ฏทๆฑ‚ๅคดๅฎ‰ๅ…จ
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("X-Frame-Options", "DENY");
    context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
    await next();
});

// 3. IP ็™ฝๅๅ•๏ผˆๅ†…้ƒจๆœๅŠก๏ผ‰
builder.Services.AddReverseProxy()
    .AddTransforms(context =>
    {
        context.AddRequestTransform(async transformContext =>
        {
            var clientIp = transformContext.HttpContext.Connection.RemoteIpAddress;
            if (!IsAllowedIp(clientIp))
            {
                transformContext.HttpContext.Response.StatusCode = 403;
                return;
            }
        });
    });

// 4. ่ฏทๆฑ‚ๅคงๅฐ้™ๅˆถ
builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10MB
});

5.3 ้™ๆตไธŽ็†”ๆ–ญ

// ไฝฟ็”จ ASP.NET Core Rate Limiting (.NET 7+)
builder.Services.AddRateLimiter(options =>
{
    // ๅ…จๅฑ€้™ๆต
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
            factory: _ => new FixedWindowRateLimiterOptions
            {
                PermitLimit = 100,
                Window = TimeSpan.FromMinutes(1),
                QueueLimit = 10
            }));
    
    // ๆŒ‰่ทฏ็”ฑ้™ๆต
    options.AddPolicy("api", context =>
        RateLimitPartition.GetSlidingWindowLimiter(
            partitionKey: context.User.Identity?.Name ?? "anonymous",
            factory: _ => new SlidingWindowRateLimiterOptions
            {
                PermitLimit = 1000,
                Window = TimeSpan.FromMinutes(1),
                SegmentsPerWindow = 6
            }));
    
    options.OnRejected = async (context, token) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        await context.HttpContext.Response.WriteAsJsonAsync(new
        {
            error = "Too many requests",
            retryAfter = context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)
                ? retryAfter.TotalSeconds : 60
        }, token);
    };
});

app.UseRateLimiter();

5.4 ๅฏ่ง‚ๆต‹ๆ€ง

// OpenTelemetry ้›†ๆˆ
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddSource("Yarp.ReverseProxy")
            .AddOtlpExporter(options =>
            {
                options.Endpoint = new Uri("http://otel-collector:4317");
            });
    })
    .WithMetrics(metrics =>
    {
        metrics
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddMeter("Yarp.ReverseProxy")
            .AddOtlpExporter();
    });

// ๅฅๅบทๆฃ€ๆŸฅ็ซฏ็‚น
builder.Services.AddHealthChecks()
    .AddCheck("gateway", () => HealthCheckResult.Healthy())
    .AddRedis(redisConnectionString)
    .AddUrlGroup(new Uri("http://backend/health"), "backend");

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

5.5 ้…็ฝฎ็ฎก็†

// ๅŠจๆ€้…็ฝฎ๏ผˆๆ— ้œ€้‡ๅฏ๏ผ‰
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .ConfigureHttpClient((context, handler) =>
    {
        // ่‡ชๅฎšไน‰ HttpClient
        handler.SslOptions.RemoteCertificateValidationCallback = 
            (sender, cert, chain, errors) => true; // ไป…ๅผ€ๅ‘็Žฏๅขƒ
    });

// ไปŽ้…็ฝฎไธญๅฟƒๅŠ ่ฝฝ๏ผˆๅฆ‚ Azure App Configuration๏ผ‰
builder.Configuration.AddAzureAppConfiguration(options =>
{
    options.Connect(connectionString)
        .Select("Gateway:*")
        .ConfigureRefresh(refresh =>
        {
            refresh.Register("Gateway:Sentinel", refreshAll: true)
                .SetCacheExpiration(TimeSpan.FromSeconds(30));
        });
});

5.6 ๅฎนๅ™จๅŒ–้ƒจ็ฝฒ

Dockerfile:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["Gateway/Gateway.csproj", "Gateway/"]
RUN dotnet restore "Gateway/Gateway.csproj"
COPY . .
WORKDIR "/src/Gateway"
RUN dotnet build -c Release -o /app/build

FROM build AS publish
RUN dotnet publish -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

# ๅฅๅบทๆฃ€ๆŸฅ
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost/health || exit 1

ENTRYPOINT ["dotnet", "Gateway.dll"]

Kubernetes ้ƒจ็ฝฒ:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-gateway
  template:
    metadata:
      labels:
        app: api-gateway
    spec:
      containers:
      - name: gateway
        image: myregistry/api-gateway:latest
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 10
        env:
        - name: ASPNETCORE_ENVIRONMENT
          value: "Production"
---
apiVersion: v1
kind: Service
metadata:
  name: api-gateway
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: api-gateway
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-gateway-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-gateway
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

5.7 ไบ‘้ƒจ็ฝฒๆฃ€ๆŸฅๆธ…ๅ•

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              ไบ‘้ƒจ็ฝฒ็ฝ‘ๅ…ณๆฃ€ๆŸฅๆธ…ๅ•                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                         โ”‚
โ”‚  ้ซ˜ๅฏ็”จ                                                 โ”‚
โ”‚  โ–ก ๅคšๅฎžไพ‹้ƒจ็ฝฒ๏ผˆ่‡ณๅฐ‘ 3 ไธช๏ผ‰                              โ”‚
โ”‚  โ–ก ่ทจๅฏ็”จๅŒบๅˆ†ๅธƒ                                        โ”‚
โ”‚  โ–ก ๅฅๅบทๆฃ€ๆŸฅ้…็ฝฎ                                        โ”‚
โ”‚  โ–ก ่‡ชๅŠจๆ‰ฉ็ผฉๅฎน็ญ–็•ฅ                                      โ”‚
โ”‚                                                         โ”‚
โ”‚  ๅฎ‰ๅ…จ                                                   โ”‚
โ”‚  โ–ก HTTPS ๅผบๅˆถ                                          โ”‚
โ”‚  โ–ก WAF ้…็ฝฎ                                            โ”‚
โ”‚  โ–ก DDoS ้˜ฒๆŠค                                           โ”‚
โ”‚  โ–ก ่ฏทๆฑ‚ๅคงๅฐ้™ๅˆถ                                        โ”‚
โ”‚  โ–ก ๆ•ๆ„Ÿไฟกๆฏ่„ฑๆ•                                        โ”‚
โ”‚                                                         โ”‚
โ”‚  ๆ€ง่ƒฝ                                                   โ”‚
โ”‚  โ–ก ้™ๆต็ญ–็•ฅ                                            โ”‚
โ”‚  โ–ก ็†”ๆ–ญ้™็บง                                            โ”‚
โ”‚  โ–ก ๅ“ๅบ”็ผ“ๅญ˜                                            โ”‚
โ”‚  โ–ก ่ฟžๆŽฅๆฑ ้…็ฝฎ                                          โ”‚
โ”‚                                                         โ”‚
โ”‚  ๅฏ่ง‚ๆต‹                                                 โ”‚
โ”‚  โ–ก ๆ—ฅๅฟ—่šๅˆ                                            โ”‚
โ”‚  โ–ก ้“พ่ทฏ่ฟฝ่ธช                                            โ”‚
โ”‚  โ–ก ๆŒ‡ๆ ‡็›‘ๆŽง                                            โ”‚
โ”‚  โ–ก ๅ‘Š่ญฆ้…็ฝฎ                                            โ”‚
โ”‚                                                         โ”‚
โ”‚  ่ฟ็ปด                                                   โ”‚
โ”‚  โ–ก ้…็ฝฎไธญๅฟƒ้›†ๆˆ                                        โ”‚
โ”‚  โ–ก ๅฏ†้’ฅ็ฎก็†๏ผˆKey Vault๏ผ‰                               โ”‚
โ”‚  โ–ก ่“็ปฟ/้‡‘ไธ้›€ๅ‘ๅธƒ                                     โ”‚
โ”‚  โ–ก ๅ›žๆปš็ญ–็•ฅ                                            โ”‚
โ”‚                                                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

6. ๆ€ป็ป“

ๆ ธๅฟƒ่ฆ็‚น

ไธป้ข˜ ๅ…ณ้”ฎ็‚น
็ฝ‘ๅ…ณไฝœ็”จ ็ปŸไธ€ๅ…ฅๅฃใ€ๅฎ‰ๅ…จ่พน็•Œใ€ๆต้‡็ฎกๆŽงใ€็›‘ๆŽงๅฎก่ฎก
ๆŽจ่ๆ–นๆกˆ ้ซ˜ๆ€ง่ƒฝ้€‰ YARP๏ผŒๅฟซ้€Ÿๆญๅปบ้€‰ Ocelot๏ผŒAzure ้€‰ APIM
ไบ‘้ƒจ็ฝฒ ๅคšๅฎžไพ‹ใ€่ทจๅฏ็”จๅŒบใ€้™ๆต็†”ๆ–ญใ€ๅฏ่ง‚ๆต‹ๆ€ง
ๅฎ‰ๅ…จ HTTPSใ€WAFใ€้™ๆตใ€่ฏทๆฑ‚ๆ ก้ชŒ

้€‰ๅž‹้€ŸๆŸฅ

้œ€ๆฑ‚ ๆ–นๆกˆ
ๆ–ฐ้กน็›ฎ + ้ซ˜ๆ€ง่ƒฝ YARP
ๅฟซ้€Ÿๆญๅปบ + ๅŠŸ่ƒฝๅฎŒๆ•ด Ocelot
Azure ไบ‘ๅŽŸ็”Ÿ Azure API Management
ๅคš่ฏญ่จ€ๅพฎๆœๅŠก Kong

ๅ‚่€ƒ่ต„ๆบ