NiX
NiX

Reputation: 111

Web API routing actions

The goal is to allow these four endpoints:

POST    v1/invoices<br/>
POST    v1/invoices/12345<br/>
POST    v1/invoices/12345/attachment<br/>
POST    v1/invoices/12345/image

Routing entries:

routes.MapHttpRoute(
        name: "InvoiceAttachments",
        routeTemplate: "v1/invoices/{id}/attachments",
        defaults: new { controller = "invoices", action = "PostAttachment" }
    );

routes.MapHttpRoute(
        name: "InvoiceImages",
        routeTemplate: "v1/invoices/{id}/images",
        defaults: new { controller = "invoices", action = "PostImage" }
    );

These are my four function definitions in the controller:

[HttpPost]
[ActionName("PostAttachment")]
public HttpResponseMessage PostAttachment(int id)

[HttpPost]
[ActionName("PostImage")]
public HttpResponseMessage PostImage(int id)

[HttpPost]
public HttpResponseMessage Post(int id)    

[HttpPost]
public HttpResponseMessage Post()

Yet when I post an invoice using the first URI, the route that gets recognized is the attachments route. How do i have endpoints with different sections after the ID variable?

Upvotes: 3

Views: 5838

Answers (4)

NiX
NiX

Reputation: 111

Got it!

Route:

routes.MapHttpRoute(
        name: "InvoiceStuff",
        routeTemplate: "v1/invoices/{id}/{*action}",
        defaults: new { controller = "invoices", action = "" }
    );

Function definitions:

[HttpPost]
[ActionName("Attachments")]
public HttpResponseMessage Attachments([FromUri]int id)

[HttpPost]
[ActionName("Images")]
public HttpResponseMessage Images([FromUri]int id)

[HttpPost]
public HttpResponseMessage Post()

[HttpPost]
[ActionName("")]
public HttpResponseMessage Post([FromUri]int id)

Works perfectly. Note that in the route I specified a default action of "" because of a defect in webAPI routing with wildcards. Had i not done the default, a null reference exception would have been thrown. More information on the defect can be found here: http://aspnetwebstack.codeplex.com/workitem/718 - slated to be fixed in ASP.NET v5.

Thanks for all your help!

Upvotes: 7

Joanna Derks
Joanna Derks

Reputation: 4063

Have you tried using constraints rather than defaults?

routes.MapHttpRoute(
        name: "InvoiceAttachments",
        routeTemplate: "v1/invoices/{id}/attachments",
        defaults: new { controller = "invoices" },
        constraints: new { action = "PostAttachment" } 
    );

routes.MapHttpRoute(
        name: "InvoiceImages",
        routeTemplate: "v1/invoices/{id}/images",
        defaults: new { controller = "invoices" },
        constraints: new { action = "PostImage" } 
    );

routes.MapHttpRoute(
        name: "Default",
        routeTemplate: "v1/invoices/{id}",
        defaults: new { controller = "invoices" }
    );

UPDATE:

You could probably sort of hack it by making the action a url part:

routes.MapHttpRoute(
        name: "InvoiceAttachments",
        routeTemplate: "v1/invoices/{id}/{action}",
        defaults: new { controller = "invoices" },
        constraints: new {action = "attachments|images"}
    );

routes.MapHttpRoute(
            name: "default",
            routeTemplate: "v1/invoices/{id}",
            defaults: new { controller = "invoices", id = RouteParameter.Optional },
            constraints: new {action = "attachments|images"}
        );

and the controller methods:

[HttpPost]
[ActionName("attachments")]
public HttpResponseMessage PostAttachment(int id)

[HttpPost]
[ActionName("images")]
public HttpResponseMessage PostImage(int id)

public HttpResponseMessage Post(int id)

public HttpResponseMessage Post()

Upvotes: 0

oeoren
oeoren

Reputation: 336

Try having a single route

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}/{action}",
    defaults: new { id = RouteParameter.Optional, action = "DefaultAction"  }
);

Then decorate functions like

[HttpPost]
[ActionName("DefaultAction")]
public HttpResponseMessage Post()


[HttpPost]
public HttpResponseMessage PostImage(int id)

Upvotes: 0

matt-dot-net
matt-dot-net

Reputation: 4244

I think you must have another route defined, because the first URI should not be matching the InvoiceAttachments route, because it does not have "/attachments" on the end

Upvotes: 1

Related Questions