ASP.NET Core Routing and RouteBuilder.MapGet for Calculating a Factorial

Earlier I wrote about ASP.NET Core MVC from scratch as well as ASP.NET Core Routing as part of my learning the fundamentals of ASP.NET Core. I am developing these examples without Visual Studio. Instead, I am using Visual Studio Code on macOS.

In the ASP.NET Core Routing example I created an implementation of IRouter to handle Fibonacci number requests. This isn't the only way to handle incoming requests for a particular route. I could have used RouteBuilder.MapGet, which also accepts a template string, but instead of an IRouter, I can just just provide a RequestDelegate to handle the requests.

I am going to use RouteBuilder.MapGet to return a factorial very similar to how I used an impementation of IRouter to return Fibonaaci numbers.

dotnet CLI

First things first, I need to create a new directory for this ASP.NET Core Web Application, issue a few dotnet CLI commands to build the initial application framework, and open the application in Visual Studio Code.

mkdir FactorialAPP && CD $_

dotnet new
dotnet restore

code.

Project.json - ASP.NET Core

As you saw from the ASP.NET Core Routing and fibonacci tutorial, I need to add 2 dependencies to project.json: Microsoft.AspNetCore.Server.Kestrel and Microsoft.AspNetCore.Routing. Kestrel is the cross-platform web server and I need ASP.NET Core Routing to provide custom routing for incoming factorial requests. Once I add these new dependencies I execute another dotnet restore command from within Visual Studio Code to install the Nuget Packages.

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        },
        "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
        "Microsoft.AspNetCore.Routing": "1.0.0"
      },
      "imports": "dnxcore50"
    }
  }
}

Startup

All .NET Core applications are console applications. I need to instantiate a web host using WebHostBuilder. Kestrel is the web server and the Startup Class will be the entry point for the ASP.NET Core Web Application.

using Microsoft.AspNetCore.Hosting;

namespace FactorialApp {
    public class Program {
        public static void Main(string[] args) {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseStartup
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;

namespace FactorialApp {
    public class Startup {
        public void ConfigureServices(IServiceCollection services) {
            services.AddRouting();
        }

        public void Configure(IApplicationBuilder app) {

            var rb = new RouteBuilder(app);

            RequestDelegate factorialRequestHandler = c => {
                var number = c.GetRouteValue("number") as string;

                int value;
                if (Int32.TryParse(number, out value)) {
                    value = Math.Abs(value);
                    var results = Factorial.Calculate(value);
                    return c.Response.WriteAsync($"{number}! = {results}");
                }

                return c.Response.WriteAsync("${number} is not an integer.");
            };

            rb.MapGet("factorial/{number:int}", factorialRequestHandler);

            var routes = rb.Build();

            app.UseRouter(routes);
        }
    }
}

As I have mentioned before, the services.AddRouting() statement just adds ASP.NET Core Routing assets to the built-in dependency injection framework offered by ASP.NET Core.

The Configure Method is where I need to configure the new routes. The key in this example is the creation of a RequestDelegate, called factorialRequestHandler. This will handle all incoming requests to calculate a factorial.

This delegate is wired up to handle route requests using the RouteBuilder.MapGet method.

rb.MapGet("factorial/{number:int}", factorialRequestHandler);

I added a constraint on the route such that number must be an integer.

One can now request the factorial of a number using the following request.

http://localhost:5000/factorial/15

Factorial Calculation

The calculation of the factorial is a simple recursive function.

using System;

namespace FactorialApp {
    public static class Factorial {
        private static Int64 CalculateFactorial(int number) {
            if (number < 2)
                return 1;

            return number * CalculateFactorial(number - 1);
        }
        public static Int64 Calculate(int number) {
            return CalculateFactorial(number);
        }
    }
}

Running the ASP.NET Core Web Application

I run the application and request the factorial of 15.

dotnet run

Project FactorialApp (.NETCoreApp,Version=v1.0) will be compiled because Input items removed from last build
Compiling FactorialApp for .NETCoreApp,Version=v1.0

Compilation succeeded.
    0 Warning(s)
    0 Error(s)

Time elapsed 00:00:01.0155552
 

Hosting environment: Production
Content root path: /Users/Sasquatch/Projects/Core/FactorialApp
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

ASP.NET Core Routing Tutorial Calculating a Factorial

Conclusion

This is slick! I promised myself I would extend these examples in a couple of ways. I want to do this using Flask and Python, and I want to do this using ASP.NET Core Web API. I also want to write a simple Python client that requests several factorials. Lots of cool stuff on my list. I hope this tutorial is useful to you in your pursuit of knowledge!

Posted by David Hayden
Koder Dojo
David Hayden is a professional Microsoft web developer. He mentors and tutors computer science students in C, C++, Java, and Python. In addition to writing computer science and programming tutorials at Koder Dojo, he also writes tutorials on C#, ASP.NET Core, and Azure as well as tutorials on Orchard Core.