As a developer advocate, I find myself writing a lot of demos for folks around .NET and, specifically, the ASP.NET Core parts of the stack. Unfortunately, writing repetitive boilerplate can make me feel like Jack Torrence from The Shining movie: “All work and no play makes Khalid a dull boy.” Nobody likes the drudgery of repeating yourself, nobody! So, given my situation, I thought I’d chase this problem down in a maze with a metaphorical axe. So, what am I trying to accomplish?
What if I could take an existing C# model and build a low-ceremony set of test endpoints that follow standard HTTP semantics, and how would I accomplish that?
In this article, you’ll see how I built a single registration for test endpoints that you can use to prototype UIs quickly for frameworks like React, Blazor, Angular, or vanilla JavaScript. Let’s get started.
The Basics of an HTTP API
For folks unfamiliar with building an HTTP API, the HTTP semantics are essential to the implementation on the server. Elements of an HTTP request include the method, headers, path, query-string values, and the optional payload. Without all these elements, your HTTP API limits how and what your user can send to your endpoint.
On the flip side, you have the HTTP Request, which has headers, payload, and status code elements. The server uses these values to tell a client what occurred on the server via a status code and what the client can expect regarding the type of payload.
In addition to HTTP semantics, I typically center the creation of HTTP APIs around a “resource”. A resource is a logical entity that a user reads and writes through requests and receives in response payloads. In my experience, an API resource can be something like a Person, Quotes, and so on.
I’ve found that building my HTTP APIs around HTTP semantics and resources makes the process of reasoning and maintaining a project simpler in the long run.
The ASP.NET Core Registration
We’ll start with the user experience I’m aiming for and then look at how to implement the code behind the endpoint registration. I aim to have a low-ceremony registration to add essential create, read, update, and delete endpoints. The endpoints will also adhere to standard HTTP semantics. Additionally, I want persistence between requests, so the user can create or update a new resource and then retrieve it using the identity. First, let’s look at the Program.cs
.
You’ll notice the call to MapAutoBogusEndpoint
allows the user to set a root path and the ability to configure the kind of data for the resource. Any property the user does not set will get random data based on the property type. For example, let’s make an HTTP call to the Index endpoint to retrieve a list of Person
resources, limiting the result to
That’s pretty cool, right?! Other calls like creating, updating, and deleting also work.
I make the best effort to assign the identifiers if an identifier needs to be incremented or generated. That said, in most cases, an HTTP API will expect that the user passes most data representing the state.
So how is this all done?
Generating Boilerplate Prototype Endpoints
I accomplish most of the work using the NuGet library AutoBogus. The configuration action allows you to configure each property accordingly when registering the endpoint. I’ll paste most of the code here.
All you need to do is create your resource types, such as Person,
and you’ll get all the necessary machinery for an HTTP API. But there’s still something missing. What about validation results?
Adding Validation to Bogus Endpoints
I made a calculated effort to return the RouteGroupBuilder
from each registration call, allowing you to add any endpoint filter to a group. Exposing this object instance makes it trivial to add FluentValidation as an endpoint filter.
We tie the validator specifically to the resource of Person
.
Any call to an endpoint not meeting your validation criteria will return an appropriate problem details response and status code.
Cool!
Other Ideas around Bogus Endpoints
This current implementation relies on runtime generation, which is ok for testing purposes, but there are a lot of potential pitfalls, primarily around identifiers. Using source code generators, you could make the endpoints rely less on reflection and more on the design-time types of the resource models. In addition, the use of source generators could lead to fewer edge case bugs.
.NET Community member João Antunes has an excellent blog post detailing registering Minimal API endpoints using source code generators. I could see someone adapting both our ideas into something useful for the community at large.
Conclusion
If you’ve ever found yourself in a situation where you needed some test endpoints to match your models but were more focused on the UI/UX rather than the implementation, this solution might be for you. The approach in this post could use some optimizations and edge case handling, but I’ll leave it up to you to work through those.
As always, thanks for reading, and I hope you enjoyed this post. If you did, please feel to share it with friends and colleagues.