diff --git a/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj b/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj
index 0c1105d..313a7e6 100644
--- a/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj
+++ b/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj
@@ -13,7 +13,7 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Workleap.Extensions.OpenAPI/TypedResult/ExtractSchemaTypeResultFilter.cs b/src/Workleap.Extensions.OpenAPI/TypedResult/ExtractSchemaTypeResultFilter.cs
index 40d610c..32f7022 100644
--- a/src/Workleap.Extensions.OpenAPI/TypedResult/ExtractSchemaTypeResultFilter.cs
+++ b/src/Workleap.Extensions.OpenAPI/TypedResult/ExtractSchemaTypeResultFilter.cs
@@ -1,3 +1,4 @@
+using System.Net.Mime;
using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -9,7 +10,7 @@ namespace Workleap.Extensions.OpenAPI.TypedResult;
internal sealed class ExtractSchemaTypeResultFilter : IOperationFilter
{
// Based on this documentation: https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-8.0
- private const string DefaultContentType = "application/json";
+ private const string DefaultContentType = MediaTypeNames.Application.Json;
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
@@ -24,6 +25,13 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context)
// when the ProducesResponseType attribute is present.
if (operation.Responses.TryGetValue(responseMetadata.HttpCode.ToString(), out var existingResponse))
{
+ // If no content type is specified, three will be added by default: application/json, text/plain, and text/json.
+ // In this case we want to enforce the proper content type associated with the method's return type.
+ if (IsDefaultContentTypes(existingResponse.Content))
+ {
+ existingResponse.Content.Clear();
+ }
+
var canEnrichContent = !existingResponse.Content.Any() && responseMetadata.SchemaType != null;
if (!canEnrichContent)
@@ -59,6 +67,11 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context)
}
}
+ private static bool IsDefaultContentTypes(IDictionary contentTypes) =>
+ contentTypes.ContainsKey(MediaTypeNames.Application.Json) &&
+ contentTypes.ContainsKey(MediaTypeNames.Text.Plain) &&
+ contentTypes.ContainsKey("text/json");
+
internal static IEnumerable GetResponsesMetadata(Type returnType)
{
// Unwrap Task<> to get the return type
@@ -146,11 +159,9 @@ internal static HashSet ExtractResponseCodesFromAttributes(IEnumerable, BadRequest, NotFound
- else
- {
- return new(statusCode, resultType.GenericTypeArguments.First());
- }
+ return new(statusCode, resultType.GenericTypeArguments.First());
}
private static int? ExtractStatusCodeFromType(Type resultType)
diff --git a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj
index 46e158a..1c0d6f3 100644
--- a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj
+++ b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/src/tests/WebApi.OpenAPI.SystemTest/ExtractTypeResult/TypedResultNoProducesController.cs b/src/tests/WebApi.OpenAPI.SystemTest/ExtractTypeResult/TypedResultNoProducesController.cs
deleted file mode 100644
index be78c24..0000000
--- a/src/tests/WebApi.OpenAPI.SystemTest/ExtractTypeResult/TypedResultNoProducesController.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Microsoft.AspNetCore.Http.HttpResults;
-using Microsoft.AspNetCore.Mvc;
-
-namespace WebApi.OpenAPI.SystemTest.ExtractTypeResult;
-
-public class TypedResultNoProducesController
-{
- [HttpGet]
- [Route("/useApplicationJsonContentType")]
- [ProducesResponseType(StatusCodes.Status200OK, "text/plain")]
- public Ok TypedResultUseApplicationJsonContentType()
- {
- return TypedResults.Ok("example");
- }
-}
\ No newline at end of file
diff --git a/src/tests/WebApi.OpenAPI.SystemTest/ExtractTypeResult/TypedResultProperContentTypeController.cs b/src/tests/WebApi.OpenAPI.SystemTest/ExtractTypeResult/TypedResultProperContentTypeController.cs
new file mode 100644
index 0000000..a51b91c
--- /dev/null
+++ b/src/tests/WebApi.OpenAPI.SystemTest/ExtractTypeResult/TypedResultProperContentTypeController.cs
@@ -0,0 +1,65 @@
+using Microsoft.AspNetCore.Http.HttpResults;
+using Microsoft.AspNetCore.Mvc;
+
+namespace WebApi.OpenAPI.SystemTest.ExtractTypeResult;
+
+public class TypedResultProperContentTypeController
+{
+ [HttpGet]
+ [EndpointName("OkNoContentType")]
+ [Route("/useApplicationJsonContentTypeWithOk")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public Ok GivenOkTypedResultAndNoContenTypeThenContentTypeApplicationJson()
+ {
+ return TypedResults.Ok();
+ }
+
+ [HttpGet]
+ [EndpointName("TemplatedOkNoContentType")]
+ [Route("/useApplicationJsonContentTypeWithOk")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public Ok GivenTemplatedOkTypedResultAndNoContenTypeThenContentTypeApplicationJson()
+ {
+ return TypedResults.Ok("example");
+ }
+
+ [HttpGet]
+ [EndpointName("ResultsNoContentType")]
+ [Route("/useApplicationJsonContentTypeWithResultsType")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, "text/plain")]
+ [ProducesResponseType(StatusCodes.Status404NotFound, "text/plain")]
+ public Results, BadRequest, NotFound> GivenResultsTypeAndNoContentTypeThenContentTypeApplicationJson()
+ {
+ return TypedResults.Ok("example");
+ }
+
+ [HttpGet]
+ [EndpointName("OkContentTypeTextPlain")]
+ [Route("/overwriteContenTypeWithProduceAttributeTextPlainForOk")]
+ [ProducesResponseType(StatusCodes.Status200OK, "text/plain")]
+ public Ok GivenOkTypedResultAndContentTypeThenKeepContentType()
+ {
+ return TypedResults.Ok();
+ }
+
+ [HttpGet]
+ [EndpointName("TemplatedOkContentTypeTextPlain")]
+ [Route("/overwriteContenTypeWithProduceAttributeTextPlainForOk")]
+ [ProducesResponseType(StatusCodes.Status200OK, "text/plain")]
+ public Ok GivenTemplatedOkTypedResultAndContentTypeThenKeepContentType()
+ {
+ return TypedResults.Ok("example");
+ }
+
+ [HttpGet]
+ [EndpointName("ResultsContentTypeTextPlain")]
+ [Route("/overwriteContenTypeWithProduceAttributeTextPlainForResultsType")]
+ [ProducesResponseType(StatusCodes.Status200OK, "text/plain")]
+ [ProducesResponseType(StatusCodes.Status400BadRequest, "text/plain")]
+ [ProducesResponseType(StatusCodes.Status404NotFound, "text/plain")]
+ public Results, BadRequest, NotFound> GivenResultsTypeAndContentTypeThenKeepContentType()
+ {
+ return TypedResults.Ok("example");
+ }
+}
\ No newline at end of file
diff --git a/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj
index 09f8f37..6613e65 100644
--- a/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj
+++ b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj
@@ -10,8 +10,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml b/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml
index 5c446f3..a0f3142 100644
--- a/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml
+++ b/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml
@@ -25,7 +25,7 @@ paths:
operationId: GetExplicitOperationIdInName
responses:
'200':
- description: Success
+ description: OK
/explicitOperationIdInSwagger:
get:
tags:
@@ -33,7 +33,7 @@ paths:
operationId: GetExplicitOperationIdInSwagger
responses:
'200':
- description: Success
+ description: OK
/noOperationId:
get:
tags:
@@ -41,7 +41,7 @@ paths:
operationId: GetNotOperationId
responses:
'200':
- description: Success
+ description: OK
/recordClassRequiredType:
get:
tags:
@@ -49,7 +49,7 @@ paths:
operationId: GetRecordClassRequiredType
responses:
'200':
- description: Success
+ description: OK
content:
application/json:
schema:
@@ -61,7 +61,7 @@ paths:
operationId: GetClassRequiredType
responses:
'200':
- description: Success
+ description: OK
content:
application/json:
schema:
@@ -75,7 +75,6 @@ paths:
- name: id
in: path
required: true
- style: simple
schema:
type: integer
format: int32
@@ -103,7 +102,6 @@ paths:
- name: id
in: query
required: true
- style: form
schema:
type: integer
format: int32
@@ -130,7 +128,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -149,13 +146,12 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
responses:
'200':
- description: Success
+ description: OK
content:
application/json:
schema:
@@ -180,7 +176,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -207,7 +202,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -230,7 +224,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -261,7 +254,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -284,7 +276,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -315,7 +306,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -332,13 +322,12 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
responses:
'200':
- description: Success
+ description: OK
/withSwaggerResponseAnnotation:
get:
tags:
@@ -359,7 +348,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -382,7 +370,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -413,7 +400,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -444,13 +430,12 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
responses:
'200':
- description: Success
+ description: OK
'202':
description: Accepted
content:
@@ -481,10 +466,19 @@ paths:
application/json:
schema:
type: string
- /useApplicationJsonContentType:
+ /useApplicationJsonContentTypeWithOk:
get:
tags:
- - TypedResultNoProduces
+ - TypedResultProperContentType
+ operationId: OkNoContentType
+ responses:
+ '200':
+ description: OK
+ /useApplicationJsonContentTypeWithOk:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: TemplatedOkNoContentType
responses:
'200':
description: OK
@@ -492,6 +486,78 @@ paths:
application/json:
schema:
type: string
+ /useApplicationJsonContentTypeWithResultsType:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: ResultsNoContentType
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: string
+ '400':
+ description: Bad Request
+ content:
+ text/plain:
+ schema:
+ type: string
+ '404':
+ description: Not Found
+ content:
+ text/plain:
+ schema:
+ type: string
+ /overwriteContenTypeWithProduceAttributeTextPlainForOk:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: OkContentTypeTextPlain
+ responses:
+ '200':
+ description: OK
+ content:
+ text/plain:
+ schema:
+ type: string
+ /overwriteContenTypeWithProduceAttributeTextPlainForOk:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: TemplatedOkContentTypeTextPlain
+ responses:
+ '200':
+ description: OK
+ content:
+ text/plain:
+ schema:
+ type: string
+ /overwriteContenTypeWithProduceAttributeTextPlainForResultsType:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: ResultsContentTypeTextPlain
+ responses:
+ '200':
+ description: OK
+ content:
+ text/plain:
+ schema:
+ type: string
+ '400':
+ description: Bad Request
+ content:
+ text/plain:
+ schema:
+ type: string
+ '404':
+ description: Not Found
+ content:
+ text/plain:
+ schema:
+ type: string
components:
schemas:
OperationEnum:
diff --git a/src/tests/Workleap.Extensions.OpenAPI.Analyzers.Tests/Workleap.Extensions.OpenAPI.Analyzers.Tests.csproj b/src/tests/Workleap.Extensions.OpenAPI.Analyzers.Tests/Workleap.Extensions.OpenAPI.Analyzers.Tests.csproj
index e559d78..c93e00e 100644
--- a/src/tests/Workleap.Extensions.OpenAPI.Analyzers.Tests/Workleap.Extensions.OpenAPI.Analyzers.Tests.csproj
+++ b/src/tests/Workleap.Extensions.OpenAPI.Analyzers.Tests/Workleap.Extensions.OpenAPI.Analyzers.Tests.csproj
@@ -12,7 +12,7 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/tests/expected-openapi-document.yaml b/src/tests/expected-openapi-document.yaml
index 5c446f3..a0f3142 100644
--- a/src/tests/expected-openapi-document.yaml
+++ b/src/tests/expected-openapi-document.yaml
@@ -25,7 +25,7 @@ paths:
operationId: GetExplicitOperationIdInName
responses:
'200':
- description: Success
+ description: OK
/explicitOperationIdInSwagger:
get:
tags:
@@ -33,7 +33,7 @@ paths:
operationId: GetExplicitOperationIdInSwagger
responses:
'200':
- description: Success
+ description: OK
/noOperationId:
get:
tags:
@@ -41,7 +41,7 @@ paths:
operationId: GetNotOperationId
responses:
'200':
- description: Success
+ description: OK
/recordClassRequiredType:
get:
tags:
@@ -49,7 +49,7 @@ paths:
operationId: GetRecordClassRequiredType
responses:
'200':
- description: Success
+ description: OK
content:
application/json:
schema:
@@ -61,7 +61,7 @@ paths:
operationId: GetClassRequiredType
responses:
'200':
- description: Success
+ description: OK
content:
application/json:
schema:
@@ -75,7 +75,6 @@ paths:
- name: id
in: path
required: true
- style: simple
schema:
type: integer
format: int32
@@ -103,7 +102,6 @@ paths:
- name: id
in: query
required: true
- style: form
schema:
type: integer
format: int32
@@ -130,7 +128,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -149,13 +146,12 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
responses:
'200':
- description: Success
+ description: OK
content:
application/json:
schema:
@@ -180,7 +176,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -207,7 +202,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -230,7 +224,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -261,7 +254,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -284,7 +276,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -315,7 +306,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -332,13 +322,12 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
responses:
'200':
- description: Success
+ description: OK
/withSwaggerResponseAnnotation:
get:
tags:
@@ -359,7 +348,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -382,7 +370,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -413,7 +400,6 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
@@ -444,13 +430,12 @@ paths:
parameters:
- name: id
in: query
- style: form
schema:
type: integer
format: int32
responses:
'200':
- description: Success
+ description: OK
'202':
description: Accepted
content:
@@ -481,10 +466,19 @@ paths:
application/json:
schema:
type: string
- /useApplicationJsonContentType:
+ /useApplicationJsonContentTypeWithOk:
get:
tags:
- - TypedResultNoProduces
+ - TypedResultProperContentType
+ operationId: OkNoContentType
+ responses:
+ '200':
+ description: OK
+ /useApplicationJsonContentTypeWithOk:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: TemplatedOkNoContentType
responses:
'200':
description: OK
@@ -492,6 +486,78 @@ paths:
application/json:
schema:
type: string
+ /useApplicationJsonContentTypeWithResultsType:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: ResultsNoContentType
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: string
+ '400':
+ description: Bad Request
+ content:
+ text/plain:
+ schema:
+ type: string
+ '404':
+ description: Not Found
+ content:
+ text/plain:
+ schema:
+ type: string
+ /overwriteContenTypeWithProduceAttributeTextPlainForOk:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: OkContentTypeTextPlain
+ responses:
+ '200':
+ description: OK
+ content:
+ text/plain:
+ schema:
+ type: string
+ /overwriteContenTypeWithProduceAttributeTextPlainForOk:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: TemplatedOkContentTypeTextPlain
+ responses:
+ '200':
+ description: OK
+ content:
+ text/plain:
+ schema:
+ type: string
+ /overwriteContenTypeWithProduceAttributeTextPlainForResultsType:
+ get:
+ tags:
+ - TypedResultProperContentType
+ operationId: ResultsContentTypeTextPlain
+ responses:
+ '200':
+ description: OK
+ content:
+ text/plain:
+ schema:
+ type: string
+ '400':
+ description: Bad Request
+ content:
+ text/plain:
+ schema:
+ type: string
+ '404':
+ description: Not Found
+ content:
+ text/plain:
+ schema:
+ type: string
components:
schemas:
OperationEnum: