Route Name for HttpGet attribute Name for base generic controller class in asp.net core 2

依然范特西╮ 提交于 2019-12-05 12:45:36

问题


I have a generic controller, which have several derived controller classes. but I cannot figure out how to handle the HttpGet's route name since it require constant.

[HttpGet("{id}", Name ="should not hard coded here for derived class")]
 public virtual async Task<IActionResult> Get(int id)

I need the route name because in my HttpPost function I want to return CreatedAtRoute() which require HttpGet's route name

The route name cannot be hard coded because all the derived class need to have a different route name.

here is the base controller

public abstract class BaseController<TEntity, TContext> : Controller where TEntity : BaseOptionType, new() where TContext : DbContext
{
    private readonly IGenericRepository<TEntity, TContext> _repository;
    private readonly ILogger<BaseGenericOptionTypesController<TEntity, TContext>> _logger;
    public BaseController(IGenericRepository<TEntity, TContext> repository, ILogger<BaseController<TEntity, TContext>> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpGet("{id}", Name = "should not hard code here for derived class")]
    public virtual async Task<IActionResult> Get(int id)
    {
        var optionType = await _repository.FindByIdAsync(id);
        if (optionType == null)
        {
            _logger.LogInformation($"[ID not found]");
            return NotFound();
        }
        return Ok(optionType);
    }
}

Here is the Derived Controller

[Route("api/v1/DerivedControllerA")]
public class DerivedControllerA : BaseController<TimeOff, HRContext>
{
    public DerivedControllerA(IGenericRepository<TimeOff, HRContext> repository, ILogger<DerivedControllerA> logger)
        : base(repository, logger)
    {

    }
}  

Any help would be appreciated, Thank you.


回答1:


I will not argue with NightOwl888 about use of base controllers in MVC. There are pros and cons, and I've dealt with the projects where use of base controllers was justified.

As regards original question, seems like the easiest way to get around this problem is to use CreatedAtAction instead of CreatedAtRoute. CreatedAtAction does not require you to name your routes, you could just use Get action name from base controller. If CreatedAtAction is called from DerivedControllerA, it will produce the URL for Get action in DerivedControllerA, and if it's called from DerivedControllerB, it will produce the URL for Get action in DerivedControllerB. So seems like shift to CreatedAtAction covers your use case pretty well.

Here is a sample call to CreatedAtAction:

[HttpPost]
public virtual IActionResult Post(/* ... */)
{
    //  Create and save an instance in repository
    //  var createdObject = ...;

    return CreatedAtAction(nameof(Get), new
    {
        //  Put actual id here
        id = 123
    }, createdObject);
}

The common mistake is to call overload of CreatedAtAction with 2 parameters. This version takes created object for response body, not the route values, which often results to No route matches the supplied values error. If you don't want to return representation of created resource in the response, you could pass null as 3rd parameter:

    return CreatedAtAction(nameof(Get), new
    {
        //  Put actual id here
        id = 123
    }, null);

If for some reason you want to stick with CreatedAtRoute call, the only possible solution that comes to my mind is to have distinct action in each derived class which just calls to base method with the actual logic:

[Route("api/v1/DerivedControllerA")]
public class DerivedControllerA : BaseController<TimeOff, HRContext>
{
    // ...

    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpGet("{id}", Name = "RouteForDerivedControllerA")]
    public virtual Task<IActionResult> Get(int id)
    {
        return base.Get(id);
    }
}

public abstract class BaseController<TEntity, TContext> : Controller where TEntity : BaseOptionType, new() where TContext : DbContext
{
    // ...

    public virtual async Task<IActionResult> Get(int id)
    {
        // Actual logic goes here
    }
}

Such solution however devalues use of BaseController in fact.



来源:https://stackoverflow.com/questions/48633939/route-name-for-httpget-attribute-name-for-base-generic-controller-class-in-asp-n

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!