"
 
 
 
ASP.NET (snapshot 2017) Microsoft documentation and samples

ASP.NET Core Web API Help Pages using Swagger

By Shayne Boyer and Scott Addie

Understanding the various methods of an API can be a challenge for a developer when building a consuming application.

Generating good documentation and help pages for your Web API, using Swagger with the .NET Core implementation Swashbuckle.AspNetCore, is as easy as adding a couple of NuGet packages and modifying the Startup.cs.

This tutorial builds on the sample on (xref:)Building Your First Web API with ASP.NET Core MVC and Visual Studio. If you’d like to follow along, download the sample at https://github.com/aspnet/Docs/tree/master/aspnetcore/tutorials/first-web-api/sample.

Getting Started

There are three main components to Swashbuckle:

NuGet Packages

Swashbuckle can be added with the following approaches:

Visual Studio

Visual Studio for Mac

Visual Studio Code

Run the following command from the Integrated Terminal:

dotnet add TodoApi.csproj package Swashbuckle.AspNetCore

.NET Core CLI

Run the following command:

dotnet add TodoApi.csproj package Swashbuckle.AspNetCore

Add and configure Swagger to the middleware

Add the Swagger generator to the services collection in the ConfigureServices method of Startup.cs:

[!code-csharpMain]

   1:  using System.IO;
   2:  using Microsoft.AspNetCore.Builder;
   3:  using Microsoft.EntityFrameworkCore;
   4:  using Microsoft.Extensions.DependencyInjection;
   5:  using Microsoft.Extensions.PlatformAbstractions;
   6:  using Swashbuckle.AspNetCore.Swagger;
   7:  using TodoApi.Models;
   8:   
   9:  namespace TodoApi
  10:  {
  11:      public class Startup2
  12:      {
  13:          #region snippet_ConfigureServices
  14:          public void ConfigureServices(IServiceCollection services)
  15:          {
  16:              services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
  17:              services.AddMvc();
  18:   
  19:              // Register the Swagger generator, defining one or more Swagger documents
  20:              services.AddSwaggerGen(c =>
  21:              {
  22:                  c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
  23:              });
  24:          }
  25:          #endregion
  26:   
  27:          #region snippet_Configure
  28:          public void Configure(IApplicationBuilder app)
  29:          {
  30:              // Enable middleware to serve generated Swagger as a JSON endpoint.
  31:              app.UseSwagger();
  32:   
  33:              // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
  34:              app.UseSwaggerUI(c =>
  35:              {
  36:                  c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
  37:              });
  38:   
  39:              app.UseMvc();
  40:          }
  41:          #endregion
  42:      }
  43:  }

Add the following using statement for the Info class:

In the Configure method of Startup.cs, enable the middleware for serving the generated JSON document and the SwaggerUI:

[!code-csharpMain]

   1:  using System.IO;
   2:  using Microsoft.AspNetCore.Builder;
   3:  using Microsoft.EntityFrameworkCore;
   4:  using Microsoft.Extensions.DependencyInjection;
   5:  using Microsoft.Extensions.PlatformAbstractions;
   6:  using Swashbuckle.AspNetCore.Swagger;
   7:  using TodoApi.Models;
   8:   
   9:  namespace TodoApi
  10:  {
  11:      public class Startup2
  12:      {
  13:          #region snippet_ConfigureServices
  14:          public void ConfigureServices(IServiceCollection services)
  15:          {
  16:              services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
  17:              services.AddMvc();
  18:   
  19:              // Register the Swagger generator, defining one or more Swagger documents
  20:              services.AddSwaggerGen(c =>
  21:              {
  22:                  c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
  23:              });
  24:          }
  25:          #endregion
  26:   
  27:          #region snippet_Configure
  28:          public void Configure(IApplicationBuilder app)
  29:          {
  30:              // Enable middleware to serve generated Swagger as a JSON endpoint.
  31:              app.UseSwagger();
  32:   
  33:              // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
  34:              app.UseSwaggerUI(c =>
  35:              {
  36:                  c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
  37:              });
  38:   
  39:              app.UseMvc();
  40:          }
  41:          #endregion
  42:      }
  43:  }

Launch the app, and navigate to http://localhost:<random_port>/swagger/v1/swagger.json. The generated document describing the endpoints appears.

Note: Microsoft Edge, Google Chrome, and Firefox display JSON documents natively. There are extensions for Chrome that format the document for easier reading. The following example is reduced for brevity.

This document drives the Swagger UI, which can be viewed by navigating to http://localhost:<random_port>/swagger:

Swagger UI
Swagger UI

Each public action method in TodoController can be tested from the UI. Click a method name to expand the section. Add any necessary parameters, and click “Try it out!”.

Example Swagger GET test
Example Swagger GET test

Customization & Extensibility

Swagger provides options for documenting the object model and customizing the UI to match your theme.

API Info and Description

The configuration action passed to the AddSwaggerGen method can be used to add information such as the author, license, and description:

[!code-csharpMain]

   1:  using System.IO;
   2:  using Microsoft.AspNetCore.Builder;
   3:  using Microsoft.EntityFrameworkCore;
   4:  using Microsoft.Extensions.DependencyInjection;
   5:  using Microsoft.Extensions.PlatformAbstractions;
   6:  using Swashbuckle.AspNetCore.Swagger;
   7:  using TodoApi.Models;
   8:   
   9:  namespace TodoApi
  10:  {
  11:      public class Startup
  12:      {       
  13:          #region snippet_ConfigureServices
  14:          public void ConfigureServices(IServiceCollection services)
  15:          {
  16:              services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
  17:              services.AddMvc();
  18:   
  19:              // Register the Swagger generator, defining one or more Swagger documents
  20:              services.AddSwaggerGen(c =>
  21:              {
  22:                  c.SwaggerDoc("v1", new Info
  23:                  {
  24:                      Version = "v1",
  25:                      Title = "ToDo API",
  26:                      Description = "A simple example ASP.NET Core Web API",
  27:                      TermsOfService = "None",
  28:                      Contact = new Contact { Name = "Shayne Boyer", Email = "", Url = "https://twitter.com/spboyer" },
  29:                      License = new License { Name = "Use under LICX", Url = "https://example.com/license" }
  30:                  });
  31:   
  32:                  // Set the comments path for the Swagger JSON and UI.
  33:                  var basePath = PlatformServices.Default.Application.ApplicationBasePath;
  34:                  var xmlPath = Path.Combine(basePath, "TodoApi.xml"); 
  35:                  c.IncludeXmlComments(xmlPath);                
  36:              });
  37:          }
  38:          #endregion
  39:   
  40:          #region snippet_Configure
  41:          public void Configure(IApplicationBuilder app)
  42:          {
  43:              app.UseStaticFiles();
  44:   
  45:              // Enable middleware to serve generated Swagger as a JSON endpoint.
  46:              app.UseSwagger();
  47:   
  48:              // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
  49:              app.UseSwaggerUI(c =>
  50:              {
  51:                  c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
  52:              });
  53:   
  54:              app.UseMvc();
  55:          }
  56:          #endregion
  57:      }
  58:  }

The following image depicts the Swagger UI displaying the version information:

Swagger UI with version information: description, author, and see more link
Swagger UI with version information: description, author, and see more link

XML Comments

XML comments can be enabled with the following approaches:

Visual Studio

Build tab of project properties
Build tab of project properties

Visual Studio for Mac

General Options section of project options
General Options section of project options

Visual Studio Code

Manually add the following snippet to the .csproj file:

[!code-xmlMain]

   1:  <Project Sdk="Microsoft.NET.Sdk.Web">
   2:   
   3:    <PropertyGroup>
   4:      <TargetFramework>netcoreapp2.0</TargetFramework>
   5:    </PropertyGroup>
   6:   
   7:    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
   8:      <DocumentationFile>bin\Debug\netcoreapp2.0\TodoApi.xml</DocumentationFile>
   9:    </PropertyGroup>
  10:   
  11:    <ItemGroup>
  12:      <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
  13:      <PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
  14:    </ItemGroup>
  15:   
  16:  </Project>


Configure Swagger to use the generated XML file. For Linux or non-Windows operating systems, file names and paths can be case sensitive. For example, a ToDoApi.XML file would be found on Windows but not CentOS.

[!code-csharpMain]

   1:  using System.IO;
   2:  using Microsoft.AspNetCore.Builder;
   3:  using Microsoft.EntityFrameworkCore;
   4:  using Microsoft.Extensions.DependencyInjection;
   5:  using Microsoft.Extensions.PlatformAbstractions;
   6:  using Swashbuckle.AspNetCore.Swagger;
   7:  using TodoApi.Models;
   8:   
   9:  namespace TodoApi
  10:  {
  11:      public class Startup
  12:      {       
  13:          #region snippet_ConfigureServices
  14:          public void ConfigureServices(IServiceCollection services)
  15:          {
  16:              services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
  17:              services.AddMvc();
  18:   
  19:              // Register the Swagger generator, defining one or more Swagger documents
  20:              services.AddSwaggerGen(c =>
  21:              {
  22:                  c.SwaggerDoc("v1", new Info
  23:                  {
  24:                      Version = "v1",
  25:                      Title = "ToDo API",
  26:                      Description = "A simple example ASP.NET Core Web API",
  27:                      TermsOfService = "None",
  28:                      Contact = new Contact { Name = "Shayne Boyer", Email = "", Url = "https://twitter.com/spboyer" },
  29:                      License = new License { Name = "Use under LICX", Url = "https://example.com/license" }
  30:                  });
  31:   
  32:                  // Set the comments path for the Swagger JSON and UI.
  33:                  var basePath = PlatformServices.Default.Application.ApplicationBasePath;
  34:                  var xmlPath = Path.Combine(basePath, "TodoApi.xml"); 
  35:                  c.IncludeXmlComments(xmlPath);                
  36:              });
  37:          }
  38:          #endregion
  39:   
  40:          #region snippet_Configure
  41:          public void Configure(IApplicationBuilder app)
  42:          {
  43:              app.UseStaticFiles();
  44:   
  45:              // Enable middleware to serve generated Swagger as a JSON endpoint.
  46:              app.UseSwagger();
  47:   
  48:              // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
  49:              app.UseSwaggerUI(c =>
  50:              {
  51:                  c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
  52:              });
  53:   
  54:              app.UseMvc();
  55:          }
  56:          #endregion
  57:      }
  58:  }

In the preceding code, ApplicationBasePath gets the base path of the app. The base path is used to locate the XML comments file. TodoApi.xml only works for this example, since the name of the generated XML comments file is based on the application name.

Adding the triple-slash comments to the method enhances the Swagger UI by adding the description to the section header:

[!code-csharpMain]

   1:  using System.Collections.Generic;
   2:  using Microsoft.AspNetCore.Mvc;
   3:  using TodoApi.Models;
   4:  using System.Linq;
   5:   
   6:  #region snippet_TodoController
   7:  namespace TodoApi.Controllers
   8:  {
   9:      [Produces("application/json")]
  10:      [Route("api/[controller]")]
  11:      public class TodoController : Controller
  12:      {
  13:          private readonly TodoContext _context;
  14:  #endregion
  15:   
  16:          public TodoController(TodoContext context)
  17:          {
  18:              _context = context;
  19:   
  20:              if (_context.TodoItems.Count() == 0)
  21:              {
  22:                  _context.TodoItems.Add(new TodoItem { Name = "Item1" });
  23:                  _context.SaveChanges();
  24:              }
  25:          }
  26:   
  27:          #region snippet_GetAll
  28:          [HttpGet]
  29:          [Produces("application/json", Type = typeof(TodoItem))]
  30:          public IEnumerable<TodoItem> GetAll()
  31:          {
  32:              return _context.TodoItems.ToList();
  33:          }
  34:          #endregion
  35:   
  36:          #region snippet_GetById
  37:          [HttpGet("{id}", Name = "GetTodo")]
  38:          [Produces("application/json", Type = typeof(TodoItem))]
  39:          public IActionResult GetById(long id)
  40:          {
  41:              var item = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  42:              if (item == null)
  43:              {
  44:                  return NotFound();
  45:              }
  46:              return new ObjectResult(item);
  47:          }
  48:          #endregion
  49:          
  50:          #region snippet_Create
  51:          /// <summary>
  52:          /// Creates a TodoItem.
  53:          /// </summary>
  54:          /// <remarks>
  55:          /// Sample request:
  56:          ///
  57:          ///     POST /Todo
  58:          ///     {
  59:          ///        "id": 1,
  60:          ///        "name": "Item1",
  61:          ///        "isComplete": true
  62:          ///     }
  63:          ///
  64:          /// </remarks>
  65:          /// <param name="item"></param>
  66:          /// <returns>A newly-created TodoItem</returns>
  67:          /// <response code="201">Returns the newly-created item</response>
  68:          /// <response code="400">If the item is null</response>            
  69:          [HttpPost]
  70:          [ProducesResponseType(typeof(TodoItem), 201)]
  71:          [ProducesResponseType(typeof(TodoItem), 400)]
  72:          public IActionResult Create([FromBody] TodoItem item)
  73:          {
  74:              if (item == null)
  75:              {
  76:                  return BadRequest();
  77:              }
  78:   
  79:              _context.TodoItems.Add(item);
  80:              _context.SaveChanges();
  81:   
  82:              return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
  83:          }
  84:          #endregion
  85:   
  86:          #region snippet_Update
  87:          [HttpPut("{id}")]
  88:          public IActionResult Update(long id, [FromBody] TodoItem item)
  89:          {
  90:              if (item == null || item.Id != id)
  91:              {
  92:                  return BadRequest();
  93:              }
  94:   
  95:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  96:              if (todo == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              todo.IsComplete = item.IsComplete;
 102:              todo.Name = item.Name;
 103:   
 104:              _context.TodoItems.Update(todo);
 105:              _context.SaveChanges();
 106:              return new NoContentResult();
 107:          }
 108:          #endregion
 109:   
 110:          #region snippet_Delete
 111:          /// <summary>
 112:          /// Deletes a specific TodoItem.
 113:          /// </summary>
 114:          /// <param name="id"></param>        
 115:          [HttpDelete("{id}")]
 116:          public IActionResult Delete(long id)
 117:          {
 118:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
 119:              if (todo == null)
 120:              {
 121:                  return NotFound();
 122:              }
 123:   
 124:              _context.TodoItems.Remove(todo);
 125:              _context.SaveChanges();
 126:              return new NoContentResult();
 127:          }
 128:          #endregion
 129:      }
 130:  }

Swagger UI showing XML comment ‘Deletes a specific TodoItem.’ for the DELETE method
Swagger UI showing XML comment ‘Deletes a specific TodoItem.’ for the DELETE method

The UI is driven by the generated JSON file, which also contains these comments:

Add a tag to the Create action method documentation. It supplements information specified in the <summary> tag and provides a more robust Swagger UI. The <remarks> tag content can consist of text, JSON, or XML.

[!code-csharpMain]

   1:  using System.Collections.Generic;
   2:  using Microsoft.AspNetCore.Mvc;
   3:  using TodoApi.Models;
   4:  using System.Linq;
   5:   
   6:  #region snippet_TodoController
   7:  namespace TodoApi.Controllers
   8:  {
   9:      [Produces("application/json")]
  10:      [Route("api/[controller]")]
  11:      public class TodoController : Controller
  12:      {
  13:          private readonly TodoContext _context;
  14:  #endregion
  15:   
  16:          public TodoController(TodoContext context)
  17:          {
  18:              _context = context;
  19:   
  20:              if (_context.TodoItems.Count() == 0)
  21:              {
  22:                  _context.TodoItems.Add(new TodoItem { Name = "Item1" });
  23:                  _context.SaveChanges();
  24:              }
  25:          }
  26:   
  27:          #region snippet_GetAll
  28:          [HttpGet]
  29:          [Produces("application/json", Type = typeof(TodoItem))]
  30:          public IEnumerable<TodoItem> GetAll()
  31:          {
  32:              return _context.TodoItems.ToList();
  33:          }
  34:          #endregion
  35:   
  36:          #region snippet_GetById
  37:          [HttpGet("{id}", Name = "GetTodo")]
  38:          [Produces("application/json", Type = typeof(TodoItem))]
  39:          public IActionResult GetById(long id)
  40:          {
  41:              var item = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  42:              if (item == null)
  43:              {
  44:                  return NotFound();
  45:              }
  46:              return new ObjectResult(item);
  47:          }
  48:          #endregion
  49:          
  50:          #region snippet_Create
  51:          /// <summary>
  52:          /// Creates a TodoItem.
  53:          /// </summary>
  54:          /// <remarks>
  55:          /// Sample request:
  56:          ///
  57:          ///     POST /Todo
  58:          ///     {
  59:          ///        "id": 1,
  60:          ///        "name": "Item1",
  61:          ///        "isComplete": true
  62:          ///     }
  63:          ///
  64:          /// </remarks>
  65:          /// <param name="item"></param>
  66:          /// <returns>A newly-created TodoItem</returns>
  67:          /// <response code="201">Returns the newly-created item</response>
  68:          /// <response code="400">If the item is null</response>            
  69:          [HttpPost]
  70:          [ProducesResponseType(typeof(TodoItem), 201)]
  71:          [ProducesResponseType(typeof(TodoItem), 400)]
  72:          public IActionResult Create([FromBody] TodoItem item)
  73:          {
  74:              if (item == null)
  75:              {
  76:                  return BadRequest();
  77:              }
  78:   
  79:              _context.TodoItems.Add(item);
  80:              _context.SaveChanges();
  81:   
  82:              return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
  83:          }
  84:          #endregion
  85:   
  86:          #region snippet_Update
  87:          [HttpPut("{id}")]
  88:          public IActionResult Update(long id, [FromBody] TodoItem item)
  89:          {
  90:              if (item == null || item.Id != id)
  91:              {
  92:                  return BadRequest();
  93:              }
  94:   
  95:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  96:              if (todo == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              todo.IsComplete = item.IsComplete;
 102:              todo.Name = item.Name;
 103:   
 104:              _context.TodoItems.Update(todo);
 105:              _context.SaveChanges();
 106:              return new NoContentResult();
 107:          }
 108:          #endregion
 109:   
 110:          #region snippet_Delete
 111:          /// <summary>
 112:          /// Deletes a specific TodoItem.
 113:          /// </summary>
 114:          /// <param name="id"></param>        
 115:          [HttpDelete("{id}")]
 116:          public IActionResult Delete(long id)
 117:          {
 118:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
 119:              if (todo == null)
 120:              {
 121:                  return NotFound();
 122:              }
 123:   
 124:              _context.TodoItems.Remove(todo);
 125:              _context.SaveChanges();
 126:              return new NoContentResult();
 127:          }
 128:          #endregion
 129:      }
 130:  }

Notice the UI enhancements with these additional comments.

Swagger UI with additional comments shown
Swagger UI with additional comments shown

Data Annotations

Decorate the model with attributes, found in System.ComponentModel.DataAnnotations, to help drive the Swagger UI components.

Add the [Required] attribute to the Name property of the TodoItem class:

[!code-csharpMain]

   1:  using System.ComponentModel;
   2:  using System.ComponentModel.DataAnnotations;
   3:   
   4:  namespace TodoApi.Models
   5:  {
   6:      public class TodoItem
   7:      {
   8:          public long Id { get; set; }
   9:   
  10:          [Required]
  11:          public string Name { get; set; }
  12:   
  13:          [DefaultValue(false)]
  14:          public bool IsComplete { get; set; }
  15:      }
  16:  }

The presence of this attribute changes the UI behavior and alters the underlying JSON schema:

Add the [Produces("application/json")] attribute to the API controller. Its purpose is to declare that the controller’s actions support a return a content type of application/json:

[!code-csharpMain]

   1:  using System.Collections.Generic;
   2:  using Microsoft.AspNetCore.Mvc;
   3:  using TodoApi.Models;
   4:  using System.Linq;
   5:   
   6:  #region snippet_TodoController
   7:  namespace TodoApi.Controllers
   8:  {
   9:      [Produces("application/json")]
  10:      [Route("api/[controller]")]
  11:      public class TodoController : Controller
  12:      {
  13:          private readonly TodoContext _context;
  14:  #endregion
  15:   
  16:          public TodoController(TodoContext context)
  17:          {
  18:              _context = context;
  19:   
  20:              if (_context.TodoItems.Count() == 0)
  21:              {
  22:                  _context.TodoItems.Add(new TodoItem { Name = "Item1" });
  23:                  _context.SaveChanges();
  24:              }
  25:          }
  26:   
  27:          #region snippet_GetAll
  28:          [HttpGet]
  29:          [Produces("application/json", Type = typeof(TodoItem))]
  30:          public IEnumerable<TodoItem> GetAll()
  31:          {
  32:              return _context.TodoItems.ToList();
  33:          }
  34:          #endregion
  35:   
  36:          #region snippet_GetById
  37:          [HttpGet("{id}", Name = "GetTodo")]
  38:          [Produces("application/json", Type = typeof(TodoItem))]
  39:          public IActionResult GetById(long id)
  40:          {
  41:              var item = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  42:              if (item == null)
  43:              {
  44:                  return NotFound();
  45:              }
  46:              return new ObjectResult(item);
  47:          }
  48:          #endregion
  49:          
  50:          #region snippet_Create
  51:          /// <summary>
  52:          /// Creates a TodoItem.
  53:          /// </summary>
  54:          /// <remarks>
  55:          /// Sample request:
  56:          ///
  57:          ///     POST /Todo
  58:          ///     {
  59:          ///        "id": 1,
  60:          ///        "name": "Item1",
  61:          ///        "isComplete": true
  62:          ///     }
  63:          ///
  64:          /// </remarks>
  65:          /// <param name="item"></param>
  66:          /// <returns>A newly-created TodoItem</returns>
  67:          /// <response code="201">Returns the newly-created item</response>
  68:          /// <response code="400">If the item is null</response>            
  69:          [HttpPost]
  70:          [ProducesResponseType(typeof(TodoItem), 201)]
  71:          [ProducesResponseType(typeof(TodoItem), 400)]
  72:          public IActionResult Create([FromBody] TodoItem item)
  73:          {
  74:              if (item == null)
  75:              {
  76:                  return BadRequest();
  77:              }
  78:   
  79:              _context.TodoItems.Add(item);
  80:              _context.SaveChanges();
  81:   
  82:              return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
  83:          }
  84:          #endregion
  85:   
  86:          #region snippet_Update
  87:          [HttpPut("{id}")]
  88:          public IActionResult Update(long id, [FromBody] TodoItem item)
  89:          {
  90:              if (item == null || item.Id != id)
  91:              {
  92:                  return BadRequest();
  93:              }
  94:   
  95:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  96:              if (todo == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              todo.IsComplete = item.IsComplete;
 102:              todo.Name = item.Name;
 103:   
 104:              _context.TodoItems.Update(todo);
 105:              _context.SaveChanges();
 106:              return new NoContentResult();
 107:          }
 108:          #endregion
 109:   
 110:          #region snippet_Delete
 111:          /// <summary>
 112:          /// Deletes a specific TodoItem.
 113:          /// </summary>
 114:          /// <param name="id"></param>        
 115:          [HttpDelete("{id}")]
 116:          public IActionResult Delete(long id)
 117:          {
 118:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
 119:              if (todo == null)
 120:              {
 121:                  return NotFound();
 122:              }
 123:   
 124:              _context.TodoItems.Remove(todo);
 125:              _context.SaveChanges();
 126:              return new NoContentResult();
 127:          }
 128:          #endregion
 129:      }
 130:  }

The Response Content Type drop-down selects this content type as the default for the controller’s GET actions:

Swagger UI with default response content type
Swagger UI with default response content type

As the usage of data annotations in the Web API increases, the UI and API help pages become more descriptive and useful.

Describing Response Types

Consuming developers are most concerned with what is returned — specifically response types and error codes (if not standard). These are handled in the XML comments and data annotations.

The Create action returns 201 Created on success or 400 Bad Request when the posted request body is null. Without proper documentation in the Swagger UI, the consumer lacks knowledge of these expected outcomes. That problem is fixed by adding the highlighted lines in the following example:

[!code-csharpMain]

   1:  using System.Collections.Generic;
   2:  using Microsoft.AspNetCore.Mvc;
   3:  using TodoApi.Models;
   4:  using System.Linq;
   5:   
   6:  #region snippet_TodoController
   7:  namespace TodoApi.Controllers
   8:  {
   9:      [Produces("application/json")]
  10:      [Route("api/[controller]")]
  11:      public class TodoController : Controller
  12:      {
  13:          private readonly TodoContext _context;
  14:  #endregion
  15:   
  16:          public TodoController(TodoContext context)
  17:          {
  18:              _context = context;
  19:   
  20:              if (_context.TodoItems.Count() == 0)
  21:              {
  22:                  _context.TodoItems.Add(new TodoItem { Name = "Item1" });
  23:                  _context.SaveChanges();
  24:              }
  25:          }
  26:   
  27:          #region snippet_GetAll
  28:          [HttpGet]
  29:          [Produces("application/json", Type = typeof(TodoItem))]
  30:          public IEnumerable<TodoItem> GetAll()
  31:          {
  32:              return _context.TodoItems.ToList();
  33:          }
  34:          #endregion
  35:   
  36:          #region snippet_GetById
  37:          [HttpGet("{id}", Name = "GetTodo")]
  38:          [Produces("application/json", Type = typeof(TodoItem))]
  39:          public IActionResult GetById(long id)
  40:          {
  41:              var item = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  42:              if (item == null)
  43:              {
  44:                  return NotFound();
  45:              }
  46:              return new ObjectResult(item);
  47:          }
  48:          #endregion
  49:          
  50:          #region snippet_Create
  51:          /// <summary>
  52:          /// Creates a TodoItem.
  53:          /// </summary>
  54:          /// <remarks>
  55:          /// Sample request:
  56:          ///
  57:          ///     POST /Todo
  58:          ///     {
  59:          ///        "id": 1,
  60:          ///        "name": "Item1",
  61:          ///        "isComplete": true
  62:          ///     }
  63:          ///
  64:          /// </remarks>
  65:          /// <param name="item"></param>
  66:          /// <returns>A newly-created TodoItem</returns>
  67:          /// <response code="201">Returns the newly-created item</response>
  68:          /// <response code="400">If the item is null</response>            
  69:          [HttpPost]
  70:          [ProducesResponseType(typeof(TodoItem), 201)]
  71:          [ProducesResponseType(typeof(TodoItem), 400)]
  72:          public IActionResult Create([FromBody] TodoItem item)
  73:          {
  74:              if (item == null)
  75:              {
  76:                  return BadRequest();
  77:              }
  78:   
  79:              _context.TodoItems.Add(item);
  80:              _context.SaveChanges();
  81:   
  82:              return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
  83:          }
  84:          #endregion
  85:   
  86:          #region snippet_Update
  87:          [HttpPut("{id}")]
  88:          public IActionResult Update(long id, [FromBody] TodoItem item)
  89:          {
  90:              if (item == null || item.Id != id)
  91:              {
  92:                  return BadRequest();
  93:              }
  94:   
  95:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
  96:              if (todo == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              todo.IsComplete = item.IsComplete;
 102:              todo.Name = item.Name;
 103:   
 104:              _context.TodoItems.Update(todo);
 105:              _context.SaveChanges();
 106:              return new NoContentResult();
 107:          }
 108:          #endregion
 109:   
 110:          #region snippet_Delete
 111:          /// <summary>
 112:          /// Deletes a specific TodoItem.
 113:          /// </summary>
 114:          /// <param name="id"></param>        
 115:          [HttpDelete("{id}")]
 116:          public IActionResult Delete(long id)
 117:          {
 118:              var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
 119:              if (todo == null)
 120:              {
 121:                  return NotFound();
 122:              }
 123:   
 124:              _context.TodoItems.Remove(todo);
 125:              _context.SaveChanges();
 126:              return new NoContentResult();
 127:          }
 128:          #endregion
 129:      }
 130:  }

The Swagger UI now clearly documents the expected HTTP response codes:

Swagger UI showing POST Response Class description ‘Returns the newly created Todo item’ and ‘400 - If the item is null’ for status code and reason under Response Messages
Swagger UI showing POST Response Class description ‘Returns the newly created Todo item’ and ‘400 - If the item is null’ for status code and reason under Response Messages

Customizing the UI

The stock UI is both functional and presentable; however, when building documentation pages for your API, you want it to represent your brand or theme. Accomplishing that task with the Swashbuckle components requires adding the resources to serve static files and then building the folder structure to host those files.

If targeting .NET Framework, add the Microsoft.AspNetCore.StaticFiles NuGet package to the project:

Enable the static files middleware:

[!code-csharpMain]

   1:  using System.IO;
   2:  using Microsoft.AspNetCore.Builder;
   3:  using Microsoft.EntityFrameworkCore;
   4:  using Microsoft.Extensions.DependencyInjection;
   5:  using Microsoft.Extensions.PlatformAbstractions;
   6:  using Swashbuckle.AspNetCore.Swagger;
   7:  using TodoApi.Models;
   8:   
   9:  namespace TodoApi
  10:  {
  11:      public class Startup
  12:      {       
  13:          #region snippet_ConfigureServices
  14:          public void ConfigureServices(IServiceCollection services)
  15:          {
  16:              services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
  17:              services.AddMvc();
  18:   
  19:              // Register the Swagger generator, defining one or more Swagger documents
  20:              services.AddSwaggerGen(c =>
  21:              {
  22:                  c.SwaggerDoc("v1", new Info
  23:                  {
  24:                      Version = "v1",
  25:                      Title = "ToDo API",
  26:                      Description = "A simple example ASP.NET Core Web API",
  27:                      TermsOfService = "None",
  28:                      Contact = new Contact { Name = "Shayne Boyer", Email = "", Url = "https://twitter.com/spboyer" },
  29:                      License = new License { Name = "Use under LICX", Url = "https://example.com/license" }
  30:                  });
  31:   
  32:                  // Set the comments path for the Swagger JSON and UI.
  33:                  var basePath = PlatformServices.Default.Application.ApplicationBasePath;
  34:                  var xmlPath = Path.Combine(basePath, "TodoApi.xml"); 
  35:                  c.IncludeXmlComments(xmlPath);                
  36:              });
  37:          }
  38:          #endregion
  39:   
  40:          #region snippet_Configure
  41:          public void Configure(IApplicationBuilder app)
  42:          {
  43:              app.UseStaticFiles();
  44:   
  45:              // Enable middleware to serve generated Swagger as a JSON endpoint.
  46:              app.UseSwagger();
  47:   
  48:              // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
  49:              app.UseSwaggerUI(c =>
  50:              {
  51:                  c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
  52:              });
  53:   
  54:              app.UseMvc();
  55:          }
  56:          #endregion
  57:      }
  58:  }

Acquire the contents of the dist folder from the Swagger UI GitHub repository. This folder contains the necessary assets for the Swagger UI page.

Create a wwwroot/swagger/ui folder, and copy into it the contents of the dist folder.

Create a wwwroot/swagger/ui/css/custom.css file with the following CSS to customize the page header:

[!code-cssMain]

   1:  .swagger-section #header
   2:  {
   3:      border-bottom: 1px solid #000000;
   4:      font-style: normal;
   5:      font-weight: 400;
   6:      font-family: "Segoe UI Light","Segoe WP Light","Segoe UI","Segoe WP",Tahoma,Arial,sans-serif;
   7:      background-color: black;
   8:  }
   9:   
  10:  .swagger-section #header h1
  11:  {
  12:      text-align: center;
  13:      font-size: 20px;
  14:      color: white;
  15:  }

Reference custom.css in the Index.html file:

[!code-htmlMain]

   1:  <!DOCTYPE html>
   2:  <html>
   3:  <head>
   4:      <meta charset="UTF-8">
   5:      <meta http-equiv="x-ua-compatible" content="IE=edge">
   6:      <title>Swagger UI</title>
   7:      <link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
   8:      <link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
   9:      <link href='css/typography.css' media='screen' rel='stylesheet' type='text/css' />
  10:      <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css' />
  11:      <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css' />
  12:      <link href='css/reset.css' media='print' rel='stylesheet' type='text/css' />
  13:      <link href='css/print.css' media='print' rel='stylesheet' type='text/css' />
  14:      <link href='css/custom.css' media='screen' rel='stylesheet' type='text/css' />
  15:      <script src='lib/object-assign-pollyfill.js' type='text/javascript'></script>
  16:      <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
  17:      <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
  18:      <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
  19:      <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
  20:      <script src='lib/handlebars-4.0.5.js' type='text/javascript'></script>
  21:      <script src='lib/lodash.min.js' type='text/javascript'></script>
  22:      <script src='lib/backbone-min.js' type='text/javascript'></script>
  23:      <script src='swagger-ui.js' type='text/javascript'></script>
  24:      <script src='lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
  25:      <script src='lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script>
  26:      <script src='lib/jsoneditor.min.js' type='text/javascript'></script>
  27:      <script src='lib/marked.js' type='text/javascript'></script>
  28:      <script src='lib/swagger-oauth.js' type='text/javascript'></script>
  29:      <!-- Some basic translations -->
  30:      <!-- <script src='lang/translator.js' type='text/javascript'></script> -->
  31:      <!-- <script src='lang/ru.js' type='text/javascript'></script> -->
  32:      <!-- <script src='lang/en.js' type='text/javascript'></script> -->
  33:      <script type="text/javascript">
  34:      $(function () {
  35:        var url = window.location.search.match(/url=([^&]+)/);
  36:        if (url && url.length > 1) {
  37:          url = decodeURIComponent(url[1]);
  38:        } else {
  39:          url = "http://petstore.swagger.io/v2/swagger.json";
  40:        }
  41:   
  42:        hljs.configure({
  43:          highlightSizeThreshold: 5000
  44:        });
  45:   
  46:        // Pre load translate...
  47:        if(window.SwaggerTranslator) {
  48:          window.SwaggerTranslator.translate();
  49:        }
  50:        window.swaggerUi = new SwaggerUi({
  51:          url: url,
  52:          dom_id: "swagger-ui-container",
  53:          supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
  54:          onComplete: function(swaggerApi, swaggerUi){
  55:            if(typeof initOAuth == "function") {
  56:              initOAuth({
  57:                clientId: "your-client-id",
  58:                clientSecret: "your-client-secret-if-required",
  59:                realm: "your-realms",
  60:                appName: "your-app-name",
  61:                scopeSeparator: " ",
  62:                additionalQueryStringParams: {}
  63:              });
  64:            }
  65:   
  66:            if(window.SwaggerTranslator) {
  67:              window.SwaggerTranslator.translate();
  68:            }
  69:          },
  70:          onFailure: function(data) {
  71:            log("Unable to Load SwaggerUI");
  72:          },
  73:          docExpansion: "none",
  74:          jsonEditor: false,
  75:          defaultModelRendering: 'schema',
  76:          showRequestHeaders: false,
  77:          showOperationIds: false
  78:        });
  79:   
  80:        window.swaggerUi.load();
  81:   
  82:        function log() {
  83:          if ('console' in window) {
  84:            console.log.apply(console, arguments);
  85:          }
  86:        }
  87:    });
  88:      </script>
  89:  </head>
  90:  <body class="swagger-section">
  91:      <div id='header'>
  92:          <div class="swagger-ui-wrap">
  93:              <a id="logo" href="http://swagger.io"><img class="logo__img" alt="swagger" height="30" width="30" src="images/logo_small.png" /><span class="logo__title">swagger</span></a>
  94:              <form id='api_selector'>
  95:                  <div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text" /></div>
  96:                  <div id='auth_container'></div>
  97:                  <div class='input'><a id="explore" class="header__btn" href="#" data-sw-translate>Explore</a></div>
  98:              </form>
  99:          </div>
 100:      </div>
 101:      <div id="message-bar" class="swagger-ui-wrap" data-sw-translate>&nbsp;</div>
 102:      <div id="swagger-ui-container" class="swagger-ui-wrap"></div>
 103:  </body>
 104:  </html>

Browse to the Index.html page at http://localhost:<random_port>/swagger/ui/Index.html. Enter http://localhost:<random_port>/swagger/v1/swagger.json in the header’s textbox, and click the Explore button. The resulting page looks as follows:

Swagger UI with custom header title
Swagger UI with custom header title

There is much more you can do with the page. See the full capabilities for the UI resources at the Swagger UI GitHub repository.





Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnetcore/tutorials/web-api-help-pages-using-swagger.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>