@@ -164,4 +164,145 @@ async Task ValidateForTopLevelInvoked()
164
164
}
165
165
} ) ;
166
166
}
167
+
168
+ [ Fact ]
169
+ public async Task CanValidateIValidatableObject_WithoutPropertyValidations ( )
170
+ {
171
+ var source = """
172
+ using System.Collections.Generic;
173
+ using System.ComponentModel.DataAnnotations;
174
+ using System.Threading.Tasks;
175
+ using Microsoft.AspNetCore.Builder;
176
+ using Microsoft.AspNetCore.Http;
177
+ using Microsoft.Extensions.Validation;
178
+ using Microsoft.AspNetCore.Mvc;
179
+ using Microsoft.AspNetCore.Routing;
180
+ using Microsoft.Extensions.DependencyInjection;
181
+
182
+ WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
183
+
184
+ builder.Services.AddValidation();
185
+
186
+ WebApplication app = builder. Build();
187
+
188
+ app.MapPost("/base", (BaseClass model) => Results.Ok(model));
189
+ app.MapPost("/derived", (DerivedClass model) => Results.Ok(model));
190
+ app.MapPost("/complex", (ComplexClass model) => Results.Ok(model));
191
+
192
+ app.Run();
193
+
194
+ public class BaseClass : IValidatableObject
195
+ {
196
+ public string? Value { get; set; }
197
+
198
+ public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
199
+ {
200
+ if (string.IsNullOrEmpty(Value))
201
+ {
202
+ yield return new ValidationResult("Value cannot be null or empty.", [nameof(Value)]);
203
+ }
204
+ }
205
+ }
206
+
207
+ public class DerivedClass : BaseClass
208
+ {
209
+ }
210
+
211
+ public class ComplexClass
212
+ {
213
+ public NestedClass? NestedObject { get; set; }
214
+ }
215
+
216
+ public class NestedClass : IValidatableObject
217
+ {
218
+ public string? Value { get; set; }
219
+ public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
220
+ {
221
+ if (string.IsNullOrEmpty(Value))
222
+ {
223
+ yield return new ValidationResult("Value cannot be null or empty.", [nameof(Value)]);
224
+ }
225
+ }
226
+ }
227
+ """ ;
228
+
229
+ await Verify ( source , out var compilation ) ;
230
+
231
+ await VerifyEndpoint ( compilation , "/base" , async ( endpoint , serviceProvider ) =>
232
+ {
233
+ await ValidateMethodCalled ( ) ;
234
+
235
+ async Task ValidateMethodCalled ( )
236
+ {
237
+ var httpContext = CreateHttpContextWithPayload ( """
238
+ {
239
+ "Value": ""
240
+ }
241
+ """ , serviceProvider ) ;
242
+
243
+ await endpoint . RequestDelegate ( httpContext ) ;
244
+
245
+ var problemDetails = await AssertBadRequest ( httpContext ) ;
246
+ Assert . Collection ( problemDetails . Errors ,
247
+ error =>
248
+ {
249
+ Assert . Equal ( "Value" , error . Key ) ;
250
+ Assert . Collection ( error . Value ,
251
+ msg => Assert . Equal ( "Value cannot be null or empty." , msg ) ) ;
252
+ } ) ;
253
+ }
254
+ } ) ;
255
+
256
+ await VerifyEndpoint ( compilation , "/derived" , async ( endpoint , serviceProvider ) =>
257
+ {
258
+ await ValidateMethodCalled ( ) ;
259
+
260
+ async Task ValidateMethodCalled ( )
261
+ {
262
+ var httpContext = CreateHttpContextWithPayload ( """
263
+ {
264
+ "Value": ""
265
+ }
266
+ """ , serviceProvider ) ;
267
+
268
+ await endpoint . RequestDelegate ( httpContext ) ;
269
+
270
+ var problemDetails = await AssertBadRequest ( httpContext ) ;
271
+ Assert . Collection ( problemDetails . Errors ,
272
+ error =>
273
+ {
274
+ Assert . Equal ( "Value" , error . Key ) ;
275
+ Assert . Collection ( error . Value ,
276
+ msg => Assert . Equal ( "Value cannot be null or empty." , msg ) ) ;
277
+ } ) ;
278
+ }
279
+ } ) ;
280
+
281
+ await VerifyEndpoint ( compilation , "/complex" , async ( endpoint , serviceProvider ) =>
282
+ {
283
+ await ValidateMethodCalled ( ) ;
284
+
285
+ async Task ValidateMethodCalled ( )
286
+ {
287
+ var httpContext = CreateHttpContextWithPayload ( """
288
+ {
289
+ "NestedObject": {
290
+ "Value": ""
291
+ }
292
+ }
293
+ """ , serviceProvider ) ;
294
+
295
+ await endpoint . RequestDelegate ( httpContext ) ;
296
+
297
+ var problemDetails = await AssertBadRequest ( httpContext ) ;
298
+ Assert . Collection ( problemDetails . Errors ,
299
+ error =>
300
+ {
301
+ Assert . Equal ( "NestedObject.Value" , error . Key ) ;
302
+ Assert . Collection ( error . Value ,
303
+ msg => Assert . Equal ( "Value cannot be null or empty." , msg ) ) ;
304
+ } ) ;
305
+ }
306
+ } ) ;
307
+ }
167
308
}
0 commit comments