本文展示一個 ASP.NET Core (C#) 實現的 IHttpModule / 中間件,攔截特定路徑 (/Server/...) 請求,轉發至內部服務 (https://localhost:89),實現跨域/微服務網關功能,並透傳表單數據、Cookie、Header。
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Gateway {
public class ReverseProxyMiddleware {
private readonly RequestDelegate _next;
private const string TargetBase = "https://localhost:89";
private const string Prefix = "/Server";
public ReverseProxyMiddleware(RequestDelegate next) {
_next = next;
}
public async Task InvokeAsync(HttpContext context) {
var path = context.Request.Path.Value;
if (!path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase)) {
await _next(context);
return;
}
// 構建目標 URL
var targetPath = path.Substring(Prefix.Length);
var targetUrl = TargetBase + targetPath + context.Request.QueryString;
// 建立上游請求
var request = WebRequest.CreateHttp(targetUrl);
request.Method = context.Request.Method;
request.ContentType = context.Request.ContentType ?? "application/x-www-form-urlencoded";
request.Timeout = 60000;
// 複製 Header(排除 Host、Connection 等)
foreach (var header in context.Request.Headers) {
if (!WebHeaderCollection.IsRestricted(header.Key)) {
request.Headers[header.Key] = header.Value.ToString();
}
}
// 轉發 Body (POST/PUT)
if (context.Request.Method != "GET" && context.Request.Method != "HEAD") {
using var reqStream = request.GetRequestStream();
await context.Request.Body.CopyToAsync(reqStream);
}
try {
using var response = (HttpWebResponse)await request.GetResponseAsync();
// 複製響應
context.Response.StatusCode = (int)response.StatusCode;
foreach (string key in response.Headers) {
if (!WebHeaderCollection.IsRestricted(key)) {
context.Response.Headers[key] = response.Headers[key];
}
}
using var respStream = response.GetResponseStream();
await respStream.CopyToAsync(context.Response.Body);
} catch (WebException ex) {
context.Response.StatusCode = 502;
await context.Response.WriteAsync("Bad Gateway: " + ex.Message);
}
}
}
}
// Startup.cs 註冊
app.UseMiddleware();
/Server/ 前綴路由至內部微服務,其餘走正常管道。Host, Connection, Content-Length) 避免衝突。CopyToAsync 避免大請求佔用內存。WebException 返回 502,記錄日誌便於排查。X-User-Id) 傳遞。微軟官方推出 YARP,提供聲明式配置、負載均衡、健康檢查、WebSocket 支援、速率限制等企業級功能,強烈建議新專案直接使用:
// appsettings.json
{
"ReverseProxy": {
"Routes": {
"server-route": {
"ClusterId": "internal",
"Match": { "Path": "/Server/{**catch-all}" },
"Transforms": [{ "PathPattern": "{**catch-all}" }]
}
},
"Clusters": {
"internal": {
"Destinations": {
"dest1": { "Address": "https://localhost:89/" }
}
}
}
}
}
// Program.cs
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
app.MapReverseProxy();
手寫中間件適合學習原理、極簡場景;生產網關請用 YARP、Ocelot、Kong、APISIX 等成熟方案。