tokyo0709
tokyo0709

Reputation: 1997

Web Api Optional Parameters in the middle with attribute routing

So I'm testing some of my routing out with Postman and I can't seem to get this call to go through:

API Function

[RoutePrefix("api/Employees")]
public class CallsController : ApiController
{
    [HttpGet]
    [Route("{id:int?}/Calls/{callId:int?}")]
    public async Task<ApiResponse<object>> GetCall(int? id = null, int? callId = null)
    {
        var testRetrieve = id;
        var testRetrieve2 = callId;

        throw new NotImplementedException();
    }
}

Postman Requests

http://localhost:61941/api/Employees/Calls DOES NOT WORK

Error:

{
  "Message": "No HTTP resource was found that matches the request URI 'http://localhost:61941/api/Employees/Calls'.",
  "MessageDetail": "No action was found on the controller 'Employees' that matches the request."
}

http://localhost:61941/api/Employees/1/Calls WORKS

http://localhost:61941/api/Employees/1/Calls/1 WORKS

Any idea why I can't use an optional between my prefix and the custom route? I've tried combining them into one custom route and that doesn't change anything, any time I try to cut out the id it causes problems.

Upvotes: 11

Views: 26232

Answers (3)

Nkosi
Nkosi

Reputation: 247561

Optional parameters must be at the end of the route template. so what you are trying to do is not possible.

Attribute routing: Optional URI Parameters and Default Values

you either change your route template

[Route("Calls/{id:int?}/{callId:int?}")]

or create a new action

[RoutePrefix("api/Employees")]
public class CallsController : ApiController {

    //GET api/Employees/1/Calls
    //GET api/Employees/1/Calls/1
    [HttpGet]
    [Route("{id:int}/Calls/{callId:int?}")]
    public async Task<ApiResponse<object>> GetCall(int id, int? callId = null) {
        var testRetrieve = id;
        var testRetrieve2 = callId;

        throw new NotImplementedException();
    }

    //GET api/Employees/Calls
    [HttpGet]
    [Route("Calls")]
    public async Task<ApiResponse<object>> GetAllCalls() {
        throw new NotImplementedException();
    }
}

Upvotes: 15

Mostafiz
Mostafiz

Reputation: 7352

Actually you dont need to specify optional parameter in route

[Route("Calls")]

or you need to change the route

 [Route("Calls/{id:int?}/{callId:int?}")]
 public async Task<ApiResponse<object>> GetCall(int? id = null, int? callId = null)

Upvotes: 1

Martin Brandl
Martin Brandl

Reputation: 59021

I would change the Route to:

[Route("Calls/{id:int?}/{callId:int?}")]

and add the [FromUri] attribute to your parameters:

([FromUri]int? id = null, [FromUri]int? callId = null)

My test function looks like this:

[HttpGet]
[Route("Calls/{id:int?}/{callId:int?}")]
public async Task<IHttpActionResult> GetCall([FromUri]int? id = null, [FromUri]int? callId = null)
{
    var test = string.Format("id: {0} callid: {1}", id, callId);

    return Ok(test);
}

I can invoke it using:

https://localhost/WebApplication1/api/Employees/Calls
https://localhost/WebApplication1/api/Employees/Calls?id=3
https://localhost/WebApplication1/api/Employees/Calls?callid=2
https://localhost/WebApplication1/api/Employees/Calls?id=3&callid=2

Upvotes: 4

Related Questions