@@ -612,6 +612,54 @@ public static async ValueTask<MyAwaitedBindAsyncStruct> BindAsync(HttpContext co
612
612
}
613
613
}
614
614
615
+ private record struct MyBothBindAsyncStruct ( Uri Uri )
616
+ {
617
+ public static ValueTask < MyBothBindAsyncStruct > BindAsync ( HttpContext context , ParameterInfo parameter )
618
+ {
619
+ Assert . True ( parameter . ParameterType == typeof ( MyBothBindAsyncStruct ) || parameter . ParameterType == typeof ( MyBothBindAsyncStruct ? ) ) ;
620
+ Assert . Equal ( "myBothBindAsyncStruct" , parameter . Name ) ;
621
+
622
+ if ( ! Uri . TryCreate ( context . Request . Headers . Referer , UriKind . Absolute , out var uri ) )
623
+ {
624
+ throw new BadHttpRequestException ( "The request is missing the required Referer header." ) ;
625
+ }
626
+
627
+ return new ( result : new ( uri ) ) ;
628
+ }
629
+
630
+ // BindAsync with ParameterInfo is preferred
631
+ public static ValueTask < MyBothBindAsyncStruct > BindAsync ( HttpContext context )
632
+ {
633
+ throw new NotImplementedException ( ) ;
634
+ }
635
+ }
636
+
637
+ private record struct MySimpleBindAsyncStruct ( Uri Uri )
638
+ {
639
+ public static ValueTask < MySimpleBindAsyncStruct > BindAsync ( HttpContext context )
640
+ {
641
+ if ( ! Uri . TryCreate ( context . Request . Headers . Referer , UriKind . Absolute , out var uri ) )
642
+ {
643
+ throw new BadHttpRequestException ( "The request is missing the required Referer header." ) ;
644
+ }
645
+
646
+ return new ( result : new ( uri ) ) ;
647
+ }
648
+ }
649
+
650
+ private record MySimpleBindAsyncRecord ( Uri Uri )
651
+ {
652
+ public static ValueTask < MySimpleBindAsyncRecord ? > BindAsync ( HttpContext context )
653
+ {
654
+ if ( ! Uri . TryCreate ( context . Request . Headers . Referer , UriKind . Absolute , out var uri ) )
655
+ {
656
+ return new ( result : null ) ;
657
+ }
658
+
659
+ return new ( result : new ( uri ) ) ;
660
+ }
661
+ }
662
+
615
663
[ Theory ]
616
664
[ MemberData ( nameof ( TryParsableParameters ) ) ]
617
665
public async Task RequestDelegatePopulatesUnattributedTryParsableParametersFromRouteValue ( Delegate action , string ? routeValue , object ? expectedParameterValue )
@@ -724,6 +772,24 @@ public async Task RequestDelegateUsesBindAsyncOverTryParseGivenNullableStruct()
724
772
Assert . Equal ( new MyBindAsyncStruct ( new Uri ( "https://example.org" ) ) , httpContext . Items [ "myBindAsyncStruct" ] ) ;
725
773
}
726
774
775
+ [ Fact ]
776
+ public async Task RequestDelegateUsesParameterInfoBindAsyncOverOtherBindAsync ( )
777
+ {
778
+ var httpContext = CreateHttpContext ( ) ;
779
+
780
+ httpContext . Request . Headers . Referer = "https://example.org" ;
781
+
782
+ var resultFactory = RequestDelegateFactory . Create ( ( HttpContext httpContext , MyBothBindAsyncStruct ? myBothBindAsyncStruct ) =>
783
+ {
784
+ httpContext . Items [ "myBothBindAsyncStruct" ] = myBothBindAsyncStruct ;
785
+ } ) ;
786
+
787
+ var requestDelegate = resultFactory . RequestDelegate ;
788
+ await requestDelegate ( httpContext ) ;
789
+
790
+ Assert . Equal ( new MyBothBindAsyncStruct ( new Uri ( "https://example.org" ) ) , httpContext . Items [ "myBothBindAsyncStruct" ] ) ;
791
+ }
792
+
727
793
[ Fact ]
728
794
public async Task RequestDelegateUsesTryParseOverBindAsyncGivenExplicitAttribute ( )
729
795
{
@@ -873,7 +939,7 @@ void TestAction([FromRoute] int tryParsable, [FromRoute] int tryParsable2)
873
939
[ Fact ]
874
940
public async Task RequestDelegateLogsBindAsyncFailuresAndSets400Response ( )
875
941
{
876
- // Not supplying any headers will cause the HttpContext TryParse overload to fail .
942
+ // Not supplying any headers will cause the HttpContext BindAsync overload to return null .
877
943
var httpContext = CreateHttpContext ( ) ;
878
944
var invoked = false ;
879
945
@@ -905,7 +971,7 @@ public async Task RequestDelegateLogsBindAsyncFailuresAndSets400Response()
905
971
[ Fact ]
906
972
public async Task RequestDelegateLogsBindAsyncFailuresAndThrowsIfThrowOnBadRequest ( )
907
973
{
908
- // Not supplying any headers will cause the HttpContext TryParse overload to fail .
974
+ // Not supplying any headers will cause the HttpContext BindAsync overload to return null .
909
975
var httpContext = CreateHttpContext ( ) ;
910
976
var invoked = false ;
911
977
@@ -931,10 +997,72 @@ public async Task RequestDelegateLogsBindAsyncFailuresAndThrowsIfThrowOnBadReque
931
997
Assert . Equal ( 400 , badHttpRequestException . StatusCode ) ;
932
998
}
933
999
1000
+ [ Fact ]
1001
+ public async Task RequestDelegateLogsSingleArgBindAsyncFailuresAndSets400Response ( )
1002
+ {
1003
+ // Not supplying any headers will cause the HttpContext BindAsync overload to return null.
1004
+ var httpContext = CreateHttpContext ( ) ;
1005
+ var invoked = false ;
1006
+
1007
+ var factoryResult = RequestDelegateFactory . Create ( ( MySimpleBindAsyncRecord mySimpleBindAsyncRecord1 ,
1008
+ MySimpleBindAsyncRecord mySimpleBindAsyncRecord2 ) =>
1009
+ {
1010
+ invoked = true ;
1011
+ } ) ;
1012
+
1013
+ var requestDelegate = factoryResult . RequestDelegate ;
1014
+ await requestDelegate ( httpContext ) ;
1015
+
1016
+ Assert . False ( invoked ) ;
1017
+ Assert . False ( httpContext . RequestAborted . IsCancellationRequested ) ;
1018
+ Assert . Equal ( 400 , httpContext . Response . StatusCode ) ;
1019
+
1020
+ var logs = TestSink . Writes . ToArray ( ) ;
1021
+
1022
+ Assert . Equal ( 2 , logs . Length ) ;
1023
+
1024
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 0 ] . EventId ) ;
1025
+ Assert . Equal ( LogLevel . Debug , logs [ 0 ] . LogLevel ) ;
1026
+ Assert . Equal ( @"Required parameter ""MySimpleBindAsyncRecord mySimpleBindAsyncRecord1"" was not provided from MySimpleBindAsyncRecord.BindAsync(HttpContext)." , logs [ 0 ] . Message ) ;
1027
+
1028
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 1 ] . EventId ) ;
1029
+ Assert . Equal ( LogLevel . Debug , logs [ 1 ] . LogLevel ) ;
1030
+ Assert . Equal ( @"Required parameter ""MySimpleBindAsyncRecord mySimpleBindAsyncRecord2"" was not provided from MySimpleBindAsyncRecord.BindAsync(HttpContext)." , logs [ 1 ] . Message ) ;
1031
+ }
1032
+
1033
+ [ Fact ]
1034
+ public async Task RequestDelegateLogsSingleArgBindAsyncFailuresAndThrowsIfThrowOnBadRequest ( )
1035
+ {
1036
+ // Not supplying any headers will cause the HttpContext BindAsync overload to return null.
1037
+ var httpContext = CreateHttpContext ( ) ;
1038
+ var invoked = false ;
1039
+
1040
+ var factoryResult = RequestDelegateFactory . Create ( ( MySimpleBindAsyncRecord mySimpleBindAsyncRecord1 ,
1041
+ MySimpleBindAsyncRecord mySimpleBindAsyncRecord2 ) =>
1042
+ {
1043
+ invoked = true ;
1044
+ } , new ( ) { ThrowOnBadRequest = true } ) ;
1045
+
1046
+ var requestDelegate = factoryResult . RequestDelegate ;
1047
+ var badHttpRequestException = await Assert . ThrowsAsync < BadHttpRequestException > ( ( ) => requestDelegate ( httpContext ) ) ;
1048
+
1049
+ Assert . False ( invoked ) ;
1050
+
1051
+ // The httpContext should be untouched.
1052
+ Assert . False ( httpContext . RequestAborted . IsCancellationRequested ) ;
1053
+ Assert . Equal ( 200 , httpContext . Response . StatusCode ) ;
1054
+ Assert . False ( httpContext . Response . HasStarted ) ;
1055
+
1056
+ // We don't log bad requests when we throw.
1057
+ Assert . Empty ( TestSink . Writes ) ;
1058
+
1059
+ Assert . Equal ( @"Required parameter ""MySimpleBindAsyncRecord mySimpleBindAsyncRecord1"" was not provided from MySimpleBindAsyncRecord.BindAsync(HttpContext)." , badHttpRequestException . Message ) ;
1060
+ Assert . Equal ( 400 , badHttpRequestException . StatusCode ) ;
1061
+ }
1062
+
934
1063
[ Fact ]
935
1064
public async Task BindAsyncExceptionsAreUncaught ( )
936
1065
{
937
- // Not supplying any headers will cause the HttpContext BindAsync overload to fail.
938
1066
var httpContext = CreateHttpContext ( ) ;
939
1067
940
1068
var factoryResult = RequestDelegateFactory . Create ( ( MyBindAsyncTypeThatThrows arg1 ) => { } ) ;
@@ -2239,6 +2367,10 @@ void nullableReferenceType(HttpContext context, MyBindAsyncRecord? myBindAsyncRe
2239
2367
{
2240
2368
context . Items [ "uri" ] = myBindAsyncRecord ? . Uri ;
2241
2369
}
2370
+ void requiredReferenceTypeSimple ( HttpContext context , MySimpleBindAsyncRecord mySimpleBindAsyncRecord )
2371
+ {
2372
+ context . Items [ "uri" ] = mySimpleBindAsyncRecord . Uri ;
2373
+ }
2242
2374
2243
2375
2244
2376
void requiredValueType ( HttpContext context , MyNullableBindAsyncStruct myNullableBindAsyncStruct )
@@ -2253,11 +2385,16 @@ void nullableValueType(HttpContext context, MyNullableBindAsyncStruct? myNullabl
2253
2385
{
2254
2386
context . Items [ "uri" ] = myNullableBindAsyncStruct ? . Uri ;
2255
2387
}
2388
+ void requiredValueTypeSimple ( HttpContext context , MySimpleBindAsyncStruct mySimpleBindAsyncStruct )
2389
+ {
2390
+ context . Items [ "uri" ] = mySimpleBindAsyncStruct . Uri ;
2391
+ }
2256
2392
2257
2393
return new object ? [ ] [ ]
2258
2394
{
2259
2395
new object ? [ ] { ( Action < HttpContext , MyBindAsyncRecord > ) requiredReferenceType , false , true , false } ,
2260
2396
new object ? [ ] { ( Action < HttpContext , MyBindAsyncRecord > ) requiredReferenceType , true , false , false , } ,
2397
+ new object ? [ ] { ( Action < HttpContext , MySimpleBindAsyncRecord > ) requiredReferenceTypeSimple , true , false , false } ,
2261
2398
2262
2399
new object ? [ ] { ( Action < HttpContext , MyBindAsyncRecord ? > ) defaultReferenceType , false , false , false , } ,
2263
2400
new object ? [ ] { ( Action < HttpContext , MyBindAsyncRecord ? > ) defaultReferenceType , true , false , false } ,
@@ -2267,6 +2404,7 @@ void nullableValueType(HttpContext context, MyNullableBindAsyncStruct? myNullabl
2267
2404
2268
2405
new object ? [ ] { ( Action < HttpContext , MyNullableBindAsyncStruct > ) requiredValueType , false , true , true } ,
2269
2406
new object ? [ ] { ( Action < HttpContext , MyNullableBindAsyncStruct > ) requiredValueType , true , false , true } ,
2407
+ new object ? [ ] { ( Action < HttpContext , MySimpleBindAsyncStruct > ) requiredValueTypeSimple , true , false , true } ,
2270
2408
2271
2409
new object ? [ ] { ( Action < HttpContext , MyNullableBindAsyncStruct ? > ) defaultValueType , false , false , true } ,
2272
2410
new object ? [ ] { ( Action < HttpContext , MyNullableBindAsyncStruct ? > ) defaultValueType , true , false , true } ,
0 commit comments