-
Notifications
You must be signed in to change notification settings - Fork 676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding a description of how I integrated Swashbuckle with DapperDox #1107
Open
lockewritesdocs
wants to merge
1
commit into
domaindrivendev:master
Choose a base branch
from
lockewritesdocs:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
# A Pirate's Life for Me: Documenting APIs with Swagger | ||
|
||
Our team starting developing a new API (in C#), which I took as an opportunity to implement Swagger (now the OpenAPI Specification), an open source project used to describe and document RESTful APIs. I wanted to show our developers and support engineers that injecting documentation into the code can reduce response time, mitigate errors, and decrease the point of entry for new hires. To illustrate those gains, I needed to develop a proof of concept. | ||
|
||
## Why Swagger? | ||
|
||
Swagger is open source and includes a UI to display your API documentation, which can be built from source code or manually in JSON. Swashbuckle, a combination of ApiExplorer and Swagger UI, enables Swagger for .NET environments, which was just what we needed. | ||
|
||
> **Note:** This article applies to .NET environments. Swashbuckle uses [a different package](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) for .NET Core environments. | ||
|
||
## Prepare to Swashbuckle | ||
|
||
Swashbuckle requires a bit of coding to implement, but using [Paket](https://fsprojects.github.io/Paket/) helps to manage .NET dependencies. With Paket, I can add the necessary Swashbuckle NuGet packages to my API project and ensure that they are current. If I need to add more packages, I can install and manage those packages through Paket. | ||
|
||
After installing Paket, I run the following command to add Swashbuckle as a dependency to my C# project. | ||
|
||
``` | ||
paket add nuget Swashbuckle.Core project <projectName> | ||
``` | ||
|
||
Now that Swashbuckle is available to my project, I can add Swashbuckle to the ```Startup.cs``` file, which is the application startup file for the API. I add each of the following Swashbuckle libraries so that the solution can access the necessary methods. | ||
|
||
```csharp | ||
using Swashbuckle.Application; | ||
using Swashbuckle.Swagger.Annotations; | ||
using Swashbuckle.Swagger; | ||
using Swashbuckle.Swagger.XmlComments; | ||
``` | ||
|
||
Then, I add the following code (see example that follows), much of which is supplied in a Swashbuckle example file. In the ```SwaggerGeneratorOptions``` class, I specify the options that I want Swashbuckle to enable. | ||
* ```schemaFilters``` post-modify complex schemas in the generated output. You can modify schemas for a specific member type or across all member types. The IModelFilter is now the ISchemaFilter. We created an IModelFilter to fix some of the generated output. | ||
* ```operationFilters``` specifies options to modify the generated output. Each entry enables a different modification for operation descriptions. | ||
|
||
```csharp | ||
namespace LinkInterface | ||
{ | ||
public class Startup | ||
{ | ||
//Enables Swashbuckle and related Swagger options. | ||
private void generateSwagger(HttpConfiguration config) | ||
{ | ||
config.EnsureInitialized(); | ||
var swaggerProvider = new SwaggerGenerator( | ||
config.Services.GetApiExplorer(), | ||
config.Formatters.JsonFormatter.SerializerSettings, | ||
new Dictionary<string, Info> { | ||
version = "v1", title = "My API", description = "Provides an interface | ||
between our API and third-party services" | ||
} | ||
new SwaggerGeneratorOptions( | ||
//Apply your Swagger options here. | ||
schemaIdSelector: (type) => type.FriendlyId(true), | ||
//Implements the SwaggerTitleFilter class, which generates title members | ||
//in the definitions model of the swagger.json file. | ||
modelFilters: new List<IModelFilter>(){new SwaggerTitleFilter()}, | ||
conflictingActionsResolver: (apiDescriptions) => apiDescriptions.GetEnumerator().Current, | ||
schemaFilters: new List<ISchemaFilter>(){new ApplySwaggerSchemaFilterAttributes()}, | ||
operationFilters: new List<IOperationFilter>() | ||
{ | ||
//Enables XML comments and writes them to the MyAPI.XML file. These comments | ||
//are included in the generated swagger.json file. | ||
new ApplyXmlActionComments("MyAPI.XML"), | ||
//Enables the SwaggerResponse output class, to specify multiple | ||
//response codes for the API. | ||
new ApplySwaggerResponseAttributes() | ||
} | ||
); | ||
|
||
var swaggerString = JsonConvert.SerializeObject( | ||
swaggerDoc, | ||
Formatting.Indented, | ||
new JsonSerializerSettings | ||
{ | ||
NullValueHandling = NullValueHandling.Ignore, | ||
Converters = new[] {new VendorExtensionsConverter()} | ||
} | ||
); | ||
|
||
//Writes the swagger.json file to the \output directory so that it can | ||
//be consumed by statis site generators. | ||
System.IO.StreamWriter file = new System.IO.StreamWriter("swagger.json"); | ||
file.WriteLine(swaggerString); | ||
file.Close(); | ||
); | ||
} | ||
} | ||
} | ||
``` | ||
After enabling these options, I *could* include code that enables the Swagger UI, but that interface looks a bit outdated. Also, I want to incorporate additional documentation written in Markdown, which the Swagger UI does not support. After reading online forums and posting questions to the [Write The Docs channel on Slack](http://www.writethedocs.org/slack/), I discovered DapperDox. | ||
|
||
## Using DapperDox | ||
|
||
[DapperDox](http://dapperdox.io/) is an open source documentation framework for OpenAPI specifications. Instead of having Swashbuckle publish our API specification in the Swagger UI, I added the following code to the ```Startup.cs``` file. This code writes the Swagger specification to a ```swagger.json``` file. | ||
|
||
```csharp | ||
System.IO.StreamWriter file = new System.IO.StreamWriter("swagger.json"); | ||
file.WriteLine(swaggerString); | ||
file.Close(); | ||
``` | ||
|
||
DapperDox reads this file and displays it in its own UI. I installed DapperDox and pointed it at my swagger.json file, and saw nothing but error messages in my command prompt. | ||
|
||
Reading through the [DapperDox documentation](http://dapperdox.io/docs/spec-resource-definitions), I discovered that *"When specifying a resource schema object...DapperDox requires that the optional schema object title member is present."* This requirement was problematic, because Swashbuckle does not include a method for adding members to a schema in the generated ```swagger.json``` file. Additionally, it took some tinkering in the code for me to realize that the missing title member on the ```definitions``` model is what caused DapperDox to break. | ||
|
||
## Fixing the output | ||
|
||
The Swashbuckle documentation offered little help in this regard, so I turned to one of our developers. After reviewing the code together, my developer counterpart created a ```SwaggerTitleFilter``` method that adds a ```title``` member to the ```definitions``` model in the resulting ```swagger.json``` file. The title member displays in the generated documentation as a link to the referenced object, creating a hyperlink between the two objects. | ||
|
||
The following code implements an ```IModelFilter``` that causes Swashbuckle to generate a title member for any schema. The ```SwaggerTitleFilter``` was referenced in the previous code sample | ||
|
||
```csharp | ||
namespace MyAPI.Swagger | ||
{ | ||
public class SwaggerTitleFilter : IModelFilter | ||
{ | ||
public void Apply(Schema schema, ModelFilterContext mfc) | ||
{ | ||
schema.vendorExtensions.Add("title", mfc.SystemType.Name); | ||
} | ||
} | ||
} | ||
``` | ||
I compiled the code and Swashbuckle generated an updated ```swagger.json``` file. With the ```title``` member added to the ```swagger.json``` output, I pointed DapperDox at the directory containing my ```swagger.json``` file. | ||
|
||
``` | ||
.\dapperdox -spec-dir=C:\Bitbucket\APIproject\source | ||
``` | ||
|
||
I opened a browser and entered ```http://localhost:3123```, which is where DapperDox runs by default, and it worked! DapperDox displayed my ```swagger.json``` file and created interactive documentation that clearly displays the requests, responses, and query parameters for the API. I demoed the output for a few developers and support engineers, and they were over the moon. | ||
|
||
 | ||
|
||
## Next steps | ||
|
||
With this framework in place, we can extend Swashbuckle to future APIs and use DapperDox to host the ```swagger.json``` file for each. The resulting output lives with the code, and provides documentation that developers and support engineers can access locally by running a single command. | ||
|
||
To add documentation beyond just the generated JSON output, DapperDox works incredibly well. I can author short tutorials that describe how to integrate our API with third-party services, which developers can easily review and modify through pull requests. As the API grows, we can add a README file that describes enhancements, modifications, and new integration points. Non-API documentation will live in an ```\assets``` directory, which DapperDox includes at build time. | ||
|
||
Each time that the code builds, the ```swagger.json``` file updates with the most current information. Developers and support engineers just run the ```.\dapperdox``` command and specify the directory where the ```swagger.json``` file lives. As the code changes, so does the documentation, so technical debt approaches zero. | ||
|
||
## Lessons learned | ||
|
||
Static site generators are all the rage, and for good reason. Providing a lightweight framework that can be deployed quickly is a huge asset when documenting APIs, especially external-facing documentation. Numerous options are available, but DapperDox felt like the right fit for our needs. | ||
|
||
The pain of determining why DapperDox was broken and the additional coding required to fix the problem was worth the effort, and we are poised to integrate this process into the next set of APIs that our team develops. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lockewritesdocs where was the swaggerDoc variable defined?
Regards!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@safv12, honestly, I don't recall. I changed jobs and do not work with Swashbuckle at all anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@domaindrivendev @heldersepu someone knows how to export the swagger.json file?
Or where is this variable defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@safv12
As far as I know there is no such an option, you could create an IDocFilter that could do that...
Why do you want to export the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@heldersepu thanks for your prompt reply.
I want to export the json file because I have a set of separate microservices in different projects. Each of these projects has its own auto-documentation with Swashbuckle.
I want to put all the files together in a single container with dapperDox every time the project is built in the CI process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not modify the source of DapperDox to retrieve the swagger doc directly from the microservices?
I think that is a better option, and DapperDox is OpenSource, I think that everyone will benefit from that enhancement. (it might be simple to code that change)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because my microservices are into a private VPC and their methods are exposed through an API Gateway of amazon.
But I think I can try something like that. Thank you so much @heldersepu
Regards!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@safv12 no problem if you have any problem implementing that reach out directly on hangouts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@safv12, here is a code sample that I have from my old notes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @lockewritesdocs :)