Introduction
More than often, developers test API, either through a browser request or using some clients such as POSTMAN, Advanced Rest Client (ARC). To expose the functionality of the API, we also tend to expose the methods and descriptions, and associated data structure through some means which require additional work. To complement either or both of these functionalities, Swagger becomes handy which provides API documentation and API testing by configuration.
Swagger is a set of open-source tools built around the OpenAPI specification that can be used to auto-generation documentation for API. Swagger UI is a tool that can be used across API lifecycle. Swagger provides easy to navigate documentation and/or visualization of API resources and enables interaction with API possible from within the application itself making the development and testing effort, as well as end-user experience seamlessly smooth. In this article, I am going to discuss how to implement swagger in API and exemplify some use cases of Swagger UI. I will be using .NET Core 2.0 Web API application and using Visual Studio 2017 IDE. I have created a sample API application with a single controller and four methods as part of the demo which is available for download.
Swagger offers the most powerful and easiest to use tools to take full advantage of the OpenAPI Specification.
Configuration
Wiring-up Swagger on an application is fairly minimal and can be accomplished in four easy steps, namely - installation, import, registration, and endpoint enablement.
The package can be installed in the Package Manager Console, or alternatively by going to the NuGet Package Manager menu.
Install-Package Swashbuckle.AspNetCore
Figure 1: Installing Swagger in the Package Manager Console.
Once installed, Swagger must be imported into Startup.cs.
using Swashbuckle.AspNetCore.Swagger;
Then the Swagger as service must be registered within ConfigureServices
method of Startup.cs.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(_version, new Info { Title = _applicationName, Version = _version });
});
Finally, the application must enable JSON as well as UI endpoints for swagger within Configure
method of Startup.cs so that end-users can interact with API methods through the Swagger
UI.
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/{_version}/swagger.json",${_applicationName} {_version}");
});
The complete list of Startup.cs file is shown in Snippet 1.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
namespace Api
{
public class Startup
{
private string _applicationName;
private string _version;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
var config = Configuration.GetSection("ApplicationSetting").Get<ApplicationSetting>();
_applicationName = config.Name;
_version = config.Version;
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(_version, new Info { Title = _applicationName, Version = _version });
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/{_version}/swagger.json", $"{_applicationName} {_version}");
});
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
Snippet 1: Content of Startup.cs
Security
The API is secured and it makes much more sense to secure the Swagger UI and its endpoints so that unauthorized users cannot see the API documentation via Swagger UI. In this case, the usual authentication mechanism must be used. For example, a WS-Federation based authentication can be used to redirect users for authentication via a single sign-on page. Assuming the WS-Federation like authentication is placed, the Configure
method must include the following code before 'app.UserSwagger();
' line of code.
app.UseAuthentication();
app.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated && context.Request.Path != "/signin-wsfed")
{
await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme);
}
else
{
await next();
}
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/{_version}/swagger.json", $"{_applicationName} {_version}");
});
In addition, the ConfigureServices
method must include the following code to trigger Authentication middleware related WsFederation
.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddWsFederation(options =>
{
options.Wtrealm = "ABC";
options.MetadataAddress = "XYZ";
});
With the authentication in place, the call to Swagger UI or Swagger JSON endpoint has to pass through the authentication middleware and thus, Swagger UI is only limited to rightful users.
Visualization
Upon completing the above four steps, swagger is ready to go. Browse through https://localhost:5001/swagger/v1/swagger.json to get the data in JSON (either in a browser or a client such as POSTMAN, Advanced Rest Client (ARC)). The returned JSON object provides the specification of the REST methods (separated by Controllers) and objects used in API. An example response is Snippet 2.
{
"swagger": "2.0",
"info": {
"version": "v1",
"title": "SmartStock API"
},
"paths": {
"/api/Stock": {
"get": {
"tags": [
"Stock"
],
"operationId": "GetStock",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [],
"responses": {
"200": {
"description": "Success",
"schema": {
"uniqueItems": false,
"type": "array",
"items": {
"$ref": "#/definitions/Stock"
}
}
}
}
},
"post": {
"tags": [
"Stock"
],
"operationId": "PostStock",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "stock",
"in": "body",
"required": false,
"schema": {
"$ref": "#/definitions/Stock"
}
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/Stock"
}
}
}
}
},
"/api/Stock/{symbol}": {
"get": {
"tags": [
"Stock"
],
"operationId": "GetStock",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "symbol",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"uniqueItems": false,
"type": "array",
"items": {
"$ref": "#/definitions/Stock"
}
}
}
}
}
},
"/api/Stock/{id}": {
"delete": {
"tags": [
"Stock"
],
"operationId": "DeleteStock",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string",
"format": "uuid"
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "boolean"
}
}
}
}
},
"/api/Values": {
"get": {
"tags": [
"Values"
],
"operationId": "Get",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [],
"responses": {
"200": {
"description": "Success",
"schema": {
"uniqueItems": false,
"type": "array",
"items": {
"type": "string"
}
}
}
},
"deprecated": true
},
"post": {
"tags": [
"Values"
],
"operationId": "Post",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [],
"parameters": [
{
"name": "value",
"in": "body",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success"
}
},
"deprecated": true
}
},
"/api/Values/{id}": {
"get": {
"tags": [
"Values"
],
"operationId": "Get",
"consumes": [],
"produces": [
"text/plain",
"application/json",
"text/json"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "string"
}
}
},
"deprecated": true
},
"put": {
"tags": [
"Values"
],
"operationId": "Put",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"produces": [],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "value",
"in": "body",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success"
}
},
"deprecated": true
},
"delete": {
"tags": [
"Values"
],
"operationId": "Delete",
"consumes": [],
"produces": [],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
}
],
"responses": {
"200": {
"description": "Success"
}
},
"deprecated": true
}
}
},
"definitions": {
"Stock": {
"type": "object",
"properties": {
"id": {
"format": "uuid",
"type": "string"
},
"ticker": {
"type": "string"
},
"company": {
"type": "string"
},
"price": {
"format": "double",
"type": "number"
}
}
}
}
}
Snippet 2: JSON Response Sample
The Swagger
UI is accessible by navigating to https://localhost:5001/swagger/index.html where a user can visualize the same data that was available in JSON response in an interactive format. This UI also supports the actual execution of the rest methods.
Figure 2: Visualizing API in Swagger UI
Testing
Swagger provides functionality to test the API methods without any tools. For example, clicking the GET
(first tab in Figure 2.) expands the method. By clicking 'Try it Out' and then 'Execute', swagger triggers a call to 'get' method to /api/stock. Note that there is a single controller named 'StockController
' in the demo application. The results are shown in the UI which can also be downloaded. Figure 3 shows a result screen for the get
method. Any method exposed in the UI can be executed in UI itself, thereby giving us the ability to test the API directly from the API itself.
Figure 3: Testing of API methods in Swagger UI
Support to Attributes
Routing attributes, Http*attributes, and any other data annotations are supported by default. Figure 4 shows how the methods decorated with HttpGet
, HttpPost
, HttpDelete
are reflected in the Swagger
UI.
Figure 4: Swagger UI reflecting HTTP attributes
Swagger
is in sync with attributes in .NET. For example, if a controller class is decorated with [Obsolete]
attribute, the UI reflects the fact that the controller is 'obsolete'. Figure 5 shows that the ValuesController
is marked 'Obsolete
' and Swagger
UI reflects the same and is not clickable. This feature becomes handy when API is phasing out certain functionality without breaking the working code.
Figure 5: Swagger UI reflecting the Obsolete attribute
Support XML Documentation
Swagger supports Documentation in UI with some configuration change. By adding the following lines of code in *.csproj file, the XML document is generated.
<Property Group>
....
<GenerateDocumentationFile>
true
</GenerateDocumentationFile>
<NoWarn>
$(NoWarn);1591
</NoWarn>
<Property Group>
With the following lines of code in the
Startup.cs, while registering, the UI shows the XML documentation.
services.AddSwaggerGen(c =>
{
...
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
}
);
With the change to use XML path, the Swagger exposes the XML document in the UI. Note that documentation for the code can be made using XML comments where such comments are placed directly before the code block. For example, following code block shows XML comment for GetStock() method which simply displays the description of 'This method returns a list of stocks' for this method when viewed in Swagger UI as shown in Figure 6:
<summary>[HttpGet] public IEnumerable<stock> GetStock()
{
return Data.DbContext.GetStocks();
}
Figure 6: Swagger UI Shows Documentation.
Swagger is supported in all major browsers and works in both local or on the web environment. The look and feel of the UI are customizable.
I showed previously how it works on a local machine. The Swagger
UI works well on the cloud. The same application deployed to Azure, https://smartstockapi.azurewebsites.net/swagger/index.html works as it worked in my local machine.
Figure 7: Swagger UI in Application Hosted in Cloud (Azure)
Resources/Materials/References