Ever found yourself staring at your ASP.NET MVC controller, trying to work out why a value you’re expecting from your Razor view isn’t being passed through?

Or perhaps you’ve found yourself trying different versions of that pesky HTML Helper tag to get the right markup that will submit form values but also render extra attributes like class or placeholder.

It’s all too easy to lose hours of your life tracking down which part of the puzzle your forgot to implement (ask me how I know!).

ASP.NET Core makes this easier, using something called Tag Helpers.

Enter the HTML Form

The typical way to handle user input is to implement a form in your web application which, when submitted, sends data to your application. Your app can then do whatever it needs to with that information (store it in a database, email it to someone etc.).

If you want to really bring this back to basics, you can just create a form using plain old html.

<form method="POST">
    <label for="firstName">Your first name</label>
    <input type="text" id="firstName" placeholder="Your name goes here" />
    <input type="submit" />
</form>

On first glance, this looks reasonable. We’ve got a single text box (firstName) and the form is going to send its data via an HTTP Post.

Unless we indicate otherwise, this will post back to the same location used to serve the form.

So, using default routing conventions, if we have a controller like this…

public class FormController : Controller
{
  [HttpGet]
  public IActionResult Index()
  {
      return View();
  }

  [HttpPost]
  public IActionResult Index(string firstName)
  {
      return Content($"Hello {firstName}");
  }    
}

Then we’d access this form via an HTTP Get request to http://<YourSiteHere>/form.

When you submit the form, it will be posted to the same address (http://<YourSiteHere>/form) (as an HTTP Post request).

But, we have a fatal flaw in our form! Run this, enter a name, submit the form and all you’ll get is a half-complete sentence.

For the value of the input to be submitted as form data, it needs to have a name attribute.

<input type="text" id="firstName" placeholder="Your name goes here" name="firstName" />

Add that and test your form again.

ASP.NET MVC spots the incoming “firstName” value (in the sumitted form data) and binds it to the firstName parameter we specified in the Index (POST) method on our controller.

Pro Tip

When you come across issues like this, it isn’t always obvious what’s causing the problem.

This is where the Chrome network tab comes into its own.

Submit your form with the developer tools open (F12) and you’ll see the form post in the network tab.

Click the relevant request and check out the Form Data section.

If you don’t see the values you expect then there’s something wrong with your form.

Enter the Tag Helper

So this brings us to ASP.NET Core and what it offers over and above the simple html form we started with.

You can use a Model and something called Tag Helpers to save you manually adding name attributes to all of your input fields.

First up, create a class to act as the model for our page.

public class FormModel
{
    public string FirstName { get; set; }
}

Now we can tweak our view (cshtml page) to use this model by adding a model declaration at the top.

@model FormBasics.Controllers.FormModel

Make sure you specify the correct namespace, where your model exists.

With that in place, we can start using tag helpers to do some of the manual plumbing (e.g. adding name attributes) we’d otherwise have to do ourselves.

<form asp-action="Index" asp-controller="Form">
    <label asp-for="FirstName"></label>
    <input asp-for="FirstName" placeholder="Your name goes here"/>
    <input type="submit" />
</form>

We’ve made a few changes here.

First we’ve explicitly defined the action and controller that this form will post back to, using asp-action and asp-controller. This isn’t strictly necessary, but it doesn’t hurt to be explicit, just in case we start moving things around, the form will continue to post to the correct controller action.

Secondly, because this page now knows about its model, we can use asp-for on our input and label elements, specifying the relevant property name from our model.

Here’s the resulting form.

And its rendered markup.

<form action="/Form" method="post">
    <label for="FirstName">First Name</label>
    <input placeholder="Your name goes here" type="text" id="FirstName" name="FirstName" value="">
    <input type="submit">
    <input name="__RequestVerificationToken" type="hidden" value="<token_generated_here>">
</form>

Note how ASP.NET has picked up that the rendered form should make a POST to /form and has automatically included the name attribute for the input field.

If you’re wondering what the __RequestVerificationToken is, this is a neat way to reduce the risk of your application being duped by a cross site request forgery attack.

You can (and should) read more about how ASP.NET prevents cross-site request forgery attacks and it’s good to know if you use ASP.NET Core’s tag helpers for your forms, the anti-forgery token is generated for you automatically.

I’ve got a model and I’m not afraid to use it

Now we’ve got a model we can get rid of that string parameter in the Index POST action and replace it with a FormModel parameter.

[HttpPost, ValidateAntiForgeryToken]
public IActionResult Index(FormModel model)
{
    return Content($"Hello {model.FirstName}");
}

ASP.NET MVC Core will bind the form data to your model automatically.

This also means we won’t find ourselves constantly updating the Index action every time we need to handle another value submitted via the form.

Incidentally, I’ve also added the other half of the Request Verification Token check here (the ValidateAntiForgeryToken attribute) to make sure this form has been posted from our site (and not a malicious site hosted by someeone else).

A better label

Finally, by default the asp-for tag helper on our label has used the name of the property for its value.

We can improve this and be explicit about what the label should say, with an attribute on our model.

public class FormModel
{
    [DisplayName("First Name")]
    public string FirstName { get; set; }
}

Admittedly, this form isn’t going to win any prizes for its design, but at least the label reads a little better!

What about HTML Helpers?

If you’ve used previous versions of MVC, you’re probably familiar with the HTML Helpers from previous versions. They look like this…

@Html.TextBox("firstname")

One huge benefit of Tag Helpers over HTML Helpers, is that they leave you free to define your markup using standard HTML tags. Rather than have some magic render the entire element for you, the tag helper extends your standard HTML element. This is evident in our example where we are free to add a placeholder attribute to the FirstName input without jumping through extra hoops to bend an HTML Helper to our will.

In Summary

ASP.NET Core tag helpers make it trivial to wire up your HTML forms to your MVC controllers whilst leaving you free to control your HTML Markup.

photo credit: jasleen_kaur obligation via photopin (license)

Want to learn .net Core?

  • Want to learn .NET Core but don't know where to start?
  • Don't have time to keep up with everything Microsoft is putting out?
  • Stuck on legacy apps when you want to build something new?
Enter your details below and get regular updates; learn how to build better .NET web apps.
comments powered by Disqus