C#泛型Web API控制器能否在Blazor客户端调用?如何实现?
泛型Web API控制器在Blazor客户端的调用方案
没问题,这个思路是可行的,但你的代码里有两个关键问题需要修正——一个是API端的泛型参数路由推断问题,另一个是客户端的重复反序列化错误,咱们一步步来解决:
首先,API端的核心问题:泛型参数无法被路由识别
你当前的WeatherForecastController里的泛型Get<TEntity>方法,ASP.NET Core根本无法正确路由到它——因为HTTP请求里没有任何信息能告诉服务器要把TEntity解析成哪个具体实体类型。所以这个方法其实永远不会被调用到,得先调整API的实现方式,这里给你两种常用方案:
方案1:通过路由参数传递实体类型,用反射处理
修改API控制器,添加实体类型名称作为路由参数,然后通过反射获取对应的仓储和数据:
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly IUowLookups UowLookups; public WeatherForecastController(IUowLookups uowLookups) { UowLookups = uowLookups; } [HttpGet("{entityTypeName}")] public IActionResult Get(string entityTypeName) { // 替换成你的实体所在的命名空间和程序集名称 var fullEntityTypeName = $"YourApp.Entities.{entityTypeName}, YourApp.Entities"; var entityType = Type.GetType(fullEntityTypeName); if (entityType == null || !typeof(Entity).IsAssignableFrom(entityType)) { return BadRequest("无效的实体类型"); } // 反射调用仓储的Repository和GetAll方法 var repositoryMethod = UowLookups.GetType().GetMethod("Repository")!; var repository = repositoryMethod.MakeGenericMethod(entityType).Invoke(UowLookups, null)!; var getAllMethod = repository.GetType().GetMethod("GetAll")!; var data = getAllMethod.Invoke(repository, null)!; return Ok(data); } }
方案2:创建泛型基控制器+具体实体控制器
这种方式更符合ASP.NET Core的路由规范,通过为每个实体创建具体控制器来实现泛型逻辑:
// 泛型基控制器,封装通用逻辑 [ApiController] [Route("api/[controller]")] public class GenericController<TEntity> : ControllerBase where TEntity : Entity { private readonly IUowLookups UowLookups; public GenericController(IUowLookups uowLookups) { UowLookups = uowLookups; } [HttpGet] public IEnumerable<TEntity> Get() { return UowLookups.Repository<TEntity>().GetAll(); } } // 针对WeatherForecast实体的具体控制器 public class WeatherForecastsController : GenericController<WeatherForecast> { public WeatherForecastsController(IUowLookups uowLookups) : base(uowLookups) { } } // 针对其他实体(比如Product)的具体控制器 public class ProductsController : GenericController<Product> { public ProductsController(IUowLookups uowLookups) : base(uowLookups) { } }
然后,修正Blazor客户端的代码
你原来的客户端代码犯了一个低级错误:HttpClient.GetFromJsonAsync本身就已经完成了JSON到对象的反序列化,不需要再嵌套调用JsonSerializer.DeserializeAsync。下面是对应两种API方案的客户端实现:
对应API方案1的客户端代码
// 确保httpClient已经在Program.cs中正确注册(比如配置了baseAddress为/api/) private readonly HttpClient _httpClient; public YourService(HttpClient httpClient) { _httpClient = httpClient; } public async Task<IEnumerable<TEntity>> Get<TEntity>() where TEntity : Entity { var entityTypeName = typeof(TEntity).Name; var apiUrl = $"weatherforecast/{entityTypeName}"; return await _httpClient.GetFromJsonAsync<IEnumerable<TEntity>>(apiUrl, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) ?? Enumerable.Empty<TEntity>(); }
对应API方案2的客户端代码
private readonly HttpClient _httpClient; public YourService(HttpClient httpClient) { _httpClient = httpClient; } public async Task<IEnumerable<TEntity>> Get<TEntity>() where TEntity : Entity { // 控制器名称通常是实体名称加复数(比如WeatherForecast → WeatherForecasts) var controllerName = $"{typeof(TEntity).Name}s"; var apiUrl = controllerName; return await _httpClient.GetFromJsonAsync<IEnumerable<TEntity>>(apiUrl, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) ?? Enumerable.Empty<TEntity>(); }
额外注意事项
- 确保API端和客户端的实体类完全一致(命名空间、属性名称/类型都要匹配),否则反序列化会失败或者丢失数据。
- 如果你的Blazor客户端是WebAssembly模式,要在
Program.cs中正确配置HttpClient的base地址:builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress + "api/") }); - 方案1的反射方式虽然灵活,但性能略低且不易调试;方案2更符合RESTful规范,维护性更好,推荐优先使用。
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

