-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathApiParameterDescriptionExtensions.cs
112 lines (95 loc) · 4.13 KB
/
ApiParameterDescriptionExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Swashbuckle.AspNetCore.SwaggerGen
{
public static class ApiParameterDescriptionExtensions
{
private static readonly Type[] RequiredAttributeTypes = new[]
{
typeof(BindRequiredAttribute),
typeof(RequiredAttribute)
};
public static bool IsRequiredParameter(this ApiParameterDescription apiParameter)
{
// From the OpenAPI spec:
// If the parameter location is "path", this property is REQUIRED and its value MUST be true.
if (apiParameter.IsFromPath())
{
return true;
}
// This is the default logic for IsRequired
bool IsRequired() => apiParameter.CustomAttributes().Any(attr => RequiredAttributeTypes.Contains(attr.GetType()));
// This is to keep compatibility with MVC controller logic that has existed in the past
if (apiParameter.ParameterDescriptor is ControllerParameterDescriptor)
{
return IsRequired();
}
// For non-controllers, prefer the IsRequired flag if we're not on netstandard 2.0, otherwise fallback to the default logic.
return
#if !NETSTANDARD2_0
apiParameter.IsRequired;
#else
IsRequired();
#endif
}
public static ParameterInfo ParameterInfo(this ApiParameterDescription apiParameter)
{
var parameterDescriptor = apiParameter.ParameterDescriptor as
#if NETCOREAPP2_2_OR_GREATER
Microsoft.AspNetCore.Mvc.Infrastructure.IParameterInfoParameterDescriptor;
#else
ControllerParameterDescriptor;
#endif
return parameterDescriptor?.ParameterInfo;
}
public static PropertyInfo PropertyInfo(this ApiParameterDescription apiParameter)
{
var modelMetadata = apiParameter.ModelMetadata;
return (modelMetadata?.ContainerType != null)
? modelMetadata.ContainerType.GetProperty(modelMetadata.PropertyName)
: null;
}
public static IEnumerable<object> CustomAttributes(this ApiParameterDescription apiParameter)
{
var propertyInfo = apiParameter.PropertyInfo();
if (propertyInfo != null) return propertyInfo.GetCustomAttributes(true);
var parameterInfo = apiParameter.ParameterInfo();
if (parameterInfo != null) return parameterInfo.GetCustomAttributes(true);
return Enumerable.Empty<object>();
}
[Obsolete("Use ParameterInfo(), PropertyInfo() and CustomAttributes() extension methods instead")]
internal static void GetAdditionalMetadata(
this ApiParameterDescription apiParameter,
ApiDescription apiDescription,
out ParameterInfo parameterInfo,
out PropertyInfo propertyInfo,
out IEnumerable<object> parameterOrPropertyAttributes)
{
parameterInfo = apiParameter.ParameterInfo();
propertyInfo = apiParameter.PropertyInfo();
parameterOrPropertyAttributes = apiParameter.CustomAttributes();
}
internal static bool IsFromPath(this ApiParameterDescription apiParameter)
{
return (apiParameter.Source == BindingSource.Path);
}
internal static bool IsFromBody(this ApiParameterDescription apiParameter)
{
return (apiParameter.Source == BindingSource.Body);
}
internal static bool IsFromForm(this ApiParameterDescription apiParameter)
{
var source = apiParameter.Source;
var elementType = apiParameter.ModelMetadata?.ElementType;
return (source == BindingSource.Form || source == BindingSource.FormFile)
|| (elementType != null && typeof(IFormFile).IsAssignableFrom(elementType));
}
}
}