Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0 Bonus: Swagger
Swashbuckle your API to OpenAPI goodness with a little Swagger
Part of the series: Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0
Alas, all things must come to an end. This is it: the bonus round of this Web API design series on ASP. NET Core. In case you missed any of the previous artfully crafted stories in this series, I present the full list for easy navigation:
- Intro and Content Negotiation
- HATEOAS
- Exceptions
- Concurrency
- Security
- Bonus (I told you weād make it here eventually)
I believe an easy way to understand Swaggeris to begin with a little history. In the dim recesses of the past, back in the days when JavaScript was considered annoying and didnāt run on servers, and everyone believed that Extensible Markup Language (XML) would change the world, there existed a complicated protocol named the Simple Object Access Protocol, or SOAP for short. Although there really wasnāt anything simple about SOAP, it paired well with the Web Services Description Language, or WSDL (we pronounce it wizz-dull).
The WSDL empowered tools to understand what SOAP services looked like, enabling what .NET developers refer to as the right-click experience. It works on all platforms (albeit, not by the same mechanism, as we know some people have those funny mouses with only one button): you simply point the right tool at the WSDL interface and it auto-generates the client code needed to connect to and interact with the service, including any data and models that are part of the definition. Pretty nifty!
The rise of smartphones and open source software completely transformed the landscape of web services. I imagine the first mobile phone tried to parse the XML returned by a WSDL call and threw up itās antennae in exasperation while crying, āEnough!ā Every phone has a web browser, and that means every phone has the ability to make HTTP/HTTPS requests and run JavaScript. That made it a simple choice to move to REST. Mobile trumped everything and soon developers all over the world tossed XML to the curb and increased their cool factor by hanging out with JSON.
Unfortunately, unless REST is implemented with HATEOAS, it doesnāt offer metadata or discovery services.
Enter the OpenAPIspecification. In a nutshell, and I quote directly from the repository:
The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for REST APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic. When properly defined via OpenAPI, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. Similar to what interface descriptions have done for lower-level programming, the OpenAPI Specification removes guesswork in calling a service.
This is great, but what it doesnāt do is remove guesswork from implementing the specification itself. Thatās when Swagger enters the picture:
Swagger is the worldās largest framework of API developer tools for the OpenAPI Specification(OAS), enabling development across the entire API lifecycle, from design and documentation, to test and deployment.
So there you have it: OpenAPI is the specification, and Swagger is a set of tools that facilitate the implementation. The tools turn out to be quite powerful. Iāve found there are fundamentally two approaches to implementing the specification via the tools. The first, like Test-Driven Development (TDD), involves designing the API first. You use a site like SwaggerHub to design your API, include your data models, expected return values, potential exceptions, authentication definitions, and more. Then you use additional tools that read the specification (which, coincidentally, is in a JSON format) and generate boilerplate server-side and client-side code.
Iāll leave that experience for you to try out.
Then thereās the developers who write their code first and throw in tests as an afterthought. You can do the same thing with Swagger, only the analogy breaks down because itās actually a valuable workflow to design your API in code and generate the specification dynamically. In other words, what Iām about to show is generally accepted in elite circles, whatever those might be. The good news is that it is incredibly easy to add these specifications to your legacy code.
You can dig into the specification yourself, but to summarize here are some simple pictures I created. I picked the color palette myself. Later Iāll show you the actual JSON and you will see the correlation between these images and the document. You might even slap your palm on your forehead and go, āWow!ā
OK, enough talk. Letās see some code! Starting with our simple ātodoā application, first I add the Swagger tool for ASP. NET Core, Swashbuckle.
dotnet add package Swashbuckle.AspNetCore
Next, add a using statement at the top of the Startup.cs
to bring in the new library:
using Swashbuckle.AspNetCore.Swagger;
Finally, add the generator to the services and configure the application to use Swagger and activate the Swagger UI (that will provide an interactive browser-based explorer).
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
services.AddMvc();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app)
{
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseMvc();
}
Thatās really all there is to it! Running the application performs exactly as before, with a few exceptions. First, you can access the Swagger UI by navigating to [http://localhost:5000/swagger](http://localhost:5000/swagger)
Not only do you get the list of API endpoints, you can expand individual ones and even test the API right from your browser! (Sorry, Postman).
If you navigate to the definition endpoint: [http://localhost:5000/swagger/v1/swagger.json](http://localhost:5000/swagger/v1/swagger.json)
ā¦you get the specification. This can be loaded to other Swagger tools that may then generate clients in multiple languages on various platforms. Pretty cool, no? Hereās what is generated by default from the ātodoā app. Uh, yeah. Itās a bit wordy.
{
"swagger": "2.0",
"info": {
"version": "v1",
"title": "My API"
},
"basePath": "/",
"paths": {
"/api/Todo": {
"get": {
"tags": [
"Todo"
],
"operationId": "ApiTodoGet",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/TodoItem"
}
}
}
}
},
"post": {
"tags": [
"Todo"
],
"operationId": "ApiTodoPost",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [],
"parameters": [
{
"name": "item",
"in": "body",
"required": false,
"schema": {
"$ref": "#/definitions/TodoItem"
}
}
],
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/api/Todo/{id}": {
"get": {
"tags": [
"Todo"
],
"operationId": "ApiTodoByIdGet",
"consumes": [],
"produces": [],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"200": {
"description": "Success"
}
}
},
"put": {
"tags": [
"Todo"
],
"operationId": "ApiTodoByIdPut",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int64"
},
{
"name": "item",
"in": "body",
"required": false,
"schema": {
"$ref": "#/definitions/TodoItem"
}
}
],
"responses": {
"200": {
"description": "Success"
}
}
},
"delete": {
"tags": [
"Todo"
],
"operationId": "ApiTodoByIdDelete",
"consumes": [],
"produces": [],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"200": {
"description": "Success"
}
}
}
}
},
"definitions": {
"TodoItem": {
"type": "object",
"properties": {
"id": {
"format": "int64",
"type": "integer"
},
"name": {
"type": "string"
},
"isComplete": {
"type": "boolean"
}
}
}
},
"securityDefinitions": {}
}
You can enhance your API definitions by providing attributes that describe more information such as additional response codes, and you can edit the auto-generated definition to improve its fidelity. Either way, I think youāll agree that Swagger is a great way to document your REST APIs and make them easier to discover and consume by clients. If āclientsā means internal applications, you can simply turn off the Swagger definition in production and use it during development.
And that, as they say, is a wrap.
In case you didnāt catch the rest of the series, hereās your easy navigation:
- Intro and Content Negotiation
- HATEOAS
- Exceptions
- Concurrency
- Security
- Bonus (what do you expect? This is itāāāthere is no more!)
Regards,
Part of the series: Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0
- Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0 Part 1: Content Negotiation
- Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0 Part 2: HATEOAS
- Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0 Part 3: Exceptions
- Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0 Part 4: Optimistic Concurrency
- Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0 Part 5: Security
- Five RESTFul Web Design Patterns Implemented in ASP.NET Core 2.0 Bonus: Swagger