Skip to content

Commit 0ff62f5

Browse files
committed
Use all content-types
1 parent 4a540e5 commit 0ff62f5

File tree

2 files changed

+49
-33
lines changed

2 files changed

+49
-33
lines changed

src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ internal static List<ApiResponseType> ReadResponseMetadata(
140140
{
141141
// ProducesResponseTypeAttribute's constructor defaults to setting "Type" to void when no value is specified.
142142
// In this event, use the action's return type for 200 or 201 status codes. This lets you decorate an action with a
143-
// [ProducesResponseType(201)] instead of [ProducesResponseType(201, typeof(Person)] when typeof(Person) can be inferred
143+
// [ProducesResponseType(201)] instead of [ProducesResponseType(typeof(Person), 201] when typeof(Person) can be inferred
144144
// from the return type.
145145
apiResponseType.Type = type;
146146
}
@@ -159,6 +159,7 @@ internal static List<ApiResponseType> ReadResponseMetadata(
159159

160160
if (apiResponseType.Type != null)
161161
{
162+
AddApiResponseFormats(apiResponseType.ApiResponseFormats, contentTypes);
162163
results[apiResponseType.StatusCode] = apiResponseType;
163164
}
164165
}
@@ -167,6 +168,17 @@ internal static List<ApiResponseType> ReadResponseMetadata(
167168
return results.Values.ToList();
168169
}
169170

171+
internal static void AddApiResponseFormats(IList<ApiResponseFormat> apiResponseFormats, IReadOnlyList<string> contentTypes)
172+
{
173+
foreach (var contentType in contentTypes)
174+
{
175+
apiResponseFormats.Add(new ApiResponseFormat
176+
{
177+
MediaType = contentType,
178+
});
179+
}
180+
}
181+
170182
private void CalculateResponseFormats(ICollection<ApiResponseType> responseTypes, MediaTypeCollection declaredContentTypes)
171183
{
172184
var responseTypeMetadataProviders = _mvcOptions.OutputFormatters.OfType<IApiResponseTypeMetadataProvider>();
@@ -182,6 +194,9 @@ private void CalculateResponseFormats(ICollection<ApiResponseType> responseTypes
182194

183195
foreach (var apiResponse in responseTypes)
184196
{
197+
// ApiResponseFormats has been preset for EndpointMetadataApiDescriptionProvider's use but MVC has more inputs to consider.
198+
apiResponse.ApiResponseFormats.Clear();
199+
185200
var responseType = apiResponse.Type;
186201
if (responseType == null || responseType == typeof(void))
187202
{

src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ private static void AddSupportedResponseTypes(IList<ApiResponseType> supportedRe
168168
responseType = awaitableInfo.ResultType;
169169
}
170170

171+
// Can't determine anything about IResults yet that's not from extra metadata. IResult<T> could help here.
172+
if (typeof(IResult).IsAssignableFrom(responseType))
173+
{
174+
responseType = typeof(void);
175+
}
176+
171177
var responseMetadata = endpointMetadata.GetOrderedMetadata<IApiResponseMetadataProvider>();
172178
var errorMetadata = endpointMetadata.GetMetadata<ProducesErrorResponseTypeAttribute>();
173179
var defaultErrorType = errorMetadata?.Type ?? typeof(void);
@@ -179,7 +185,12 @@ private static void AddSupportedResponseTypes(IList<ApiResponseType> supportedRe
179185
{
180186
foreach (var apiResponseType in responseMetadataTypes)
181187
{
182-
AddApiResponseFormats(apiResponseType.ApiResponseFormats, contentTypes);
188+
if (apiResponseType.ApiResponseFormats.Count == 0 &&
189+
CreateDefaultApiResponseFormat(responseType) is { } defaultResponseFormat)
190+
{
191+
apiResponseType.ApiResponseFormats.Add(defaultResponseFormat);
192+
}
193+
183194
supportedResponseTypes.Add(apiResponseType);
184195
}
185196
}
@@ -190,57 +201,47 @@ private static void AddSupportedResponseTypes(IList<ApiResponseType> supportedRe
190201

191202
if (contentTypes.Count > 0)
192203
{
193-
// If metadata provided us with response formats, use that instead of the defaults.
204+
// If metadata provided us with response formats, use that instead of the default.
194205
defaultApiResponseType.ApiResponseFormats.Clear();
195-
AddApiResponseFormats(defaultApiResponseType.ApiResponseFormats, contentTypes);
206+
ApiResponseTypeProvider.AddApiResponseFormats(defaultApiResponseType.ApiResponseFormats, contentTypes);
196207
}
197208

198209
supportedResponseTypes.Add(defaultApiResponseType);
199210
}
200211
}
201212

202213
private static ApiResponseType CreateDefaultApiResponseType(Type responseType)
214+
{
215+
var apiResponseType = new ApiResponseType
216+
{
217+
ModelMetadata = new EndpointModelMetadata(ModelMetadataIdentity.ForType(responseType)),
218+
StatusCode = 200,
219+
};
220+
221+
if (CreateDefaultApiResponseFormat(responseType) is { } responseFormat)
222+
{
223+
apiResponseType.ApiResponseFormats.Add(responseFormat);
224+
}
225+
226+
return apiResponseType;
227+
}
228+
229+
private static ApiResponseFormat? CreateDefaultApiResponseFormat(Type responseType)
203230
{
204231
if (responseType == typeof(void) || typeof(IResult).IsAssignableFrom(responseType))
205232
{
206-
// Can't determine anything about IResults yet that's not from extra metadata. IResult<T> could help here.
207-
return new ApiResponseType
208-
{
209-
ModelMetadata = new EndpointModelMetadata(ModelMetadataIdentity.ForType(typeof(void))),
210-
StatusCode = 200,
211-
};
233+
return null;
212234
}
213235
else if (responseType == typeof(string))
214236
{
215237
// This uses HttpResponse.WriteAsync(string) method which doesn't set a content type. It could be anything,
216238
// but I think "text/plain" is a reasonable assumption if nothing else is specified with metadata.
217-
return new ApiResponseType
218-
{
219-
ApiResponseFormats = { new ApiResponseFormat { MediaType = "text/plain" } },
220-
ModelMetadata = new EndpointModelMetadata(ModelMetadataIdentity.ForType(typeof(string))),
221-
StatusCode = 200,
222-
};
239+
return new ApiResponseFormat { MediaType = "text/plain" };
223240
}
224241
else
225242
{
226243
// Everything else is written using HttpResponse.WriteAsJsonAsync<TValue>(T).
227-
return new ApiResponseType
228-
{
229-
ApiResponseFormats = { new ApiResponseFormat { MediaType = "application/json" } },
230-
ModelMetadata = new EndpointModelMetadata(ModelMetadataIdentity.ForType(responseType)),
231-
StatusCode = 200,
232-
};
233-
}
234-
}
235-
236-
private static void AddApiResponseFormats(IList<ApiResponseFormat> apiResponseFormats, MediaTypeCollection contentTypes)
237-
{
238-
foreach (var contentType in contentTypes)
239-
{
240-
apiResponseFormats.Add(new ApiResponseFormat
241-
{
242-
MediaType = contentType,
243-
});
244+
return new ApiResponseFormat { MediaType = "application/json" };
244245
}
245246
}
246247
}

0 commit comments

Comments
 (0)