API 网关与 BFF 模式
大约 8 分钟约 2321 字
API 网关与 BFF 模式
简介
API 网关是微服务架构中的统一入口,负责路由转发、认证授权、限流熔断等横切关注点。BFF(Backend for Frontend)模式为不同客户端提供定制化的 API 聚合层。理解 YARP、Ocelot 等网关框架的配置与扩展,有助于构建高效的 API 网关。
特点
YARP 网关配置
基础配置
// dotnet add package Yarp.ReverseProxy
// appsettings.json
// {
// "ReverseProxy": {
// "Routes": {
// "user-route": {
// "ClusterId": "user-cluster",
// "AuthorizationPolicy": "Default",
// "Match": {
// "Path": "/api/users/{**catch-all}",
// "Methods": [ "GET", "POST", "PUT", "DELETE" ]
// },
// "Transforms": [
// { "PathPattern": "{**catch-all}" },
// { "RequestHeader": "X-Gateway", "Set": "YARP" }
// ]
// },
// "order-route": {
// "ClusterId": "order-cluster",
// "Match": {
// "Path": "/api/orders/{**catch-all}"
// }
// },
// "product-route": {
// "ClusterId": "product-cluster",
// "Match": {
// "Path": "/api/products/{**catch-all}"
// }
// }
// },
// "Clusters": {
// "user-cluster": {
// "HealthCheck": {
// "Active": {
// "Enabled": true,
// "Interval": "00:00:10",
// "Timeout": "00:00:05",
// "Path": "/health"
// }
// },
// "Destinations": {
// "user-1": { "Address": "https://user-service:8080" },
// "user-2": { "Address": "https://user-service:8081" }
// },
// "LoadBalancingPolicy": "RoundRobin"
// },
// "order-cluster": {
// "Destinations": {
// "order-1": { "Address": "https://order-service:8080" }
// }
// }
// }
// }
// }
// Program.cs
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// 认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://auth.example.com";
options.Audience = "api-gateway";
});
app.UseAuthentication();
app.UseAuthorization();
app.MapReverseProxy();
// 负载均衡策略:
// RoundRobin — 轮询
// LeastRequests — 最少连接
// Random — 随机
// PowerOfTwoChoices — 随机选两个,挑连接少的自定义转换与中间件
// 请求/响应转换
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
.AddTransforms(transformBuilder =>
{
// 添加请求头
transformBuilder.AddRequestTransform(async context =>
{
// 关联 ID
var correlationId = context.HttpContext.TraceIdentifier;
context.ProxyRequest.Headers.Add("X-Correlation-Id", correlationId);
context.ProxyRequest.Headers.Add("X-Request-Id", correlationId);
// 传递用户信息
var user = context.HttpContext.User;
if (user.Identity?.IsAuthenticated == true)
{
context.ProxyRequest.Headers.Add("X-User-Id",
user.FindFirst("sub")?.Value ?? "");
context.ProxyRequest.Headers.Add("X-User-Roles",
user.FindAll("role").Select(c => c.Value).ToArray());
}
// 客户端信息
context.ProxyRequest.Headers.Add("X-Client-IP",
context.HttpContext.Connection.RemoteIpAddress?.ToString() ?? "");
context.ProxyRequest.Headers.Add("X-Forwarded-For",
context.HttpContext.Connection.RemoteIpAddress?.ToString() ?? "");
});
// 响应转换
transformBuilder.AddResponseTransform(async context =>
{
// 添加安全头
context.HttpContext.Response.Headers.XContentTypeOptions = "nosniff";
context.HttpContext.Response.Headers.XFrameOptions = "DENY";
// 添加处理时间
var elapsed = context.HttpContext.Items["RequestStartTime"] is DateTime start
? (DateTime.UtcNow - start).TotalMilliseconds
: 0;
context.HttpContext.Response.Headers.Append("X-Response-Time", $"{elapsed:F0}ms");
});
});
// 网关中间件管道
app.Use(async (context, next) =>
{
context.Items["RequestStartTime"] = DateTime.UtcNow;
// 限流检查
var rateLimiter = context.RequestServices.GetRequiredService<IRateLimiterService>();
if (!await rateLimiter.AllowRequestAsync(context))
{
context.Response.StatusCode = 429;
await context.Response.WriteAsJsonAsync(new
{
type = "https://example.com/errors/rate-limit",
title = "Too Many Requests",
status = 429
});
return;
}
await next();
});
// 自定义授权策略
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminOnly", policy =>
policy.RequireRole("admin"))
.AddPolicy("UserOrAdmin", policy =>
policy.RequireRole("user", "admin"));
// 路由绑定授权策略
// 在配置中设置 "AuthorizationPolicy": "AdminOnly"BFF 模式
前端定制化网关
// BFF(Backend for Frontend)— 为不同客户端提供定制 API
// Web BFF — 服务 Web 前端
// Mobile BFF — 服务移动端
// Admin BFF — 服务管理后台
// Web BFF 示例
public class WebBffController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<WebBffController> _logger;
public WebBffController(IHttpClientFactory httpClientFactory, ILogger<WebBffController> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}
// 聚合首页数据(一次请求获取多个服务的数据)
[HttpGet("api/web/home")]
public async Task<ActionResult<HomePageDto>> GetHomePage(CancellationToken ct)
{
// 并行调用多个服务
var userTask = GetCurrentUserAsync(ct);
var recommendationsTask = GetRecommendationsAsync(ct);
var cartTask = GetCartAsync(ct);
var notificationsTask = GetNotificationsAsync(ct);
await Task.WhenAll(userTask, recommendationsTask, cartTask, notificationsTask);
var homePage = new HomePageDto
{
User = await userTask,
Recommendations = await recommendationsTask,
Cart = await cartTask,
Notifications = await notificationsTask
};
return Ok(homePage);
}
// 订单详情页聚合
[HttpGet("api/web/orders/{orderId}")]
public async Task<ActionResult<OrderDetailDto>> GetOrderDetail(Guid orderId, CancellationToken ct)
{
var orderClient = _httpClientFactory.CreateClient("OrderService");
var userClient = _httpClientFactory.CreateClient("UserService");
var productClient = _httpClientFactory.CreateClient("ProductService");
// 获取订单
var orderResponse = await orderClient.GetAsync($"/api/orders/{orderId}", ct);
orderResponse.EnsureSuccessStatusCode();
var order = await orderResponse.Content.ReadFromJsonAsync<OrderDto>(ct);
// 并行获取关联数据
var userTask = userClient.GetFromJsonAsync<UserDto>($"/api/users/{order!.UserId}", ct);
var productTasks = order.Items.Select(item =>
productClient.GetFromJsonAsync<ProductDto>($"/api/products/{item.ProductId}", ct));
var user = await userTask;
var products = await Task.WhenAll(productTasks);
return Ok(new OrderDetailDto
{
Order = order,
User = user!,
Products = products.Select((p, i) => new OrderProductDto
{
Product = p!,
Quantity = order.Items[i].Quantity
}).ToList()
});
}
private async Task<UserDto?> GetCurrentUserAsync(CancellationToken ct)
{
var client = _httpClientFactory.CreateClient("UserService");
return await client.GetFromJsonAsync<UserDto>("/api/users/me", ct);
}
private async Task<List<ProductDto>> GetRecommendationsAsync(CancellationToken ct)
{
var client = _httpClientFactory.CreateClient("ProductService");
return await client.GetFromJsonAsync<List<ProductDto>>("/api/products/recommendations", ct)
?? new List<ProductDto>();
}
private async Task<CartDto?> GetCartAsync(CancellationToken ct)
{
var client = _httpClientFactory.CreateClient("OrderService");
return await client.GetFromJsonAsync<CartDto>("/api/cart", ct);
}
private async Task<List<NotificationDto>> GetNotificationsAsync(CancellationToken ct)
{
var client = _httpClientFactory.CreateClient("NotificationService");
return await client.GetFromJsonAsync<List<NotificationDto>>("/api/notifications", ct)
?? new List<NotificationDto>();
}
}
// DTO 定义
public record HomePageDto
{
public UserDto? User { get; init; }
public List<ProductDto> Recommendations { get; init; } = new();
public CartDto? Cart { get; init; }
public List<NotificationDto> Notifications { get; init; } = new();
}
public record OrderDetailDto
{
public OrderDto Order { get; init; } = null!;
public UserDto User { get; init; } = null!;
public List<OrderProductDto> Products { get; init; } = new();
}GraphQL BFF
// dotnet add package GraphQL
// dotnet add package GraphQL.MicrosoftDI
// GraphQL 类型定义
public class UserType : ObjectGraphType<UserDto>
{
public UserType()
{
Field(x => x.Id);
Field(x => x.Name);
Field(x => x.Email);
Field(x => x.Avatar);
}
}
public class OrderType : ObjectGraphType<OrderDto>
{
public OrderType()
{
Field(x => x.Id);
Field(x => x.Status);
Field(x => x.TotalAmount);
Field(x => x.CreatedAt);
// 按需加载用户
Field<UserType>("user")
.ResolveAsync(async context =>
{
var client = context.RequestServices.GetRequiredService<IHttpClientFactory>()
.CreateClient("UserService");
return await client.GetFromJsonAsync<UserDto>(
$"/api/users/{context.Source.UserId}", context.CancellationToken);
});
// 按需加载订单项
Field<ListGraphType<OrderItemType>>("items")
.ResolveAsync(async context =>
{
var client = context.RequestServices.GetRequiredService<IHttpClientFactory>()
.CreateClient("OrderService");
return await client.GetFromJsonAsync<List<OrderItemDto>>(
$"/api/orders/{context.Source.Id}/items", context.CancellationToken);
});
}
}
// 查询定义
public class AppQuery : ObjectGraphType
{
public AppQuery()
{
Field<UserType>("user")
.Argument<IdGraphType>("id")
.ResolveAsync(async context =>
{
var id = context.GetArgument<Guid>("id");
var client = context.RequestServices.GetRequiredService<IHttpClientFactory>()
.CreateClient("UserService");
return await client.GetFromJsonAsync<UserDto>($"/api/users/{id}", context.CancellationToken);
});
Field<ListGraphType<OrderType>>("orders")
.Argument<IdGraphType>("userId")
.ResolveAsync(async context =>
{
var userId = context.GetArgument<Guid>("userId");
var client = context.RequestServices.GetRequiredService<IHttpClientFactory>()
.CreateClient("OrderService");
return await client.GetFromJsonAsync<List<OrderDto>>(
$"/api/orders?userId={userId}", context.CancellationToken);
});
}
}
// 注册
builder.Services.AddGraphQL(b => b
.AddAutoSchema<AppQuery>()
.AddSystemTextJson());
app.MapGraphQL("/graphql");
app.MapGraphQLPlayground("/graphql/ui");网关安全
认证与限流
// 全局限流
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
{
var userId = context.User?.FindFirst("sub")?.Value ?? context.Connection.RemoteIpAddress?.ToString() ?? "anonymous";
return RateLimitPartition.GetSlidingWindowLimiter(userId,
_ => new SlidingWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1),
SegmentsPerWindow = 6,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 10
});
});
options.OnRejected = async (context, ct) =>
{
context.HttpContext.Response.StatusCode = 429;
await context.HttpContext.Response.WriteAsJsonAsync(new
{
type = "https://example.com/errors/rate-limit",
title = "Too Many Requests",
status = 429,
detail = "请求过于频繁,请稍后重试"
}, ct);
};
});
// API Key 认证
public class ApiKeyMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ApiKeyMiddleware> _logger;
public ApiKeyMiddleware(RequestDelegate next, ILogger<ApiKeyMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// 健康检查端点跳过认证
if (context.Request.Path.StartsWithSegments("/health"))
{
await _next(context);
return;
}
if (!context.Request.Headers.TryGetValue("X-Api-Key", out var apiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(new { error = "API Key required" });
return;
}
var validKey = context.RequestServices
.GetRequiredService<IConfiguration>()["ApiKeys:Gateway"];
if (apiKey != validKey)
{
_logger.LogWarning("Invalid API Key from {IP}",
context.Connection.RemoteIpAddress);
context.Response.StatusCode = 403;
await context.Response.WriteAsJsonAsync(new { error = "Invalid API Key" });
return;
}
await _next(context);
}
}优点
缺点
总结
YARP 是 ASP.NET Core 官方的高性能反向代理框架,通过 JSON 配置定义路由和集群,支持健康检查和多种负载均衡策略。BFF 模式为不同客户端提供定制化的 API 聚合层,通过并行调用减少前端请求次数。GraphQL BFF 支持按需加载关联数据。网关层集中处理认证、限流、日志等横切关注点。建议为 Web、Mobile、Admin 分别设计 BFF,避免过度通用化。
关键知识点
- 先分清这个主题位于请求链路、后台任务链路还是基础设施链路。
- 服务端主题通常不只关心功能正确,还关心稳定性、性能和可观测性。
- 任何框架能力都要结合配置、生命周期、异常传播和外部依赖一起看。
项目落地视角
- 画清请求进入、业务执行、外部调用、日志记录和错误返回的完整路径。
- 为关键链路补齐超时、重试、熔断、追踪和结构化日志。
- 把配置与敏感信息分离,并明确不同环境的差异来源。
常见误区
- 只会堆中间件或组件,不知道它们在链路中的执行顺序。
- 忽略生命周期和线程池、连接池等运行时资源约束。
- 没有监控和测试就对性能或可靠性下结论。
进阶路线
- 继续向运行时行为、可观测性、发布治理和微服务协同深入。
- 把主题和数据库、缓存、消息队列、认证授权联动起来理解。
- 沉淀团队级模板,包括统一异常处理、配置约定和基础设施封装。
适用场景
- 当你准备把《API 网关与 BFF 模式》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合 API 服务、后台任务、实时通信、认证授权和微服务协作场景。
- 当需求开始涉及稳定性、性能、可观测性和发布流程时,这类主题会成为基础设施能力。
落地建议
- 先定义请求链路与失败路径,再决定中间件、过滤器、服务边界和依赖方式。
- 为关键链路补日志、指标、追踪、超时与重试策略。
- 环境配置与敏感信息分离,避免把生产参数写死在代码或镜像里。
排错清单
- 先确认问题发生在路由、模型绑定、中间件、业务层还是基础设施层。
- 检查 DI 生命周期、配置来源、序列化规则和认证上下文。
- 查看线程池、连接池、缓存命中率和外部依赖超时。
复盘问题
- 如果把《API 网关与 BFF 模式》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《API 网关与 BFF 模式》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《API 网关与 BFF 模式》最大的收益和代价分别是什么?
