1- namespace OpenAPI . WebApiGenerator . CodeGeneration ;
1+ using System ;
2+ using Microsoft . OpenApi ;
3+
4+ namespace OpenAPI . WebApiGenerator . CodeGeneration ;
25
36internal sealed class HttpRequestExtensionsGenerator (
7+ OpenApiSpecVersion openApiVersion ,
48 string @namespace )
59{
610 private const string HttpRequestExtensionsClassName = "HttpRequestExtensions" ;
11+
12+ private readonly string _openApiVersion = openApiVersion switch
13+ {
14+ OpenApiSpecVersion . OpenApi2_0 => "2.0" ,
15+ OpenApiSpecVersion . OpenApi3_0 => "3.0" ,
16+ OpenApiSpecVersion . OpenApi3_1 => "3.1" ,
17+ _ => throw new ArgumentOutOfRangeException ( nameof ( openApiVersion ) , openApiVersion , "Unknown OpenAPI version" )
18+ } ;
719
820 internal string CreateBindParameterInvocation (
921 string requestVariableName ,
@@ -14,6 +26,7 @@ internal string CreateBindParameterInvocation(
1426 $ """"
1527 { @namespace } .{ HttpRequestExtensionsClassName } .Bind<{ bindingTypeName } >(
1628 { requestVariableName } ,
29+ "{ _openApiVersion } ",
1730 """
1831 { parameterSpecificationAsJson }
1932 """)
@@ -37,37 +50,39 @@ internal SourceCode GenerateHttpRequestExtensionsClass() =>
3750 $$$ """"
3851 #nullable enable
3952 using System.Collections.Concurrent;
53+ using System.Diagnostics.CodeAnalysis;
4054 using System.Text.Json;
4155 using Corvus.Json;
4256 using Microsoft.AspNetCore.Http;
4357 using Microsoft.Extensions.Primitives;
44- using OpenAPI.ParameterStyleParsers.OpenApi20;
45- using OpenAPI.ParameterStyleParsers.OpenApi20.ParameterParsers;
58+ using OpenAPI.ParameterStyleParsers;
4659
4760 namespace {{{ @namespace }}} ;
4861
4962 internal static class {{{ HttpRequestExtensionsClassName }}}
5063 {
51- private static readonly ConcurrentDictionary<Parameter, ParameterValueParser> ParserCache = new();
64+ private static readonly ConcurrentDictionary<IParameter, IParameterValueParser> ParserCache = new();
65+ private static IParameterValueParser GetParser(IParameter parameter) => ParserCache.GetOrAdd(parameter, _ => parameter.CreateParameterValueParser());
5266
5367 /// <summary>
5468 /// Binds an http parameter to a json type
5569 /// </summary>
5670 /// <param name="request"></param>
57- /// <param name="parameterSpecificationAsJson"></param>
58- /// <typeparam name="T"></typeparam>
59- /// <returns></returns>
71+ /// <param name="openApiVersion">OpenAPI Version of the specification</param>
72+ /// <param name="parameterSpecificationAsJson">OpenAPI parameter specification formatted as json</param>
73+ /// <typeparam name="T">The type to bind</typeparam>
74+ /// <returns>The bound instance</returns>
6075 /// <exception cref="BadHttpRequestException"></exception>
6176 internal static T Bind<T>(this HttpRequest request,
77+ string openApiVersion,
6278 string parameterSpecificationAsJson)
6379 where T : struct, IJsonValue<T>
6480 {
65- var parameter = Parameter.FromOpenApi20ParameterSpecification( parameterSpecificationAsJson);
81+ var parameter = ParameterFactory.OpenApi(openApiVersion, parameterSpecificationAsJson);
6682 return parameter switch
6783 {
6884 _ when parameter.InBody => T.Parse(request.BodyReader.AsStream()),
69- _ when TryGetValue(request, parameter, out var stringValue) =>
70- Parse<T>(parameter, stringValue),
85+ _ when TryParse<T>(request, parameter, out var value) => value.Value,
7186 _ => T.Undefined
7287 };
7388 }
@@ -82,79 +97,90 @@ internal static async Task<T> BindBodyAsync<T>(this HttpRequest request,
8297 return T.FromJson(document.RootElement.Clone());
8398 }
8499
85-
86- private static T Parse<T>(Parameter parameter, string? stringValue)
87- where T : struct, IJsonValue<T>
88- {
89- var parser = ParserCache.GetOrAdd(parameter, ParameterValueParser.Create);
90- if (!parser.TryParse(stringValue, out var instance, out var error))
91- {
92- throw new BadHttpRequestException(error);
93- }
94-
95- return instance == null ? T.Null : T.Parse(instance.ToJsonString());
96- }
97-
98- private static bool TryGetValue(this HttpRequest request, Parameter parameter, out string? stringValue) =>
100+ private static bool TryParse<T>(this HttpRequest request, IParameter parameter, [NotNullWhen(true)] out T? value)
101+ where T : struct, IJsonValue<T> =>
99102 parameter switch
100103 {
101- _ when parameter.InHeader => TryGetHeaderValue (request.Headers, parameter, out stringValue ),
102- _ when parameter.InFormData => TryGetFormDataValue (request.Form, parameter, out stringValue ),
103- _ when parameter.InPath => TryGetPathValue (request.RouteValues, parameter, out stringValue ),
104- _ when parameter.InQuery => TryGetQueryValue (request.Query, parameter, out stringValue ),
104+ _ when parameter.InHeader => TryParseHeader<T> (request.Headers, parameter, out value ),
105+ _ when parameter.InFormData => TryParseForm<T> (request.Form, parameter, out value ),
106+ _ when parameter.InPath => TryParsePath<T> (request.RouteValues, parameter, out value ),
107+ _ when parameter.InQuery => TryParseQuery<T> (request.Query, parameter, out value ),
105108 _ => throw new InvalidOperationException($"Parameter {parameter.Name} has an unknown location")
106109 };
107110
108- private static bool TryGetQueryValue(IQueryCollection query, Parameter parameter, out string? stringValue)
111+ private static bool TryParseQuery<T>(IQueryCollection query, IParameter parameter, [NotNullWhen(true)] out T? value)
112+ where T : struct, IJsonValue<T>
109113 {
110- stringValue = null;
114+ value = null;
111115 return query.TryGetValue(parameter.Name, out var values) &&
112- TryGetValue (values, parameter, out stringValue );
116+ TryParse<T> (values, parameter, out value );
113117 }
114118
115- private static bool TryGetPathValue(RouteValueDictionary requestPath, Parameter parameter, out string? stringValue)
119+ private static bool TryParsePath<T>(RouteValueDictionary requestPath, IParameter parameter, [NotNullWhen(true)] out T? value)
120+ where T : struct, IJsonValue<T>
116121 {
117- if (!requestPath.TryGetValue(parameter.Name, out var value ))
122+ if (!requestPath.TryGetValue(parameter.Name, out var objValue ))
118123 {
119- stringValue = null ;
124+ value = default ;
120125 return false;
121126 }
122127
123- stringValue = value switch
128+ var stringValue = objValue switch
124129 {
125130 null => null,
126131 string strValue => strValue,
127132 _ => throw new InvalidOperationException(
128- $"Route value of '{value }' with type '{value .GetType()}' is not supported")
133+ $"Route value of '{objValue }' with type '{objValue .GetType()}' is not supported")
129134 };
135+
136+ var parser = GetParser(parameter);
137+ value = Parse<T>(parser, stringValue);
130138 return true;
131139 }
132140
133- private static bool TryGetFormDataValue(IFormCollection requestForm, Parameter parameter, out string? stringValue)
141+ private static bool TryParseForm<T>(IFormCollection requestForm, IParameter parameter, [NotNullWhen(true)] out T? value)
142+ where T : struct, IJsonValue<T>
134143 {
135- stringValue = null ;
136- return requestForm.TryGetValue(parameter.Name, out var values) && TryGetValue (values, parameter, out stringValue );
144+ value = default ;
145+ return requestForm.TryGetValue(parameter.Name, out var values) && TryParse<T> (values, parameter, out value );
137146 }
138147
139- private static bool TryGetHeaderValue(IHeaderDictionary headers, Parameter parameter, out string? stringValue)
148+ private static bool TryParseHeader<T>(IHeaderDictionary headers, IParameter parameter, [NotNullWhen(true)] out T? value)
149+ where T : struct, IJsonValue<T>
140150 {
141- stringValue = null ;
151+ value = default ;
142152 return headers.TryGetValue(parameter.Name, out var values) &&
143- TryGetValue (values, parameter, out stringValue );
153+ TryParse<T> (values, parameter, out value );
144154 }
145155
146- private static bool TryGetValue(StringValues values, Parameter parameter, out string? stringValue)
156+ private static bool TryParse<T>(StringValues values, IParameter parameter, [NotNullWhen(true)] out T? value)
157+ where T : struct, IJsonValue<T>
147158 {
148159 if (values.Count == 0)
149160 {
150- stringValue = null ;
161+ value = default ;
151162 return false;
152163 }
153- stringValue = parameter.ValueIncludesKey
164+
165+ var parser = GetParser(parameter);
166+ var stringValue = parser.ValueIncludesParameterName
154167 ? string.Join('&', values.Select(value => $"{parameter.Name}=${value}"))
155168 : values.Single();
169+
170+ value = Parse<T>(parser, stringValue);
156171 return true;
157172 }
173+
174+ private static T Parse<T>(IParameterValueParser parser, string? value)
175+ where T : struct, IJsonValue<T>
176+ {
177+ if (!parser.TryParse(value, out var instance, out var error))
178+ {
179+ throw new BadHttpRequestException(error);
180+ }
181+
182+ return instance == null ? T.Null : T.Parse(instance.ToJsonString());
183+ }
158184 }
159185 #nullable restore
160186 """" ) ;
0 commit comments