Last active
August 4, 2021 21:52
-
-
Save GeorgDangl/acecc279b2cbf7f366a6487eb50d3833 to your computer and use it in GitHub Desktop.
Multiple response types in ASP.NET Core with Swagger, see https://blog.dangl.me/archive/different-response-schemas-in-aspnet-core-swagger-api-definition-with-nswag/
This file contains hidden or 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
[HttpGet("")] | |
[LightQuery] | |
[Authorize(Policy = DanglIdentityConstants.Authorization.AUTHENTICATION_CONNECTOR_OR_ADMIN_POLICY_NAME)] | |
[NSwag.Annotations.SwaggerResponse((int)HttpStatusCode.OK, typeof(PaginationResult<ClientGet>))] | |
[NSwag.Annotations.SwaggerResponse((int)HttpStatusCode.OK, typeof(IEnumerable<ClientGet>))] | |
public IActionResult GetAllClients(string filter = null) | |
{ | |
var clientsQuery = _identityServerClientsRepository.GetAllClients() | |
.ProjectTo<ClientGet>(); | |
if (!string.IsNullOrWhiteSpace(filter)) | |
{ | |
clientsQuery = clientsQuery.Where(c => c.ClientName.Contains(filter) || c.ClientUri.Contains(filter)); | |
} | |
return Ok(clientsQuery); | |
} |
This file contains hidden or 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
[HttpGet("")] | |
[LightQuery] | |
[Authorize(Policy = DanglIdentityConstants.Authorization.AUTHENTICATION_CONNECTOR_OR_ADMIN_POLICY_NAME)] | |
[ProducesResponseType(typeof(PaginationResult<ClientGet>), 200)] | |
[ProducesResponseType(typeof(ClientGet), 200)] | |
public IActionResult GetAllClients(string filter = null) | |
{ | |
var clientsQuery = _identityServerClientsRepository.GetAllClients() | |
.ProjectTo<ClientGet>(); | |
if (!string.IsNullOrWhiteSpace(filter)) | |
{ | |
clientsQuery = clientsQuery.Where(c => c.ClientName.Contains(filter) || c.ClientUri.Contains(filter)); | |
} | |
return Ok(clientsQuery); | |
} |
This file contains hidden or 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
{ | |
"operationId": "Clients_GetAllClients", | |
"responses": { | |
"200": { | |
"x-nullable": true, | |
"description": "", | |
"schema": {}, | |
"x-expectedSchemas": [ | |
{ | |
"description": "", | |
"schema": { | |
"$ref": "#/definitions/PaginationResultOfClientGet" | |
} | |
}, | |
{ | |
"description": "", | |
"schema": { | |
"type": "array", | |
"items": { | |
"$ref": "#/definitions/ClientGet" | |
} | |
} | |
} | |
] | |
} | |
} | |
} |
This file contains hidden or 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
{ | |
"operationId": "Clients_GetAllClients", | |
"responses": { | |
"200": { | |
"x-nullable": true, | |
"description": "", | |
"schema": { | |
"$ref": "#/definitions/PaginationResultOfClientGet" | |
} | |
} | |
} | |
} |
This file contains hidden or 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
// With the framework-provided ProducesResponseTypeAttribute, it just used the first response type | |
System.Threading.Tasks.Task<PaginationResult<ClientGet>> GetAllClientsAsync(string filter); | |
// While the NSwag specific attribute correctly recognized there are two schemas and thus typed the response as 'object' | |
System.Threading.Tasks.Task<object> GetAllClientsAsync(string filter); |
This file contains hidden or 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
[Fact] | |
public async Task GeneratesPaginationResultAndEnumerableForGetAllClientsAction() | |
{ | |
_url = "http://example.com/swagger/swagger.json"; | |
await MakeRequest(); | |
var responseString = await _response.Content.ReadAsStringAsync(); | |
var json = JObject.Parse(responseString); | |
var expectedSchemas = json["paths"]["/api/clients"]["get"]["responses"]["200"]["x-expectedSchemas"] as JArray; | |
Assert.NotNull(expectedSchemas); | |
Assert.Equal(2, expectedSchemas.Count); | |
} | |
[Fact] | |
public void NoControllerHasMultipleResponseTypesDefined() | |
{ | |
// There was a behavior change between NSwag 11.19 and 11.20. Previously, | |
// having two attributes of type [ResponseType] was OK and resulted in | |
// both being considered int he Swagger specification. After the update, | |
// only the first attribute was considered, thus creating incorrect | |
// Swagger clients. | |
var controllerMethods = typeof(Dangl.Identity.Startup) | |
.GetTypeInfo() | |
.Assembly | |
.DefinedTypes | |
.Where(t => typeof(Microsoft.AspNetCore.Mvc.ControllerBase).GetTypeInfo().IsAssignableFrom(t)) | |
.SelectMany(controllerType => controllerType.DeclaredMethods) | |
.ToList(); | |
Assert.NotEmpty(controllerMethods); | |
foreach (var controllerMethod in controllerMethods) | |
{ | |
var methodAttributes = controllerMethod.CustomAttributes | |
.Where(att => att.AttributeType == typeof(Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute)) | |
.Where(att => att.ConstructorArguments.Count == 2) // Only taking the overload which defines a status code and a response type | |
.Where(att => att.ConstructorArguments.Any(arg => arg.Value is int statusCode && (statusCode >= 200 && statusCode <= 299))) // Only where success status codes are defined | |
.ToList(); | |
Assert.False(methodAttributes.Count > 1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment