A Node.js Developer Tries .NET Again

Mon May 09 2022

I recently wrote a post about frustration I experienced when trying out .NET having worked with Node.js for the past several years. Turns out my Google-fu was off and I should have been searching for "ASP.NET Minimal APIs" which leads to this amazing document : https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0

All the fine grained control I am used to is right there! No laborious MVC to wade though, no more butting heads with razor pages, just plain GET and POST requests.

I wanted to see how hard it was to duplicate things I normally do with a simple express API:

  1. Can I use url path parameters in GET requests?
  2. Can I access the request and response objects?
  3. Can I process input from a POST request body?
  4. Can I write middleware for requests?
  5. Can I do an async web request and return the result within a handler?
  6. Can I serve static files?
  7. Can I render basic html templates?
  8. Can I add a swagger UI?

Yes! The answer to all of the above was yes! I was shocked. My experience of trying to get back into .NET had been like someone who normally goes wherever they want on a bicycle suddenly being restricted to traveling by train. Now I have a bike back!

Here are the details on the items above:

  1. Using request path parameters is as simple as adding {placeholders} to the path.
app.MapGet("/hello/{name}", (string name) => $"Hello {name}!");

2 and 3) Request and response objects are available via a HttpContext binding. Parsing of JSON body happens automatically via parameter binding.

app.MapPost("/thing", Thing (HttpContext context, Thing thang) => {
    Console.WriteLine(context.Request.Method);
    return thang;
});
  1. Middleware looks an awful lot like it does in express.
app.Use(async (context, next) =>
{
    Console.WriteLine("Halo Fren - I iz Middleware! " + context.Request.Path);
    context.Items.Add("user", "Doge");
    await next.Invoke();
});
  1. Async HTTP requests within a handler are nice and easy and automatically parse JSON.
app.MapGet("/proxy", async Task<Todo> (HttpContext context) => {
    Console.WriteLine("Middleware says I am " + (string)context.Items["user"]);
    var client = new HttpClient();
    var todo = await client.GetFromJsonAsync<Todo>("https://jsonplaceholder.typicode.com/todos/1");
    return todo;
}).WithTags("Proxy"); // Sets swagger tag
  1. Serving static files is a one-liner.
app.UseStaticFiles();
  1. To serve HTML I found the well maintained Handlebars.NET.
app.MapGet("/html", async context =>
{
    var source = System.IO.File.ReadAllText(@"./views/demo.html");
    var template = Handlebars.Compile(source);
    var data = new
    {
        title = "Demo Html",
        body = "This is super simple html!"
    };
    var result = template(data);
    await context.Response.WriteAsync(result);
});
  1. Swagger was also super easy to setup. I did have to re-write several of my handlers to add types for their input parameters and output. I even found how to setup groups (see code for #5 above). The full swagger setup is in the demo repo.

Bonus) After getting all this stuff working I noticed that the project template included a dockerfile. The dockerfile built and ran first try which was really fun to see.