Home OpenAPI Supported the Right Way
Post
Cancel

OpenAPI Supported the Right Way

The biggest problem with OpenAPI solutions in .Net is that they treat the OpenAPI Description document as a documentation artifact. They describe what the API supports, but that’s it.

JsonSchema.Net.Api v1.2.2 completely replaces existing solutions by generating schemas used to validate incoming requests and then using those same schemas when generating the OpenAPI Description document. This approach guarantees that the OpenAPI Description document always exactly matches validation.

Being social

A few years ago, I approached the Microsoft.OpenApi.Net team with the idea of updating to support OpenAPI 3.1, only to find they were already headed down that road. Since this upgrade would require full support of JSON Schema 2020-12, I recommended using JsonSchema.Net as their schema provider. However after some time, I found that their approach was to use my library as an internal provider rather than a first-class citizen on their models. In short, I felt that they were unnecessarily overcomplicating things.

In order to show them what I was thinking, I built Graeae: a new set of OpenAPI models explicitly built on my schema models. I ended up publishing it because I figured maybe someone would find it useful. But the OpenApi.Net team decided to keep with their approach. So Graeae sat as a prototype and small side project that I could occasionally play with.

Evolving JSON Schema support

Over the next few years, I continued working on JsonSchema.Net and its extensions. I was able to incorporate new features into the library suite Native AOT support, generating schemas from .Net types, both with reflection and later using source generation, and automatic request validation built directly into the ASP.Net pipeline.

JsonSchema.Net was refactored a couple times in order to improve the API and performance, and the json-everything website was updated.

At this point, I felt I had effective coverage of the JSON Schema space, and the libraries were nicely stable. I wanted to start working on supporting more places where JSON Schema was used.

Returning to OpenAPI

Ever since my interaction with the OpenApi.Net team, and especially after building validation into the ASP.Net pipeline, I wanted to see if I could build OpenAPI in a way that used the schemas that I was already generating.

The key was remembering Graeae. I already had models that were built on JsonSchema.Net. I should use those. They were a bit out of date because I hadn’t kept the models updated as the schema library evolved, but it wasn’t too difficult. Over several iterations, I was able to get a simple yet flexible API.

What’s in the box?

To add OpenAPI support, you just need to call a single extension method in your startup:

1
builder.Services.AddOpenApi();

This will generate an OpenAPI 3.1 Description document and serve it at /openapi.json and /openapi.yaml, serve a reference page at /openapi/reference, and configure for request validation.

The document is generated at compile time using a source generator, similar to how the schemas are generated. To accomplish this, the generator scans controllers and minimal API endpoints and pulls out the routes, parameters, request bodies, and responses. Summaries, descriptions, and tags are read from the ASP.Net attributes and .With*() extension methods, and from XML comments if those are present. Because there’s no reflection, this also works with Native AOT.

The schemas in the document are the same ones the validation uses, which means that any endpoint that has validation also gets a 400 response added to the OpenAPI Description document automatically.

The AddOpenApi() method also takes a configuration action lets you fully customize the OpenAPI Description as well as set up several other options. You can add servers, security schemes, contact info, or anything else that can’t be determined from the code. The full OpenAPI 3.1 model is supported. There are also options to write the document to a file on startup and to publish multiple documents split by controller.

Lastly, the reference page. Swagger’s UX is really outdated in my opinion. While other similar tools are more robust that Swagger, adding dependencies on many of them was overkill just to support a simple interactivity page, so I built one. The page that is published is inspired by the functionality of those other tools, and is fully stylable using your own CSS. Functionally, it lists the endpoints, shows the documentation and request schemas for each, and provides a console where you can build and send requests directly from the browser. It can also generate sample code for the request in several languages.

The docs cover all of the options in detail and include a complete example.

Leaving things better than you found them

My goal for this update is to set a new bar for what it means to support OpenAPI in .Net. Not only is it about describing what the API does, but it’s also about providing the functionality to ensure the documentation is accurate and then enforce its claims.

If you aren’t generating revenue, you like the work I put out, and you would still like to support the project, please consider becoming a sponsor!

This post is licensed under CC BY 4.0 by the author.

All the Error Messages

-