Subscribe to our new YouTube channel for FREE videos on ASP.NET family of technologies. Click here for more details. Information about our instructor-led online courses is available here.

Handle Unknown Actions in ASP.NET Core MVC

While working with ASP.NET Core MVC, usually you have known number of actions and views. At times you might come across a situation where you need to deal with actions unknown at development time. Let me briefly explain a situation I recently stumbled upon.

There was an ASP.NET Core web application centered around electronic products. There was ProductsController that had Index() action as shown below:

public IActionResult Index()
{
    // some processing
    return View(model);
}

This is quite simple and straightforward. Now, each product has associated information pages such as a help page, technical specifications page, offers page, store addresses page and so on. What's more - the number of information pages for a product were unknown at the time of creating the controller. Somebody would create these pages / views at a later stage and they may get added or removed at any time depending on the company requirement.

Consider the following URLs :

/Home/Index
/Home/Help
/Home/Specifications
/Home/Stores
/Home/Offers

From the URLs it appears that the HomeController will have five actions namely Index, Help, Specifications, Stores, and Offers. In reality Index() is the only action whereas others are mere information pages to be rendered in the browser. What if an information page gets added or removed? Obviously, you need to add or remove an action to take care of the information page under consideration.

Luckily, ASP.NET Core routing allows you to handle the situation easily. Let's see how.

Begin by creating a new ASP.NET Core MVC application and add HomeController as you normally do. Also add the Index() action as shown above. You will also need three .cshtml files - Index.cshtml, Help.cshtml, Specifications.cshtml - for testing purpose.

Now, open Startup.cs and go to Configure() method. Write the following code to define the routing:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    endpoints.MapControllerRoute(
        name: "infoPage",
        pattern: "{controller}/{*infoPage}",
        defaults: new { action = "ShowInfoPage" });
});

Notice the second route definition. It contains the catch all *infoPage route parameter. This way anything other than Index will be handled by ShowInfoPage() action. The ShowInfoPage() looks like this:

public IActionResult ShowInfoPage(string infoPage)
{
    return View(infoPage);
}

As you can see the ShowInfoPage() accepts the infoPage route parameter. In our example this parameter will be information page name such as Help, Specifications, and Stores. Inside, we simply return a view file that matches the infoPage name. Alternatively, you can use inforPage parameter to dynamically decide which page is to be displayed.

With the above routing configuration in place, the first URL (/Home/Index) will be handled by the Index() action whereas all the other URLs will reach ShowInfoPage() action. The ShowInfoPage() will return the specified page to the browser. Of course, if a page doesn't exist an error will be raised.

The following figure shows a set of such "pages" residing in the Views > Home folder.

The same requirement can be accomplished using attribute routing as follows:

[Route("[controller]")]
public class HomeController : Controller
{
    [Route("[action]")]
    public IActionResult Index()
    {
        return View();
    }

    [HttpGet("{infoPage}")]
    public IActionResult ShowInfoPage(string infoPage)
    {
       return View(infoPage);
    }
}

In this case the first URL will be mapped to the Index() action. All the other GET URLs are mapped to ShowInfoPage() action. The route parameter infoPage specifies the page name (Help, Specifications etc.).

Also modify the UseEndPoints() call to resemble this:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

The MapControllers() ensures that attribute based routing is taken into account while building the routes.

In the above examples, you used the technique to deal with dynamically varying information pages. However, you can use the technique to deal with any action that is unknown at development time.

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant, trainer, author, yoga mentor, and meditation teacher. He has been programming, meditating, and teaching for 24+ years. He conducts instructor-led online training courses in ASP.NET family of technologies for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get article updates : Facebook  Twitter  LinkedIn  YouTube

Posted On : 02 March 2020


Tags : ASP.NET ASP.NET Core MVC C# Visual Studio Configuration