Why Bother with C#?
In my day job, I work with many APIs written in C#. Deployments involve running scripts that move DLLs to Windows servers running IIS. These servers have a large footprint and take a long time to initially provision. I think that the agility provided by being able to dockerize ASP.NET APIs could improve resource utilization, deployment times, and the ability to rapidly scale horizontally.
Since .NET Standard 2.0 and ASP.NET Core 2.0 were recently released, I decided to write a simple API using ASP.NET Core 2.0 and see if the dream of developing and deploying C# outside of Windows is finally a reality.
Example Project: Todo-Backend
Try it live here! Code available here.
The TodoMVC project exists to showcase front end technologies. It is a simple todo list application, typically using storage within the browser.
There is a backend version of the project called Todo-Backend, which provides a unit test page and a modified TodoMVC implementation that can target any backend that passes the tests.
Installing Boilerplate
To begin, I created a new webapi
project.
- Install .NET Core
- Run this:
mkdir dotnetcore-todo-webapi
cd dotnetcore-todo-webapi
dotnet new webapi
This creates a runnable example application that doesn’t do much, but it saves the time of writing the boilerplate configuration files.
Modeling the Data
To allow the API to store and work with to-do items, I had to define what a to-do is, as well as what a to-do repository needs. For this, I created the TodoWebApi.Data
namespace, which contains:
Writing the Controller
The controller was very simple to write, with one exception.
The Todo-Backend spec requires that each todo item contains a url
field. Without knowledge of the base URL, this proved to be a challenge. I ended up writing two mapping functions that use Request
.
For shipping in environments where load balancers terminate SSL/TLS, I added an environment variable that forces the returned URLs to use HTTPS. See Issue #1 for more details.
Wiring up Dependency Injection
Dependency injection is built into ASP.NET Core and was easy to wire up. To inject an implementation of ITodoRepository
, I just added two lines to ConfigureServices
in Startup.cs
.
var todoRepository = new TodoWebApi.Data.InMemory.TodoRepository();
services.AddSingleton(typeof(ITodoRepository), todoRepository);
The ITodoRepository
is then magically provided in the TodosController
constructor.
Fixing CORS
Since Todo-Backend directly calls APIs on different origins, CORS headers must be set up to allow any origin and method. They mention this in the instructions for creating a new implementation. This is done via app.UseCors
, which provides a CorsPolicyBuilder
.
CORS is checked by an OPTIONS
preflight request. Since the controller does not have any handlers for OPTIONS
, I wrote a middleware that returns an empty response for these requests:
private static void OptionsMiddleware(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("");
});
}
Extra Credit: Remove Null Json Properties
The default JSON serializer settings include null values, which seems unnecessary and could make responses larger. Removing null values is not a requirement for Todo-Backend, but it was really simple to configure in Startup.cs:
.AddJsonOptions(opt => {
opt.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});
Dockerize
I was able to use 17.05’s new multi-stage build feature to prevent a bloated build output. Microsoft published the microsoft/aspnetcore-build
and microsoft/aspnetcore
base images for dockerizing ASP.NET Core applications.
The only command that needs to be run in the -build
container is dotnet publish
, which restores dependencies, builds the project, and outputs DLLs to a specified folder.
Summary
The Todo-Backend project is a fun way to play with an unknown technology, and the kata-like test structure provides a nice path toward a complete implementation. However, I did find that many of the implementations do not handle PATCH
requests correctly, throwing away the existing data that is not in the body as though they were simply PUT
requests. This can likely be fixed by adding another test.
ASP.NET Core is a wonderful step forward for developers who are familiar with C# but don’t want the overhead of Windows and IIS. I was able to develop the entire API without even touching a Windows machine. The quick start template provides a just enough boilerplate to get started, and the ASP.NET Core documentation is pretty thorough. Getting everything built and deployed was fairly straightforward. I am excited to see what improvements will be made now that ASP.NET has been open-sourced and is in the hands of the community.