ASP.NET Core 控制器路由属性继承和抽象控制器动作 - AmbiguousActionException

7
我正在开发ASP.NET Core Webapi项目。我想实现一种基础/抽象的通用控制器,用于所有常见控制器的方法(例如CRUD方法),并在所有其他控制器中继承此控制器。 以下是示例代码:
public abstract class BaseApiController : Controller 
{
    [HttpGet]
    [Route("")]
    public virtual IActionResult GetAll() 
    {
        ...
    }

    [HttpGet]
    [Route("{id}")]
    public virtual IActionResult GetById(int id)
    {
        ...
    }

    [HttpPost]
    [Route("")]
    public virtual IActionResult Insert(myModel model)
    {
        ...
    }
}


[Route("api/Student")]
public class StudentController : BaseApiController 
{
    // Inherited endpoints:
    // GetAll method is available on api/Student [GET]
    // GetById method is available on api/Student/{id} [GET]
    // Insert method is available on api/Student [POST]
    //
    // Additional endpoints:
    // ShowNotes is available on api/Student/{id}/ShowNotes [GET]
    [HttpGet]
    [Route("{id}/ShowNotes")]
    public virtual IActionResult ShowNotes(int id) 
    {
        ...
    }
}

[Route("api/Teacher")]
public class TeacherController : BaseApiController 
{
    // Inherited endpoints:
    // GetAll method is available on api/Teacher [GET]
    // GetById method is available on api/Teacher/{id} [GET]
    // Insert method is available on api/Teacher [POST]
    //
    // Additional endpoints:
    // ShowHours is available on api/Teacher/{id}/ShowHours [GET]
    [HttpGet]
    [Route("{id}/ShowHours")]
    public virtual IActionResult ShowHours(int id) 
    {
        ...
    }
}

我曾在.NET Framework WebApi中见过这种解决方案,还有自定义的RouteProvider,例如:

public class WebApiCustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
    }
}

每次我尝试在派生控制器中访问端点时,都会出现AmbiguousActionException异常:

Multiple actions matched. The following actions matched route data and had all constraints satisfied:
XXX.WebApi.Controllers.CommonAppData.TeacherController.GetById
XXX.WebApi.Controllers.CommonAppData.StudentController.GetById

是否可以在.NET Core WebApi中创建这样的基本控制器? 我应该如何编写它以在派生控制器中不需要显式声明即可到达Action方法? 我应该如何配置此类解决方案?在Startup类中需要进行任何其他配置吗?


在.NET Core WebApi中有任何问题吗? - Roman Marusyk
你的代码在.NET Core WebAPI中运行良好,无需任何额外的自定义RouteProvider。问题出在哪里? - Roman Marusyk
1
我为每个派生端点收到AmbiguousActionException异常。 我的基础控制器是通用的,但为了更清晰的问题,我跳过了这部分。 - Piotr Trojan
你的代码再次正常工作了!或者你没有分享所有的代码,或者你需要删除 WebApiCustomDirectRouteProvider - Roman Marusyk
如果我没记错的话,当OpenApi / Swagger处于活动状态时,我看到过这个问题。但不确定。我想,有些东西Swagger会从类型中删除命名空间(可以禁用)。但不确定... - Christoph Lütjen
显示剩余4条评论
1个回答

1

由于这个主题很好,所以值得我解释一下我是如何做到的。您可以像这样创建一个通用控制器:

[Route("api/[controller]/[action]")]
[ApiController]
public class BaseController<TEntity> : Controller where TEntity : class, new()
{
    private readonly YourDbContext _db;
    
    public BaseController(YourDbContext db)
    {
        _db=db;
    }
    
    public virtual async Task<IActionResult> GetAll()
    {
        var data= await _db.Set<TEntity>().ToListAsync();
        return View(data);
    }
    
    public virtual IActionResult GetById(int id)
    {
        var data= await _db.Set<TEntity>().FindAsync(e=>e.Id == id);
        return View(data);
    }
    .....
    
}

然后你可以这样使用它:

public class StudentController : BaseController<Student> 
{
}

当然,你可以通过使用可选参数来更好地编写它,就像这样:

public virtual async Task<IActionResult> GetAll(Expression<Func<TEntity, bool>>? predicate = null)
{
    var data= await _db.Set<TEntity>().Where(predicate).ToListAsync();
    ....
}

甚至可以覆盖继承控制器中的每个操作,按照您的喜好添加必要的参数到您的操作中

public override Task<IActionResult> Get(....

希望你喜欢它 ;) 不要忘记 点赞

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接