Reputation: 6027
I am trying to pull an ID from the URL. My route looks like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
And the Action link and generated URL look like this:
<li>@Html.ActionLink((string)link.Name, "Video", new { id = link.ID } )</li>
http://localhost:57762/Video/3 // <-- generated URL
Then we get to the controller:
public ActionResult Video()
{
int id = Convert.ToInt32(RouteData.Values["id"].ToString());
var treatment = Treatment.Find(id);
ViewBag.Title = treatment.Name;
ViewBag.Treatment = treatment;
return View();
}
Here's the weird part:
The view renders everything correctly. I can access ViewBag.Treatment all day long and everything works as expected, but the second my browser finishes loading the view, Visual Studio throws this Exception:
An exception of type 'System.FormatException' occurred in mscorlib.dll but was not handled in user code
Additional information: Input string was not in a correct format.
It is complaining about this line:
int id = Convert.ToInt32(RouteData.Values["id"].ToString());
What am I doing wrong?
Upvotes: 0
Views: 2341
Reputation: 54628
In the controller you get to specify what the id
is:
public ActionResult Index(int id)
{
return View();
}
public ActionResult Index2(string id)
{
return View();
}
The model binder will convert the value automatically (or throw a yellow screen if it can't).
A couple of tips taken from 6 Tips for ASP.NET MVC Model Binding. (I'd like to copy the whole thing here, but really only tip one is relevant).
Tip #1: Prefer Binding Over Request.Form
If you are writing your actions like this ..
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create()
{
Recipe recipe = new Recipe();
recipe.Name = Request.Form["Name"];
// ...
return View();
}
.. then you are doing it all wrong. The model binder can save you from using the Request and HttpContext properties – those properties make the action harder to read and harder to test. One step up would be to use a FormCollection parameter instead:
public ActionResult Create(FormCollection values)
{
Recipe recipe = new Recipe();
recipe.Name = values["Name"];
// ...
return View();
}
With the FormCollection you don’t have to dig into the Request object, and sometimes you need this low level of control. But, if all of your data is in Request.Form, route data, or the URL query string, then you can let model binding work its magic:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Recipe newRecipe)
{
// ...
return View();
}
In this example, the model binder will create your newRecipe object and populate it with data it finds in the request (by matching up data with the recipe’s property names). It’s pure auto-magic. There are many ways to customize the binding process with “white lists”, “black lists”, prefixes, and marker interfaces. For more control over when the binding takes place you can use the UpdateModel and TryUpdateModel methods. Just beware of unintentional binding – see Justin Etheredge’s Think Before You Bind.
Upvotes: 4
Reputation: 100547
You want something like following if you really expect that parameter to be optional:
public ActionResult Video(int? id) ...
But it may be easier to have separate action for cases when you actually have id
and adjust routes accordingly.
Upvotes: 2
Reputation:
You could get the framework to do this for you with it's binding capabilities:
public ActionResult Video(int? id)
No need to extract id explicity from route data.
Upvotes: 2