☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
microservices 初學

🔌 用 ASP.NET Core 建立微服務 API

📌 Minimal API vs Controller-based API

ASP.NET Core 提供兩種建立 API 的方式:

// ── 方式 1:Minimal API(簡潔、適合小型微服務) ──
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/api/products", async (ProductDbContext db) =>
    await db.Products.ToListAsync());

app.MapGet("/api/products/{id}", async (int id, ProductDbContext db) =>
    await db.Products.FindAsync(id) is Product p
        ? Results.Ok(p)
        : Results.NotFound());

app.Run();

// ── 方式 2:Controller-based API(結構化、適合大型服務) ──
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _service;

    public ProductsController(IProductService service) => _service = service;

    [HttpGet]
    public async Task<ActionResult<List<ProductDto>>> GetAll()
        => Ok(await _service.GetAllAsync());

    [HttpGet("{id}")]
    public async Task<ActionResult<ProductDto>> GetById(int id)
    {
        var product = await _service.GetByIdAsync(id);
        return product is null ? NotFound() : Ok(product);
    }
}
比較 Minimal API Controller-based
程式碼量
學習曲線
適用場景 小型微服務 大型、複雜的服務
過濾器 EndpointFilter ActionFilter
API 版本控制 支援 支援

📌 建立第一個微服務 API

步驟 1:建立專案

# 建立方案與專案
dotnet new sln -n EShop
dotnet new webapi -n ProductService --no-https
dotnet sln add ProductService/ProductService.csproj

# 加入必要套件
cd ProductService
dotnet add package Microsoft.EntityFrameworkCore.Npgsql
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
dotnet add package AspNetCore.HealthChecks.NpgSql

步驟 2:定義領域模型

// Models/Product.cs
namespace ProductService.Models;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public decimal Price { get; set; }
    public int StockQuantity { get; set; }
    public string Category { get; set; } = string.Empty;
    public bool IsActive { get; set; } = true;
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

📌 RESTful 設計原則

RESTful API 設計規範:
├── 使用名詞而非動詞:/api/products(不是 /api/getProducts)
├── 使用複數形式:/api/products(不是 /api/product)
├── 使用 HTTP 方法表達操作:
│   ├── GET    /api/products      → 取得列表
│   ├── GET    /api/products/42   → 取得單筆
│   ├── POST   /api/products      → 建立
│   ├── PUT    /api/products/42   → 完整更新
│   ├── PATCH  /api/products/42   → 部分更新
│   └── DELETE /api/products/42   → 刪除
├── 使用正確的 HTTP 狀態碼
└── 使用巢狀路由表達關係:/api/orders/42/items

📌 DTO 與 AutoMapper

// DTOs/ProductDto.cs — 回傳給客戶端的資料
public record ProductDto(
    int Id,
    string Name,
    string Description,
    decimal Price,
    string Category,
    bool IsActive);

// DTOs/CreateProductDto.cs — 建立時的輸入
public record CreateProductDto(
    string Name,
    string Description,
    decimal Price,
    int StockQuantity,
    string Category);

// DTOs/UpdateProductDto.cs — 更新時的輸入
public record UpdateProductDto(
    string? Name,
    string? Description,
    decimal? Price,
    int? StockQuantity,
    string? Category);

// Profiles/ProductProfile.cs — AutoMapper 映射設定
public class ProductProfile : Profile
{
    public ProductProfile()
    {
        CreateMap<Product, ProductDto>();
        CreateMap<CreateProductDto, Product>();
        CreateMap<UpdateProductDto, Product>()
            .ForAllMembers(opt => opt.Condition((src, dest, srcMember) =>
                srcMember != null)); // 只更新非 null 的欄位
    }
}

📌 健康檢查 (Health Checks)

微服務必須提供健康檢查端點,讓 API Gateway 和 Kubernetes 知道服務狀態。

// Program.cs
builder.Services.AddHealthChecks()
    .AddNpgSql(connectionString, name: "database")
    .AddCheck("self", () => HealthCheckResult.Healthy());

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = async (context, report) =>
    {
        context.Response.ContentType = "application/json";
        var result = new
        {
            status = report.Status.ToString(),
            checks = report.Entries.Select(e => new
            {
                name = e.Key,
                status = e.Value.Status.ToString(),
                duration = e.Value.Duration.TotalMilliseconds
            }),
            totalDuration = report.TotalDuration.TotalMilliseconds
        };
        await context.Response.WriteAsJsonAsync(result);
    }
});

// /health/ready — 完整就緒檢查(包含資料庫)
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
});

// /health/live — 存活檢查(只檢查應用本身)
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = _ => false // 不執行任何外部檢查
});

📌 Swagger / OpenAPI 文件

// Program.cs
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "Product Service API",
        Version = "v1",
        Description = "電商微服務 - 商品管理 API"
    });
});

app.UseSwagger();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "Product Service v1");
    c.RoutePrefix = string.Empty; // Swagger 作為首頁
});

📌 完整範例:Product Service

// Program.cs — 完整的 Product 微服務
var builder = WebApplication.CreateBuilder(args);

// 服務注冊
builder.Services.AddDbContext<ProductDbContext>(opt =>
    opt.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddHealthChecks()
    .AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

// ── API 端點 ──
var products = app.MapGroup("/api/products");

products.MapGet("/", async (IProductRepository repo, IMapper mapper) =>
{
    var items = await repo.GetAllAsync();
    return Results.Ok(mapper.Map<List<ProductDto>>(items));
});

products.MapGet("/{id}", async (int id, IProductRepository repo, IMapper mapper) =>
{
    var product = await repo.GetByIdAsync(id);
    return product is null ? Results.NotFound() : Results.Ok(mapper.Map<ProductDto>(product));
});

products.MapPost("/", async (CreateProductDto dto, IProductRepository repo, IMapper mapper) =>
{
    var product = mapper.Map<Product>(dto);
    await repo.AddAsync(product);
    var result = mapper.Map<ProductDto>(product);
    return Results.Created($"/api/products/{product.Id}", result);
});

app.MapHealthChecks("/health");

app.Run();

下一章: 我們將學習微服務之間如何溝通 — 同步的 HTTP/gRPC 和非同步的訊息佇列。

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊