@@ -7075,6 +7075,158 @@ public void Create_Populates_EndpointBuilderWithRequestDelegateAndMetadata()
7075
7075
Assert . Same ( options . EndpointBuilder . Metadata , result . EndpointMetadata ) ;
7076
7076
}
7077
7077
7078
+ private class ParameterListRequiredStringFromDifferentSources
7079
+ {
7080
+ public HttpContext ? HttpContext { get ; set ; }
7081
+
7082
+ [ FromRoute ]
7083
+ public required string RequiredRouteParam { get ; set ; }
7084
+
7085
+ [ FromQuery ]
7086
+ public required string RequiredQueryParam { get ; set ; }
7087
+
7088
+ [ FromHeader ]
7089
+ public required string RequiredHeaderParam { get ; set ; }
7090
+ }
7091
+
7092
+ [ Fact ]
7093
+ public async Task RequestDelegateFactory_AsParameters_SupportsRequiredMember ( )
7094
+ {
7095
+ // Arrange
7096
+ static void TestAction ( [ AsParameters ] ParameterListRequiredStringFromDifferentSources args ) { }
7097
+
7098
+ var httpContext = CreateHttpContext ( ) ;
7099
+
7100
+ var factoryResult = RequestDelegateFactory . Create ( TestAction ) ;
7101
+ var requestDelegate = factoryResult . RequestDelegate ;
7102
+
7103
+ // Act
7104
+ await requestDelegate ( httpContext ) ;
7105
+
7106
+ // Assert that the required modifier on members that
7107
+ // are not nullable treats them as required.
7108
+ Assert . Equal ( 400 , httpContext . Response . StatusCode ) ;
7109
+
7110
+ var logs = TestSink . Writes . ToArray ( ) ;
7111
+
7112
+ Assert . Equal ( 3 , logs . Length ) ;
7113
+
7114
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 0 ] . EventId ) ;
7115
+ Assert . Equal ( LogLevel . Debug , logs [ 0 ] . LogLevel ) ;
7116
+ Assert . Equal ( @"Required parameter ""string RequiredRouteParam"" was not provided from route." , logs [ 0 ] . Message ) ;
7117
+
7118
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 1 ] . EventId ) ;
7119
+ Assert . Equal ( LogLevel . Debug , logs [ 1 ] . LogLevel ) ;
7120
+ Assert . Equal ( @"Required parameter ""string RequiredQueryParam"" was not provided from query string." , logs [ 1 ] . Message ) ;
7121
+
7122
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 2 ] . EventId ) ;
7123
+ Assert . Equal ( LogLevel . Debug , logs [ 2 ] . LogLevel ) ;
7124
+ Assert . Equal ( @"Required parameter ""string RequiredHeaderParam"" was not provided from header." , logs [ 2 ] . Message ) ;
7125
+ }
7126
+
7127
+ private class ParameterListRequiredNullableStringFromDifferentSources
7128
+ {
7129
+ public HttpContext ? HttpContext { get ; set ; }
7130
+
7131
+ [ FromRoute ]
7132
+ public required StringValues ? RequiredRouteParam { get ; set ; }
7133
+
7134
+ [ FromQuery ]
7135
+ public required StringValues ? RequiredQueryParam { get ; set ; }
7136
+
7137
+ [ FromHeader ]
7138
+ public required StringValues ? RequiredHeaderParam { get ; set ; }
7139
+ }
7140
+
7141
+ [ Fact ]
7142
+ public async Task RequestDelegateFactory_AsParameters_SupportsNullableRequiredMember ( )
7143
+ {
7144
+ // Arrange
7145
+ static void TestAction ( [ AsParameters ] ParameterListRequiredNullableStringFromDifferentSources args )
7146
+ {
7147
+ args . HttpContext ! . Items . Add ( "RequiredRouteParam" , args . RequiredRouteParam ) ;
7148
+ args . HttpContext ! . Items . Add ( "RequiredQueryParam" , args . RequiredQueryParam ) ;
7149
+ args . HttpContext ! . Items . Add ( "RequiredHeaderParam" , args . RequiredHeaderParam ) ;
7150
+ }
7151
+
7152
+ var httpContext = CreateHttpContext ( ) ;
7153
+
7154
+ var factoryResult = RequestDelegateFactory . Create ( TestAction ) ;
7155
+ var requestDelegate = factoryResult . RequestDelegate ;
7156
+
7157
+ // Act
7158
+ await requestDelegate ( httpContext ) ;
7159
+
7160
+ // Assert that when properties are required but nullable
7161
+ // we evaluate them as optional because required members
7162
+ // must be initialized but they can be initialized to null
7163
+ // when an NRT is required.
7164
+ Assert . Equal ( 200 , httpContext . Response . StatusCode ) ;
7165
+
7166
+ Assert . Null ( httpContext . Items [ "RequiredRouteParam" ] ) ;
7167
+ Assert . Null ( httpContext . Items [ "RequiredQueryParam" ] ) ;
7168
+ Assert . Null ( httpContext . Items [ "RequiredHeaderParam" ] ) ;
7169
+ }
7170
+
7171
+ #nullable disable
7172
+ private class ParameterListMixedRequiredStringsFromDifferentSources
7173
+ {
7174
+ public HttpContext HttpContext { get ; set ; }
7175
+
7176
+ [ FromRoute ]
7177
+ public required string RequiredRouteParam { get ; set ; }
7178
+
7179
+ [ FromRoute ]
7180
+ public string OptionalRouteParam { get ; set ; }
7181
+
7182
+ [ FromQuery ]
7183
+ public required string RequiredQueryParam { get ; set ; }
7184
+
7185
+ [ FromQuery ]
7186
+ public string OptionalQueryParam { get ; set ; }
7187
+
7188
+ [ FromHeader ]
7189
+ public required string RequiredHeaderParam { get ; set ; }
7190
+
7191
+ [ FromHeader ]
7192
+ public string OptionalHeaderParam { get ; set ; }
7193
+ }
7194
+
7195
+ [ Fact ]
7196
+ public async Task RequestDelegateFactory_AsParameters_SupportsRequiredMember_NullabilityDisabled ( )
7197
+ {
7198
+ // Arange
7199
+ static void TestAction ( [ AsParameters ] ParameterListMixedRequiredStringsFromDifferentSources args ) { }
7200
+
7201
+ var httpContext = CreateHttpContext ( ) ;
7202
+
7203
+ var factoryResult = RequestDelegateFactory . Create ( TestAction ) ;
7204
+ var requestDelegate = factoryResult . RequestDelegate ;
7205
+
7206
+ // Act
7207
+ await requestDelegate ( httpContext ) ;
7208
+
7209
+ // Assert that we only execute required parameter
7210
+ // checks for members that have the required modifier
7211
+ Assert . Equal ( 400 , httpContext . Response . StatusCode ) ;
7212
+
7213
+ var logs = TestSink . Writes . ToArray ( ) ;
7214
+ Assert . Equal ( 3 , logs . Length ) ;
7215
+
7216
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 0 ] . EventId ) ;
7217
+ Assert . Equal ( LogLevel . Debug , logs [ 0 ] . LogLevel ) ;
7218
+ Assert . Equal ( @"Required parameter ""string RequiredRouteParam"" was not provided from route." , logs [ 0 ] . Message ) ;
7219
+
7220
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 1 ] . EventId ) ;
7221
+ Assert . Equal ( LogLevel . Debug , logs [ 1 ] . LogLevel ) ;
7222
+ Assert . Equal ( @"Required parameter ""string RequiredQueryParam"" was not provided from query string." , logs [ 1 ] . Message ) ;
7223
+
7224
+ Assert . Equal ( new EventId ( 4 , "RequiredParameterNotProvided" ) , logs [ 2 ] . EventId ) ;
7225
+ Assert . Equal ( LogLevel . Debug , logs [ 2 ] . LogLevel ) ;
7226
+ Assert . Equal ( @"Required parameter ""string RequiredHeaderParam"" was not provided from header." , logs [ 2 ] . Message ) ;
7227
+ }
7228
+ #nullable enable
7229
+
7078
7230
private DefaultHttpContext CreateHttpContext ( )
7079
7231
{
7080
7232
var responseFeature = new TestHttpResponseFeature ( ) ;
0 commit comments