You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 08:07:45