Search code examples
c#asp.net-mvcasp.net-coremodel-view-controller

Is there a way to create route groups in an aspnet controller?


I want to know if there's a way to create subclasses that act as a routing group for an api using AspNetCore Mvc.

These are the routes I need to use.

api/speak api/dance/tango api/dance/salsa api/sing/song api/sing/tune

[Route("api")]
[ApiController]
public class ApiController: Controller Base {
    private IMyService myService;
    
    public ApiController(IMyService myService) {
        this.myService = myService;
    }

    [Route("dance")] // This is what I want to do, create a sub-route "group"
    public class Dance {
        [HttpGet("tango")]
        public async Task<IActionResult> Tango() {
            return Okay(myService.TangoDance());
        }

        [HttpGet("salsa")]
        public async Task<IActionResult> Salsa() {
            return Okay(myService.alsaDance());
        }
    }

    [Route("sing")] // And create a second sub-route "group" in the controller
    public class Dance {
        [HttpGet("song")]
        public async Task<IActionResult> Song() {
            return Okay(myService.SingSong());
        }

        [HttpGet("tune")]
        public async Task<IActionResult> Tune() {
            return Okay(myService.SingTune());
        }
    }

    [HttpGet("speak")]
    public async Task<IActionResult> Speak() {
        return Okay(myService.Words());
    }
}

In the code above myService doesn't work in the subclasses. I used that code as I felt it explained best what I want to accomplish. (I had the subclasses inherit ApiController so that I could use myService inside those methods but that didn't fix the routes not working.)

The reason I'm on stack overflow is because I can't get this to work, and I can't find anything online.

I'm using Swagger to test the routes, that's how I can see that they're not showing up.

All help is greatly appreciated.


Solution

  • These would be separate controllers. There's a good argument for doing it this way (idiomatically), anyone with experience would instantly recognise the pattern and know exactly how things work.

    You would use a base controller to capture the service dependency:

    [ApiController]
    public abstract class MyBaseController: ControllerBase {
        protected IMyService myService;
        
        public MyBaseController(IMyService myService) {
            this.myService = myService;
        }
    }
    

    And then implement the controllers like so, passing the (injected) dependency down to your base controller:

    [Route("api/[controller]")]
    public class SpeakController: MyBaseController {
        public SpeakController(IMyService myService) : base(myService)
        {
        }
    
        [HttpGet] // GET api/speak
        public async Task<IActionResult> Index()
        {
            return Okay(myService.Words());       
        }
    }
    
    [Route("api/[controller]")]
    public class DanceController: MyBaseController {
        public DanceController(IMyService myService) : base(myService)
        {
        }
    
        [HttpGet] // GET api/dance/tango
        public async Task<IActionResult> Tango() {
            return Okay(myService.TangoDance());
        }
    
        [HttpGet] // GET api/dance/salsa
        public async Task<IActionResult> Salsa() {
            return Okay(myService.alsaDance());
        }
    }
    
    [Route("api/[controller]")]
    public class SingController: MyBaseController {
        public SingController(IMyService myService) : base(myService)
        {
        }
    
        [HttpGet] // GET api/sing/song
        public async Task<IActionResult> Song() {
            return Okay(myService.SingSong());
        }
    
        [HttpGet] // GET api/sing/tune
        public async Task<IActionResult> Tune() {
            return Okay(myService.SingTune());
        }
    }