1

I have the following controller defined in a web api project:

[RoutePrefix("api/installations")]
public class InstallationsController : BaseController
{
    // GET all
    [Authorize]
    [Route("")]
    public async Task<IHttpActionResult> Get(){ /*...*/}

    // GET single
    [Authorize]
    [Route("{id:int}")]
    public async Task<IHttpActionResult> GetInstallation(int id){ /*...*/}

    //POST new
    [Authorize]
    [Route("")]
    public async Task<IHttpActionResult> PostInstallation(ViewModels.Installation installation){ /*...*/}

    //PUT update
    [Authorize]
    [Route("{id:int}")]
    public async Task<IHttpActionResult> PutInstallation(int id, ViewModels.Installation installation){ /*...*/}

    // DELETE
    [Authorize]
    [Route("{id:int}")]
    public async Task<IHttpActionResult> DeleteInstallation(int id){ /*...*/}

    [Authorize]
    [Route("{id:int}/newimage")]
    [HttpPut]
    public async Task<IHttpActionResult> PutNewImageUri(int installationId){ /*...*/}
}

ALL of the above routes works except the last one, where I basically want to do a PUT (I've tried POST as well without luck) to "api/installations/1/newimage" and get a link for uploading binary data to Blob Storage. My problem seems to be that any POST or PUT (and probably DELETE) to anything "after" the "{id:int}" field won't work. GET actually works fine, as I have this in another controller:

[RoutePrefix("api/customers")]
public class CustomersController : BaseController

    // GET related items
    [Authorize]
    [Route("{id:int}/{subitem}")]
    public async Task<IHttpActionResult> GetCustomerChild(int id, string subItem)

This will be called for GET requests to "api/customers/1/anystring", and it worked when I only had "/installations" instead of "/{subitem}" as a variable as well. Changing my PUT handler to "{id:int}/{imageAction}" doesn't work either.

In my WebApiConfig class I only have the following (no convention-based routing):

config.MapHttpAttributeRoutes();

From the browser I only get this response, for which I've found multiple similar questions, but no solution that fixes my problem:

{
    "message": "No HTTP resource was found that matches the request URI 'https://localhost:44370/api/installations/6/newimage'.",
    "messageDetail": "No action was found on the controller 'Installations' that matches the request."
}

I've started trying to debug using the code from this question: get a list of attribute route templates asp.net webapi 2.2

It looks like my route/function shows up int the list of RouteEntries, but still it doesn't seem like it's matched to my requests, and I can't find any good suggestions about how I can debug this further. Any pointers would be appreciated!

Community
  • 1
  • 1
Martin
  • 73
  • 2
  • 8
  • 1
    Is it because your action contains an argument for `installationId` but your route is configured with `id` so they don't match? – Phil Cooper Jan 18 '17 at 09:27
  • Oh god, of course it is.. And I had this exact same problem on another controller earlier, and I probably did the exact same mistake back then. This is my first question on StackOverflow, can I somehow mark your comment as the answer, or should I just delete this since the title is kind of unrelated to my actual problem? – Martin Jan 18 '17 at 10:00
  • I've added an answer with suggestions in case users come across this again. – Phil Cooper Jan 18 '17 at 11:21

1 Answers1

0

It's because your action contains an argument for installationId but your route is configured with id so they don't match.

Either change the route:

[Route("{installationId:int}/newimage")]

Or the signature:

public async Task<IHttpActionResult> PutNewImageUri(int id){ /*...*/}
Phil Cooper
  • 3,083
  • 39
  • 63
  • Thanks a lot, I didn't realize the parameter names had to match, thought only the order and/or types mattered. It works perfectly now! – Martin Jan 18 '17 at 12:54