在ABP微服务架构开发过程中,网关层启动后调用微服务接口时出现以下异常:
System.NullReferenceException: Object reference not set to an instance of an object.
异常堆栈指向网关层控制器中的某一行代码,通常是在调用微服务接口的位置。进一步分析发现,该异常是由于网关层控制器中依赖的服务接口实例为null导致的。
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); // 此处引发异常
}
}
在ABP框架中,app.UseConfiguredEndpoints();
方法用于注册和启用应用程序的路由系统,包括控制器路由的注册和依赖注入容器的初始化。当注释掉该方法后:
// 原启动配置
app.UseConfiguredEndpoints(); // 启用路由系统,会触发控制器依赖注入检查
app.UseOcelot().Wait(); // 启用Ocelot代理
// 修改后配置(注释掉UseConfiguredEndpoints)
// app.UseConfiguredEndpoints(); // 注释掉,不启用网关层路由系统
app.UseOcelot().Wait(); // Ocelot直接处理所有请求
这种解决方案本质上是绕过了问题而非解决了问题,存在以下隐患:
正确的解决方案应该是修复依赖注入配置,而不是绕过路由系统。
在开发环境中,HTTPS配置可能会遇到以下问题:
找到网关层的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
}
}
在网关层的Program.cs
或Startup.cs
中,修改Kestrel配置为HTTP:
builder.WebHost.UseKestrel(options =>
{
options.ListenAnyIP(44376, listenOptions =>
{
listenOptions.UseHttp(); // 关键修改:使用HTTP
});
});
找到微服务的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"
}
}
}
}
在微服务的Program.cs
中,修改Kestrel配置为HTTP:
builder.WebHost.UseKestrel(options =>
{
options.ListenAnyIP(50383, listenOptions =>
{
listenOptions.UseHttp(); // 关键修改:使用HTTP
});
});
将微服务的appsettings.json
中的SelfUrl
改为HTTP:
{
"App": {
"SelfUrl": "http://localhost:50383", // 改为http
"ClientUrl": "http://localhost:4200",
"CorsOrigins": "http://*.ProductService.com,http://localhost:4200"
},
// 其他配置保持不变...
}
如果使用了认证授权服务,需要修改相关配置为HTTP:
// 网关层appsettings.json
{
"AuthServer": {
"Authority": "http://localhost:50399", // 改为http和认证服务的HTTP端口
"RequireHttpsMetadata": false,
"SwaggerClientId": "WebSite_Gateway_Swagger"
},
// 其他配置保持不变...
}
Now listening on: http://localhost:50383
Application started.
http://localhost:50383/api/productservice/product
http://localhost:44376/api/productservice/product
虽然注释app.UseConfiguredEndpoints();
可以暂时解决空引用异常,但这不是正确的解决方案。正确的做法是修复依赖注入配置:
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);
}
}
在网关层模块中注册远程服务代理:
public override void ConfigureServices(ServiceConfigurationContext context)
{
// 注册远程服务代理
context.Services.AddProxy<ProductService.Contracts.IProductService>(
remoteServiceName: "ProductService",
proxyUrl: "http://localhost:50383"
);
}
恢复app.UseConfiguredEndpoints();
并确保其在Ocelot之前调用:
app.UseRouting();
app.UseConfiguredEndpoints(); // 启用路由系统
app.UseOcelot().Wait(); // 启用Ocelot代理
System.NullReferenceException
在网关层通常是由于依赖注入失败导致的,注释app.UseConfiguredEndpoints();
只是绕过了问题,而非解决问题