5

I am curious why the ApiController handles default parameter values on actions differently than a 'regular' Controller.

This code works just fine, request to /Test means page gets value 1

public class TestController : Controller
{
    public ActionResult Index(int page = 1)
    {
        return View(page);
    }
}

This code doesn't work when a request is made to /api/Values. It fails with:

"The parameters dictionary contains a null entry for parameter 'page' of non-nullable type 'System.Int32' for method 'System.Collections.Generic.IEnumerable`1[System.String] Get(Int32)' in 'MvcApplication1.Controllers.Controllers.ValuesController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."

public class ValuesController : ApiController
{
    public IEnumerable<string> Get(int page = 1)
    {
        return new string[] { page.ToString() };
    }      
}

Any hints on why this is?

2 Answers 2

5

Try adding the [FromUri] or [FromForm] parameter attribute.

public class ValuesController : ApiController
{
    public IEnumerable<string> Get([FromUri]int page = 1)
    {
        return new string[] { page.ToString() };
    }      
}

Mike Stall has two good posts about parameter binding in Webapi which does not work as it does in ASP MVC. The big problem to get used to is that you can only read the request body once in your pipeline. So if you need to read more than 1 complex object as a parameter, you probably need to resort to ModelBinding by parameter. I had a problem similar to yours when I was reading the content body earlier in the pipeline for logging purposes and did not realize about the read once restriction above and had to solve with my own custom model binder.

Explains model binding at http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx and then suggests a way to make WebAPI model binding more like ASP MVC http://blogs.msdn.com/b/jmstall/archive/2012/04/18/mvc-style-parameter-binding-for-webapi.aspx

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, will read these blog posts, hopefully that will clear things up.
Seems like it's [FromUri] nowdays!
0

Try defining as Nullable<T>:

public class ValuesController : ApiController
{
    public IEnumerable<string> Get(int? page = 1)
    {
        return new string[] { page.ToString() };
    }      
}

1 Comment

I did solve it in a similar way. I was not looking for a workaround here, I wanted to know why the behavior was different between the two controller types. Thanks for the answer though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.