To finish off our routing series, we're going to spend a post looking at how to use ASP.NET Core 3.0 Anchor Tag Helpers to generate HTML anchor tags for MVC controllers and actions and Razor Pages, as well as how to access route data and query string parameters on the views and pages in our apps.

A large, lit question mark symbol, turned 90 degrees on its side.
I question, therefore I am? Photo by Jon Tyson / Unsplash

Let's get started!

Tag Helper Basics

Let's begin with a quick reminder of what Tag Helpers are.  If you have no idea what I'm talking about, check out a post I wrote a few years ago explaining what tag helpers do in ASP.NET Core.  The post was written for ASP.NET Core 1.0, but the ideas hold in modern versions.

Let's assume that we have the following convention-based route:

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
This is the default route generated with new ASP.NET Core 3.0 MVC projects.

Because this is the "landing" or "default" route for our application, the following URLs will match it:

/home/index/5
/home/index
/home
/

Tag Helpers, in ASP.NET Core 3.0, are classes which allow us to write HTML on our CSHTML pages and still use server-side information, such as actions, controllers, route data, etc. A basic anchor tag helper might look like this:

<a asp-area="" asp-controller="Home" asp-action="Index">Home</a>

Note the "asp-" prefixes. This prefix must be applied to any controller, route, area, page, route data, or other item that we expect ASP.NET 3.0 to handle the processing of. In this case, our link will be to the HomeController.Index() action.

The above tag helper generates the following HTML:

<a href="/">Home</a>

Note that the tag helper will generate the "simplest" (which is usually also the shortest) URL that will match the intended route.

Tag Helper Setup

Newly-generated .NET Core projects in Visual Studio include a file called _ViewImports.cshtml, which looks like this (example taken from the sample project):

@using RoutingAspNetCoreDemo.Endpoints
@using RoutingAspNetCoreDemo.Endpoints.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

The third line is what enables us to use tag helpers on our CSHTML views and pages. We don't need any other setup. But if you find yourself unable to use tag helpers, or having difficulty, ensure that this file exists and that particular line is in it.

Basic Routing with Anchor Tag Helpers

We can link to controllers and actions in our MVC and Razor Pages apps using "asp-controller" and "asp-action" attributes:

<a asp-controller="Home" asp-action="LinkGenerator">Link Generator</a>

We can also link to Razor Pages using "asp-page" attributes:

<a asp-page="/Index">Home</a>
Note the slash in this example; Razor Pages requires this to exist to redirect correctly.

This tag helper generates the following HTML:

<a href="/">Home</a>
Razor Pages assumes the page "Index.cshtml" is the "landing" page for the app, hence this URL.

Route Parameters

Tag Helpers also include a way for us to insert route parameters into our anchor tags. In this case, we use the "asp-route-" prefix. Let's say we have another convention-based route:

endpoints.MapControllerRoute("parameters", 
                             "parameters/{level}/{type}/{id}",
                             defaults: new { controller = "Home", action = "Parameters" });

This route expects a URL like the following:

/parameters/5/Test%20Name/19
Remember that %20 is a URL-encoded space.

We can generate that specific URL by specifying the route parameters in our anchor tag helper:

<a asp-route="parameters"
    asp-route-id="19"
    asp-route-type="Test Name"
    asp-route-level="5">Parameters</a>

Which generates this HTML:

<a href="/parameters/5/Test%20Name/19">Parameters</a>

Routing to Areas

If we are using Areas in our ASP.NET Core 3.0 applications, we can generate anchor links to controllers, actions, or pages in areas by using the "asp-area" attribute:

<a asp-area="blog" asp-controller="Post" asp-action="Article" asp-route-title="this-is-a-test-article">Article</a>

Which generates this HTML:

<a href="/blog/Post/Article?title=this-is-a-test-article">Article</a>

Routing to Named Routes

We can also use anchor tag helpers to route to a named route. Here's one example of such a route:

endpoints.MapControllerRoute("account", 
                             "account/{id?}",
                             defaults: new { controller = "Account", action = "Index" });

We can use the following tag helper to generate a link to the "/account/{id}" action, where the route value ID is 5:

<a asp-route="account" asp-route-id="5">Account</a>

This generates the corresponding HTML:

<a href="/account/5">Account</a>

Route Data vs Query String

ASP.NET Core 3.0 differentiates route data from query string data, and they are accessed in different ways.  Say we again have this route:

endpoints.MapControllerRoute("parameters", 
                             "parameters/{level}/{type}/{id}",
                             defaults: new { controller = "Home", action = "Parameters" });

To which the following URL is matched:

/parameters/18/title/7?postTitle=this-is-a-title

ASP.NET Core 3.0 would make the following route data:

  • Level = 18
  • Type = "title"
  • ID = 7

But the "postTitle" value is part of the query string and therefore not part of the route data.

Accessing Route Data

To access route data in MVC views, we can use ViewContext.RouteData.Values:

@{
    ViewData["Title"] = "Article";
}

<h1>Article</h1>

The slug for this article is @ViewContext.RouteData.Values["title"]
Note that if "title" does not exist in the route values, null will be returned

In Razor Pages, we can access route data by merely using RouteData.Values:

@page "/article/{id:int:required}/{**title:alpha}"
@model RoutingAspNetCoreDemo.RazorPages.Areas.Blog.Pages.ArticleModel
@{}

<h2>Article Page</h2>

<p>The ID is @RouteData.Values["id"] and the title is "@RouteData.Values["title"]"</p>

Accessing Query String Parameters

In ASP.NET Core 3.0 MVC, we can access query string parameters by using @Context.Request.Query:

<h1>Article</h1>

<p>The slug for this article is @Context.Request.Query["title"].ToString()</p>

In Razor Pages, the accessor is simpler: just @Request.Query:

<h1>Date Article</h1>

<p>The slug for the article is @Request.Query["title"]</p>

Summary

In both ASP.NET Core 3.0 MVC and Razor Pages, creating anchor tags and accessing route data and query string parameters is pretty straightforward. Remember the following:

  • Use "asp-controller", "asp-action", "asp-page", "asp-area", and "asp-route" attributes to route to the correct actions, routes, or pages.
  • Use "asp-route-" prefixed attributes to include specific data in the route values for the generated link.
  • Access route data by using @ViewContext.RouteData.Values[] (or RouteData.Values[] in Razor Pages).
  • Access query string parameters by using @Context.Request.Query[] (or Request.Query[] in Razor Pages).

Don't forget to check out the sample project over on GitHub! Also, stay tuned on Wednesday, because the very first post from Joe Middour, a writer in this blog's Guest Writer Program, will be published!

Happy Routing!