2

I have a generic abstract controller in my ASP.Net core Web Api project with POST handling:

 [HttpPost]
 public async Task<IActionResult> Create([FromBody]R itemDto)
 {
     var ret = await _service.CreateNewItemAsync(itemDto);
     return CreatedAtRoute("Get", new { id = ret.Id }, ret);
 }

The 'Get' route looks like this:

 [HttpGet("{id}", Name = "Get")]
 public async Task<R> GetAsync(int id)
 {
     var model = await _service.GetItemAsync(id);

     return model;
 }

I have 2 Controller classes which inherit from this Base Controller and when I try to make a simple get on one of these Controllers I get the following error message:

Error 1: Attribute routes with the same name 'Get' must have the same template: Action: 'MyApi.WebApi.Controllers.FooController.GetAsync' - Template: 'api/foo/{id}' Action: 'MyApi.WebApi.Controllers.BarController.GetAsync' - Template: 'api/bar/{id}'

It is very obvious why I get this error, but I don't know what to do to use a named route in a base controller.

Can anyone point me in the right direction?

Thomas Geulen
  • 590
  • 7
  • 22
  • 1
    I am having similar problem (https://stackoverflow.com/questions/48633939/route-name-for-httpget-attribute-name-for-base-generic-controller-class-in-asp-n), please let me know if you have your question solved. – HExit Feb 10 '18 at 07:27

2 Answers2

3

Parametrize BaseController logic with route name. Remove any routing logic from parent. Use a const string to parametrize attribute in child controller. Then pass this string to the parent:

public class BaseController : Controller
{
    //no routing attributes
    public IActionResult CreateImpl(string routeName)
    {
        //place any logic here
        return CreatedAtRoute(routeName, new object());
    }
}

[Route("api/[controller]")]
public class FooController : BaseController
{
    private const string _getRouteName = "Get_" + nameof(FooController);

    [HttpPost]
    public async Task<IActionResult> Create()
    {
        return CreateImpl(_getRouteName);
    }

    [HttpGet(Name = _getRouteName)]
    public override string Get()
    {
        return base.Get();
    }
}
Ilya Chumakov
  • 23,161
  • 9
  • 86
  • 114
  • 1
    Thanks for your answer. In your code you are placing Get and Post in the derived class. My code is having all the logic in the BaseController because to avoid code duplication. Is there any chance to avoid having the same code in every derived controller? – Thomas Geulen May 08 '17 at 16:04
  • Ok, I was too fast with reading your code. As far as I understand routing in asp.net core, I have no other chance to do it in this way. – Thomas Geulen May 08 '17 at 16:11
  • 1
    @ThomasGeulen, that's true, Get and Post don't contain any business logic. Child controller manages routing only. As we cann't declare more than one route with the same name, a child controller should declare its own route. – Ilya Chumakov May 08 '17 at 16:15
1

Use CreatedAtAction instead of CreateAtRoute, which will still return Status 201 and you do not need to worry about the route name. Thanks to @CodeFuller The is how I solved the same problem.

 return CreatedAtAction(nameof(GetAsync), new { id = 123 }, createObjectDto);

Please refer to the following link Route Name for HttpGet attribute Name for base generic controller class in asp.net core

HExit
  • 696
  • 7
  • 17