Skip to content

Commit 6a403de

Browse files
author
Chebotov Nikolay
committed
Add "ChangeAllResponsesByHttpStatusCode<T>" extension method for "SwaggerGenOptions" allows to change all responses by specific http status codes in OpenApi document;
Add "TagOrderByNameDocumentFilter" for ordering tags by name in OpenApi document.
1 parent 88365b7 commit 6a403de

12 files changed

+394
-22
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88

99
These are the changes to each version that has been released on the [nuget](https://www.nuget.org/packages/Unchase.Swashbuckle.AspNetCore.Extensions/).
1010

11+
## v2.1.0 `(2020-02-19)`
12+
13+
- [x] Add `ChangeAllResponsesByHttpStatusCode<T>` extension method for `SwaggerGenOptions` allows to change all responses by specific http status codes in OpenApi document
14+
- [x] Add `TagOrderByNameDocumentFilter` for ordering tags by name in OpenApi document
15+
1116
## v2.0.0 `(2020-02-08)`
1217

1318
**BREAKING CHANGES (see [README.md](README.md)):**

README.md

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ app.UseSwaggerUI(c =>
7474
});
7575
```
7676

77-
1. **Fix enums**:
77+
1. **Fix enums in OpenApi document**:
7878
- In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable whichever extensions (filters) you need:
7979

8080
```csharp
@@ -108,7 +108,7 @@ public void ConfigureServices(IServiceCollection services)
108108
}
109109
```
110110

111-
2. **Hide Paths and Defenitions from OpenApi documentation** without accepted roles:
111+
2. **Hide Paths and Defenitions from OpenApi documentation** without accepted roles in OpenApi document:
112112
- In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable `HidePathsAndDefinitionsByRolesDocumentFilter` document filter:
113113

114114
```csharp
@@ -127,7 +127,7 @@ public void ConfigureServices(IServiceCollection services)
127127
}
128128
```
129129

130-
3. **Append action count into the SwaggetTag's descriptions**:
130+
3. **Append action count into the SwaggetTag's descriptions in OpenApi document**:
131131
- In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable `AppendActionCountToTagSummaryDocumentFilter` document filter:
132132

133133
```csharp
@@ -164,6 +164,66 @@ public class TodoController : ControllerBase
164164
...
165165
```
166166

167+
4. **Change all responses for specific HTTP status codes in OpenApi document**:
168+
- In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable `ChangeAllResponsesByHttpStatusCode<T>` extension (filter) with whichever HTTP status codes you need:
169+
170+
```csharp
171+
// This method gets called by the runtime. Use this method to add services to the container.
172+
public void ConfigureServices(IServiceCollection services)
173+
{
174+
...
175+
176+
services.AddSwaggerGen(options =>
177+
{
178+
...
179+
180+
// change responses for specific HTTP status code ("200")
181+
options.ChangeAllResponsesByHttpStatusCode(
182+
httpStatusCode: 200,
183+
responseDescription: "200 status code description",
184+
responseExampleOption : ResponseExampleOptions.AddNew, // add new response examples
185+
responseExample: new TodoItem { Tag = Tag.Workout, Id = 111, IsComplete = false, Name = "test" }); // some class for response examples
186+
187+
// change responses for specific HTTP status code ("400" (HttpStatusCode.BadRequest))
188+
options.ChangeAllResponsesByHttpStatusCode(
189+
httpStatusCode: HttpStatusCode.BadRequest,
190+
responseDescription: "400 status code description",
191+
responseExampleOption: ResponseExampleOptions.Clear, // claer response examples
192+
responseExample: new ComplicatedClass()); // some class for response examples
193+
194+
// change responses for specific HTTP status code ("201" (StatusCodes.Status201Created))
195+
options.ChangeAllResponsesByHttpStatusCode(
196+
httpStatusCode: StatusCodes.Status201Created,
197+
responseDescription: "201 status code description",
198+
responseExampleOption: ResponseExampleOptions.None, // do nothing with response examples
199+
responseExample: new ComplicatedClass()); // some class for response examples
200+
201+
...
202+
});
203+
}
204+
```
205+
206+
5. **Order tags by name in OpenApi document**:
207+
- In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable `TagOrderByNameDocumentFilter` document filter:
208+
209+
```csharp
210+
// This method gets called by the runtime. Use this method to add services to the container.
211+
public void ConfigureServices(IServiceCollection services)
212+
{
213+
...
214+
215+
services.AddSwaggerGen(options =>
216+
{
217+
...
218+
219+
// order tags by name
220+
options.DocumentFilter<TagOrderByNameDocumentFilter>();
221+
222+
...
223+
});
224+
}
225+
```
226+
167227
## Builds status
168228

169229
|Status|Value|
@@ -179,7 +239,7 @@ public class TodoController : ControllerBase
179239

180240
## Features
181241

182-
#### Fix enums
242+
### Fix enums
183243

184244
- Add an output enums integer values with there strings like `0 = FirstEnumValue` without a `StringEnumConverter` in swaggerUI and schema (by default enums will output only their integer values)
185245
- Add description to each enum value that has an `[Description]` attribute in `swaggerUI` and schema - should use *options.DocumentFilter\<DisplayEnumsWithValuesDocumentFilter\>(**true**);* or *options.AddEnumsWithValuesFixFilters(**true**);*
@@ -275,7 +335,7 @@ public class TodoController : ControllerBase
275335
}
276336
```
277337

278-
#### Hide Paths and Defenitions from OpenApi documentation
338+
### Hide Paths and Defenitions from OpenApi documentation
279339

280340
- Hide all OpenAPIDocument **Paths** and **Defenitions** without accepted roles:
281341

@@ -319,6 +379,12 @@ public class SamplePersonController : ControllerBase
319379
}
320380
```
321381

382+
### Change responses for specific HTTP status codes
383+
384+
For example:
385+
386+
![Change responses](assets/changeResponsesByHttpStatusCode.png)
387+
322388
## HowTos
323389

324390
- [ ] Add HowTos in a future

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: 2.0.{build}
1+
version: 2.1.{build}
22
pull_requests:
33
do_not_increment_build_number: true
44
skip_tags: true
30.8 KB
Loading

src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/EnumTypeExtensions.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,28 @@
33
using System;
44
using System.Text;
55
using System.Collections.Generic;
6+
using System.ComponentModel;
67

78
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions
89
{
910
internal static class EnumTypeExtensions
1011
{
11-
internal static string GetDescriptionFromEnumOption(Type enumOptionType, object enumOption)
12+
private static string GetDescriptionFromEnumOption(Type enumOptionType, object enumOption)
1213
{
1314
return enumOptionType.GetFieldAttributeDescription(enumOption, 0);
1415
}
1516

17+
private static string GetFieldAttributeDescription(this Type enumType, object enumField, int attributeNumber)
18+
{
19+
if (!enumType.IsEnum)
20+
return string.Empty;
21+
var memInfo = enumType.GetMember(enumField.ToString());
22+
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
23+
if (attributes.Length > 0)
24+
return (attributes[attributeNumber] as DescriptionAttribute)?.Description ?? string.Empty;
25+
return string.Empty;
26+
}
27+
1628
internal static List<OpenApiString> GetEnumValuesDescription(Type enumType)
1729
{
1830
var enumsDescriptions = new List<OpenApiString>();
Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using System;
2-
using System.ComponentModel;
1+
using System.ComponentModel;
2+
using System.Net;
33
using Microsoft.Extensions.DependencyInjection;
44
using Swashbuckle.AspNetCore.SwaggerGen;
55
using Unchase.Swashbuckle.AspNetCore.Extensions.Filters;
@@ -8,22 +8,69 @@ namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions
88
{
99
public static class SwaggerGenOptionsExtensions
1010
{
11-
public static void AddEnumsWithValuesFixFilters(this SwaggerGenOptions swaggerGenOptions, bool includeDescriptionFromAttribute = false)
11+
#region Extensions
12+
13+
/// <summary>
14+
/// Change all responses by specific http status codes in OpenApi document.
15+
/// </summary>
16+
/// <typeparam name="T">Type of response example.</typeparam>
17+
/// <param name="swaggerGenOptions"><see cref="SwaggerGenOptions"/>.</param>
18+
/// <param name="httpStatusCode">HTTP status code.</param>
19+
/// <param name="responseDescription">Response description.</param>
20+
/// <param name="responseExampleOption"><see cref="ResponseExampleOptions"/>.</param>
21+
/// <param name="responseExample">New example for response.</param>
22+
/// <returns>
23+
/// Returns <see cref="SwaggerGenOptions"/>.
24+
/// </returns>
25+
public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode<T>(
26+
this SwaggerGenOptions swaggerGenOptions,
27+
int httpStatusCode,
28+
string responseDescription = null,
29+
ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None,
30+
T responseExample = default) where T : class
31+
{
32+
swaggerGenOptions.DocumentFilter<ChangeResponseByHttpStatusCodeDocumentFilter<T>>(httpStatusCode, responseDescription, responseExampleOption, responseExample);
33+
return swaggerGenOptions;
34+
}
35+
36+
/// <summary>
37+
/// Change all responses by specific http status codes in OpenApi document.
38+
/// </summary>
39+
/// <typeparam name="T">Type of response example.</typeparam>
40+
/// <param name="swaggerGenOptions"><see cref="SwaggerGenOptions"/>.</param>
41+
/// <param name="httpStatusCode">HTTP status code.</param>
42+
/// <param name="responseDescription">Response description.</param>
43+
/// <param name="responseExampleOption"><see cref="ResponseExampleOptions"/>.</param>
44+
/// <param name="responseExample">New example for response.</param>
45+
/// <returns>
46+
/// Returns <see cref="SwaggerGenOptions"/>.
47+
/// </returns>
48+
public static SwaggerGenOptions ChangeAllResponsesByHttpStatusCode<T>(
49+
this SwaggerGenOptions swaggerGenOptions,
50+
HttpStatusCode httpStatusCode,
51+
string responseDescription = null,
52+
ResponseExampleOptions responseExampleOption = ResponseExampleOptions.None,
53+
T responseExample = default) where T : class
54+
{
55+
return swaggerGenOptions.ChangeAllResponsesByHttpStatusCode((int)httpStatusCode, responseDescription, responseExampleOption, responseExample);
56+
}
57+
58+
/// <summary>
59+
/// Add filters to fix enums in OpenApi document.
60+
/// </summary>
61+
/// <param name="swaggerGenOptions"><see cref="SwaggerGenOptions"/>.</param>
62+
/// <param name="includeDescriptionFromAttribute">If true - add extensions (descriptions) from <see cref="DescriptionAttribute"/>.</param>
63+
/// <returns>
64+
/// Returns <see cref="SwaggerGenOptions"/>.
65+
/// </returns>
66+
public static SwaggerGenOptions AddEnumsWithValuesFixFilters(this SwaggerGenOptions swaggerGenOptions, bool includeDescriptionFromAttribute = false)
1267
{
1368
swaggerGenOptions.SchemaFilter<XEnumNamesSchemaFilter>(includeDescriptionFromAttribute);
1469
swaggerGenOptions.ParameterFilter<XEnumNamesParameterFilter>(includeDescriptionFromAttribute);
1570
swaggerGenOptions.DocumentFilter<DisplayEnumsWithValuesDocumentFilter>(includeDescriptionFromAttribute);
71+
return swaggerGenOptions;
1672
}
1773

18-
internal static string GetFieldAttributeDescription(this Type enumType, object enumField, int attributeNumber)
19-
{
20-
if (!enumType.IsEnum)
21-
return string.Empty;
22-
var memInfo = enumType.GetMember(enumField.ToString());
23-
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
24-
if (attributes.Length > 0)
25-
return (attributes[attributeNumber] as DescriptionAttribute)?.Description ?? string.Empty;
26-
return string.Empty;
27-
}
74+
#endregion
2875
}
2976
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
using Microsoft.OpenApi.Any;
2+
using Microsoft.OpenApi.Models;
3+
using Swashbuckle.AspNetCore.SwaggerGen;
4+
using System.Linq;
5+
6+
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Filters
7+
{
8+
/// <summary>
9+
/// Options for response example.
10+
/// </summary>
11+
public enum ResponseExampleOptions
12+
{
13+
/// <summary>
14+
/// Clear example.
15+
/// </summary>
16+
Clear = 0,
17+
18+
/// <summary>
19+
/// Add (replace) example.
20+
/// </summary>
21+
AddNew = 1,
22+
23+
/// <summary>
24+
/// Do nothing.
25+
/// </summary>
26+
None = 2
27+
}
28+
29+
/// <summary>
30+
/// Document filter for changing responses by specific http status codes in OpenApi document.
31+
/// </summary>
32+
/// <typeparam name="T">Type of response example.</typeparam>
33+
internal class ChangeResponseByHttpStatusCodeDocumentFilter<T> : IDocumentFilter where T : class
34+
{
35+
#region Fileds
36+
37+
private int _httpStatusCode;
38+
39+
private string _responseDescription;
40+
41+
private ResponseExampleOptions _responseExampleOption;
42+
43+
private T _responseExample;
44+
45+
#endregion
46+
47+
#region Constructors
48+
49+
/// <summary>
50+
/// Constructor.
51+
/// </summary>
52+
/// <param name="httpStatusCode">HTTP status code.</param>
53+
/// <param name="responseDescription">Response description.</param>
54+
/// <param name="responseExampleOption"><see cref="ResponseExampleOptions"/>.</param>
55+
/// <param name="responseExample">New example for response.</param>
56+
public ChangeResponseByHttpStatusCodeDocumentFilter(int httpStatusCode, string responseDescription, ResponseExampleOptions responseExampleOption, T responseExample)
57+
{
58+
this._httpStatusCode = httpStatusCode;
59+
this._responseDescription = responseDescription;
60+
this._responseExampleOption = responseExampleOption;
61+
this._responseExample = responseExample;
62+
}
63+
64+
#endregion
65+
66+
#region Methods
67+
68+
/// <summary>
69+
/// Apply filter.
70+
/// </summary>
71+
/// <param name="swaggerDoc"><see cref="OpenApiDocument"/>.</param>
72+
/// <param name="context"><see cref="DocumentFilterContext"/>.</param>
73+
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
74+
{
75+
if (context.SchemaRepository.Schemas.ContainsKey(typeof(T).Name))
76+
{
77+
var schema = context.SchemaRepository.Schemas[typeof(T).Name];
78+
foreach (var response in swaggerDoc.Paths.SelectMany(p => p.Value.Operations.SelectMany(o => o.Value.Responses)))
79+
{
80+
if (response.Key == this._httpStatusCode.ToString())
81+
{
82+
if (!string.IsNullOrWhiteSpace(this._responseDescription))
83+
response.Value.Description = this._responseDescription;
84+
85+
if (response.Value.Content.ContainsKey("application/json"))
86+
{
87+
var jsonContent = response.Value.Content["application/json"];
88+
switch (this._responseExampleOption)
89+
{
90+
case ResponseExampleOptions.Clear:
91+
response.Value.Content.Remove("application/json");
92+
break;
93+
case ResponseExampleOptions.AddNew:
94+
if (this._responseExample != null)
95+
jsonContent.Example = new OpenApiString(System.Text.Json.JsonSerializer.Serialize(this._responseExample, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
96+
jsonContent.Schema = schema;
97+
break;
98+
case ResponseExampleOptions.None:
99+
default:
100+
break;
101+
}
102+
}
103+
else
104+
{
105+
switch (this._responseExampleOption)
106+
{
107+
case ResponseExampleOptions.AddNew:
108+
if (this._responseExample != null)
109+
{
110+
response.Value.Content.Add("application/json", new OpenApiMediaType()
111+
{
112+
Example = new OpenApiString(System.Text.Json.JsonSerializer.Serialize(this._responseExample, new System.Text.Json.JsonSerializerOptions { WriteIndented = true })),
113+
Schema = schema
114+
});
115+
}
116+
break;
117+
case ResponseExampleOptions.Clear:
118+
case ResponseExampleOptions.None:
119+
default:
120+
break;
121+
}
122+
123+
}
124+
}
125+
}
126+
}
127+
}
128+
129+
#endregion
130+
}
131+
}

0 commit comments

Comments
 (0)