Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Is routing only supported at the controller level? Just trying to wrap my head around how to set up routing #1050

Unanswered
runxc1 asked this question in Q&A
Discussion options

I am just trying to get my head wrapped around the routing constraints that the versioning library places on a project. I looked through the wiki and didn't see a routing discussion though the Conventions page does show some of the conventions. What I'm wondering is if there is some way of putting the [Route] attribute on each method rather than solely on the Class so that I have full control of the route and am not limited to basicaly a PUT, POST, GET method in each controller.

I tried updating one of the examples that had Swagger configured and putting any routing anywhere but on the Controller made it so Swagger didn't show. As the project I'm working on is new I can probably create a Controller for every route that might be called but I'm used to grouping common logic in the same class and a lot of the methods would be very similar such as two different lookup methods one that returns all data fields and one that only returns a limited set in order to save bandwidth.

You must be logged in to vote

Replies: 2 comments · 1 reply

Comment options

Routing is largely unchanged. A key tenet has always been to not require developers to learn or do anything different for routing. What you know about routing should continue to work. The one small exception is if you version by URL segment. I strongly discourage that approach, but it's a popular method. When versioning by URL segment, you need to use the ApiVersionRouteConstraint in your route template (ex: v{version:apiVersion}/resource. API Versioning doesn't perform any type of magic string parsing or regex on templates.

You can version down to specific actions, but - personally - I always find that a bit strange. Most authors, think about an API as a collection of operations that can be performed. For example, the Orders API as opposed to the Get Order and Cancel Order API. To that end, a controller is a convenient and logical container to organize related operations in an API. If you want to version down to a specific action, you certain can. Controllers can be used, but Minimal APIs are intrinsically structured that way.

If you combine that with OpenAPI, it may look visually strange. Typically, you might have the Orders API with something like:

Orders (1.0)

  • Get Order - GET /order/{id}
  • Place Order - POST /order
  • Cancel Order - DELETE /order/{id}

Versioning down to an action will likely result in a flat list:

Get Order (1.0)

  • Get Order - GET /order/{id}

Place Order (1.0)

  • Place Order - POST /order

Cancel Order (1.0)

  • Cancel Order - DELETE /order/{id}

That may be what you want, but if not, be advised.

Conventions refer to how API versions are applied. API version information is just metadata that is bolted on. It can be applied by decorating attributes or by convention. Conventions can be applied through a fluent API or by a provided, well-known convention (ex: My.Api.V1.Controllers maps controllers to 1.0). You can also create your own conventions, which might be based on information that API Versioning does not natively understand. None of this affects routing. It only affects how the metadata is applied.

I'm happy to elaborate further or provide additional details.

You must be logged in to vote
0 replies
Comment options

I'm wondering if really what I'm running into here then is more of an issue with the Swagger configuration in the examples only being able to handle scenarios where the routing attribute is applied to the controller as the way that I was testing this was basically seeing if the Swagger UI popped up or not. Whenever I tried to add routing directly on a method it would give a 404 on the swagger page or one time I got an error stating there was no swagger file.

I was able to add the Route to the Controller file and add the action name there in order to get the swagger to generate and have more than one method with the same http verb. Below is the example of what worked going along with the Orders example you gave. Interleaving of API versions in the same controller doesn't work if you name something for example AddV2 but that kind of makes sense that it wouldn't.

Working example of more than one method per http verb with Swagger.

namespace Biz.Api.Controllers
{
    [ApiController]
    [Route("api/order/[action]")]
    [ApiVersion(1.0)]

    public class OrderController : ControllerBase
    {
        private readonly ILogger<OrderController> _logger;

        public OrderController(ILogger<OrderController> logger)
        {
            _logger = logger;
        }

        [HttpPost]
        public Example Add([FromBody] Example example)
        {
            example.Msg = "This is V1";
            return example;
        }
    }
}
namespace Biz.Api.Controllers2
{
    [ApiController]
    [Route("api/order/[action]")]
    [ApiVersion(2.0)]
    public class OrderController : ControllerBase
    {
        private readonly ILogger<OrderController> _logger;

        public OrderController(ILogger<OrderController> logger)
        {
            _logger = logger;
        }


        [HttpPost]
        public Example Add([FromBody] Example example)
        {
            example.Msg = "This is V2";
            return example;
        }

        [HttpPost]
        public Example UpdateStatus([FromBody] Example example)
        {
            example.Msg = "This is V2";
            return example;
        }

        [HttpPost]
        public Example Ship([FromBody] Example example)
        {
            example.Msg = "This is V2";
            return example;
        }

        [HttpPost]
        public Example Refund([FromBody] Example example)
        {
            example.Msg = "This is V2";
            return example;
        }
    }
}
public class Example
{
    public string? Msg { get; set; }
}
You must be logged in to vote
1 reply
@commonsensesoftware
Comment options

Hmmm... I've tried a couple of variants of your example and it seems to work fine.

namespace Biz.Api.Controllers.V1
{
    [ApiController]
    public class OrderController : ControllerBase
    {
        [HttpPost]
        [ApiVersion( 1.0 )]
        [Route( "api/[controller]/[action]" )]
        public Example Add( [FromBody] Example example )
        {
            example.Msg = "This is V1";
            return example;
        }
    }
}

namespace Biz.Api.Controllers.V2
{
    [ApiController]
    [ApiVersion( 2.0 )]
    public class OrderController : ControllerBase
    {
        [HttpPost]
        [Route( "api/[controller]/[action]" )]
        public Example Add( [FromBody] Example example )
        {
            example.Msg = "This is V2";
            return example;
        }

        [HttpPost]
        [Route( "api/[controller]/[action]" )]
        public Example UpdateStatus( [FromBody] Example example )
        {
            example.Msg = "This is V2";
            return example;
        }

        [HttpPost]
        [Route( "api/[controller]/[action]" )]
        public Example Ship( [FromBody] Example example )
        {
            example.Msg = "This is V2";
            return example;
        }

        [HttpPost]
        [Route( "api/[controller]/[action]" )]
        public Example Refund( [FromBody] Example example )
        {
            example.Msg = "This is V2";
            return example;
        }
    }
}

Could you share how you want it to look and what you expect to have happen?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
2 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.