If there’s one thing you’re guaranteed to encounter when building ASP.NET Core MVC applications, it’s forms.

There’s no avoiding them, sooner or later you’re going to need one or more forms to capture data from your users.

Thankfully ASP.NET Core has a few tricks up its sleeves to make implementing these forms easier.

One of those tricks is the “Tag Helper”.

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" />

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
  public IActionResult Index()
      return View();

  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 we can manually set up our forms to post the right data, correctly tagged so our MVC controllers know how to handle it.

But this seems like a bit of a faff (and is asking for trouble every time we rename anything either in the HTML or the C#).

Which brings us to ASP.NET Core’s Tag Helpers.

You can use 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" />

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>">

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.

*Pop your details in the form below to find out what request verification tokens are and why you need them.*

Want to know what else you can do with Tag Helpers?

Get this handy guide to some of the more common Tag Helpers, why they're needed and how to use them.

You'll get ASP.NET advice, guidance and useful tips from me once a week (or so). I respect your email privacy, unsubscribe at any time.

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…


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)

Just before you go, remember to check out this free chapter on Tag Helpers.

Want to know what else you can do with Tag Helpers?

Get this handy guide to some of the more common Tag Helpers, why they're needed and how to use them.

You'll get ASP.NET advice, guidance and useful tips from me once a week (or so). I respect your email privacy, unsubscribe at any time.