ABP微服務架構中網關層NullReferenceException問題解析與HTTP配置優化
一、網關層System.NullReferenceException問題解析
1.1 問題現象與原因分析
在ABP微服務架構開發過程中,網關層啟動后調用微服務接口時出現以下異常:
System.NullReferenceException: Object reference not set to an instance of an object.
異常堆棧指向網關層控制器中的某一行代碼,通常是在調用微服務接口的位置。進一步分析發現,該異常是由于網關層控制器中依賴的服務接口實例為null導致的。
1.2 關鍵代碼場景
public class ProdcutController : AbpController, IProductService
{public IProductService ProductService { get; set; } // 屬性注入,實例為null[HttpGet]public async Task<PagedResultDto<ProductDto>> GetListAsync([FromQuery] PagedAndSortedResultRequestDto pagedAndSortedResultRequestDto){return await ProductService.GetListAsync(pagedAndSortedResultRequestDto); // 此處引發異常}
}
1.3 為什么注釋app.UseConfiguredEndpoints()可以解決問題
在ABP框架中,app.UseConfiguredEndpoints();
方法用于注冊和啟用應用程序的路由系統,包括控制器路由的注冊和依賴注入容器的初始化。當注釋掉該方法后:
- 路由系統未啟用:網關層的控制器不會被注冊到路由系統中,請求不再由網關層的控制器處理
- Ocelot直接代理請求:請求被Ocelot直接捕獲并轉發到微服務,繞過了網關層的控制器
- 依賴注入問題被繞過:由于網關層控制器不再處理請求,依賴注入失敗導致的空引用異常不再觸發
// 原啟動配置
app.UseConfiguredEndpoints(); // 啟用路由系統,會觸發控制器依賴注入檢查
app.UseOcelot().Wait(); // 啟用Ocelot代理// 修改后配置(注釋掉UseConfiguredEndpoints)
// app.UseConfiguredEndpoints(); // 注釋掉,不啟用網關層路由系統
app.UseOcelot().Wait(); // Ocelot直接處理所有請求
1.4 本質原因與隱患
這種解決方案本質上是繞過了問題而非解決了問題,存在以下隱患:
- 網關層的自定義控制器功能失效
- 網關層的中間件和過濾器不再對請求生效
- 無法利用ABP框架的依賴注入和控制器特性
正確的解決方案應該是修復依賴注入配置,而不是繞過路由系統。
二、ABP微服務架構中HTTPS轉HTTP配置指南
2.1 為什么需要將HTTPS改為HTTP
在開發環境中,HTTPS配置可能會遇到以下問題:
- 證書配置復雜,需要信任開發證書
- 本地調試時HTTPS可能引發連接問題
- 微服務間通信使用HTTP更便捷
2.2 網關層Ocelot配置修改
2.2.1 修改Ocelot配置文件
找到網關層的ocelot.json
配置文件,將所有DownstreamScheme
從https
改為http
,并調整端口:
{"Routes": [{"UpstreamPathTemplate": "/api/productservice/{everything}","DownstreamPathTemplate": "/api/productservice/{everything}","DownstreamScheme": "http", // 關鍵修改:從https改為http"DownstreamHostAndPorts": [{"Host": "localhost","Port": 50383 // 改為HTTP端口,與微服務的HTTP端口一致}],// 其他配置保持不變...}],"GlobalConfiguration": {"BaseUrl": "http://localhost:44376" // 改為http}
}
2.2.2 修改網關層啟動配置
在網關層的Program.cs
或Startup.cs
中,修改Kestrel配置為HTTP:
builder.WebHost.UseKestrel(options =>
{options.ListenAnyIP(44376, listenOptions =>{listenOptions.UseHttp(); // 關鍵修改:使用HTTP});
});
2.3 微服務層配置修改
2.3.1 修改微服務launchSettings.json
找到微服務的Properties/launchSettings.json
,修改端口為HTTP:
{"iisSettings": {"iisExpress": {"applicationUrl": "http://localhost:50383", // 改為http"sslPort": 0 // 移除HTTPS端口配置}},"profiles": {"ProductService": {"commandName": "Project","launchBrowser": true,"applicationUrl": "http://localhost:50383", // 改為http"environmentVariables": {"ASPNETCORE_ENVIRONMENT": "Development"}}}
}
2.3.2 修改微服務Kestrel配置
在微服務的Program.cs
中,修改Kestrel配置為HTTP:
builder.WebHost.UseKestrel(options =>
{options.ListenAnyIP(50383, listenOptions =>{listenOptions.UseHttp(); // 關鍵修改:使用HTTP});
});
2.3.3 修改微服務appsettings.json
將微服務的appsettings.json
中的SelfUrl
改為HTTP:
{"App": {"SelfUrl": "http://localhost:50383", // 改為http"ClientUrl": "http://localhost:4200","CorsOrigins": "http://*.ProductService.com,http://localhost:4200"},// 其他配置保持不變...
}
2.4 認證與授權配置調整
如果使用了認證授權服務,需要修改相關配置為HTTP:
// 網關層appsettings.json
{"AuthServer": {"Authority": "http://localhost:50399", // 改為http和認證服務的HTTP端口"RequireHttpsMetadata": false,"SwaggerClientId": "WebSite_Gateway_Swagger"},// 其他配置保持不變...
}
2.5 驗證HTTP配置
- 啟動微服務,確認控制臺輸出為HTTP端口:
Now listening on: http://localhost:50383
Application started.
- 使用Postman測試微服務接口:
http://localhost:50383/api/productservice/product
- 測試網關轉發:
http://localhost:44376/api/productservice/product
三、網關層依賴注入問題的正確解決方案
雖然注釋app.UseConfiguredEndpoints();
可以暫時解決空引用異常,但這不是正確的解決方案。正確的做法是修復依賴注入配置:
3.1 使用構造函數注入
public class ProductController : AbpController
{private readonly IProductService _productService;// 構造函數注入確保服務實例被正確初始化public ProductController(IProductService productService){_productService = productService;}[HttpGet]public async Task<PagedResultDto<ProductDto>> GetListAsync([FromQuery] PagedAndSortedResultRequestDto pagedAndSortedResultRequestDto){return await _productService.GetListAsync(pagedAndSortedResultRequestDto);}
}
3.2 注冊服務到依賴注入容器
在網關層模塊中注冊遠程服務代理:
public override void ConfigureServices(ServiceConfigurationContext context)
{// 注冊遠程服務代理context.Services.AddProxy<ProductService.Contracts.IProductService>(remoteServiceName: "ProductService",proxyUrl: "http://localhost:50383");
}
3.3 啟用路由系統
恢復app.UseConfiguredEndpoints();
并確保其在Ocelot之前調用:
app.UseRouting();
app.UseConfiguredEndpoints(); // 啟用路由系統
app.UseOcelot().Wait(); // 啟用Ocelot代理
四、總結與最佳實踐
4.1 問題解決思路總結
System.NullReferenceException
在網關層通常是由于依賴注入失敗導致的,注釋app.UseConfiguredEndpoints();
只是繞過了問題,而非解決問題- 將HTTPS改為HTTP可以簡化開發環境的配置,但生產環境應使用HTTPS保證安全
- 正確的解決方案是修復依賴注入配置,使用構造函數注入并正確注冊服務
4.2 開發環境最佳實踐
- 依賴注入:始終使用構造函數注入,避免屬性注入
- 路由配置:正確配置網關層和微服務的路由,確保請求正確轉發
- HTTPS/HTTP:開發環境可使用HTTP簡化配置,生產環境必須使用HTTPS
- 日志記錄:啟用詳細日志,便于問題排查
- 異常處理:添加全局異常處理,避免應用崩潰