@@ -623,4 +623,172 @@ async Task ValidPublicPropertyStillValidated(Endpoint endpoint)
623623 }
624624 } ) ;
625625 }
626+
627+ [ Fact ]
628+ public async Task ValidatesPropertiesWithJsonIgnoreWhenWritingConditions ( )
629+ {
630+ // Arrange
631+ var source = """
632+ using System;
633+ using System.ComponentModel.DataAnnotations;
634+ using System.Collections.Generic;
635+ using System.Threading.Tasks;
636+ using Microsoft.AspNetCore.Builder;
637+ using Microsoft.AspNetCore.Http;
638+ using Microsoft.Extensions.Validation;
639+ using Microsoft.AspNetCore.Routing;
640+ using Microsoft.Extensions.DependencyInjection;
641+ using Microsoft.AspNetCore.Mvc;
642+ using System.Text.Json.Serialization;
643+
644+ var builder = WebApplication.CreateBuilder();
645+
646+ builder.Services.AddValidation();
647+
648+ var app = builder.Build();
649+
650+ app.MapPost("/json-ignore-conditions", (JsonIgnoreConditionsModel model) => Results.Ok("Passed"!));
651+
652+ app.Run();
653+
654+ public class JsonIgnoreConditionsModel
655+ {
656+ // JsonIgnore without Condition defaults to Always - should be ignored
657+ [JsonIgnore]
658+ [MaxLength(10)]
659+ public string? PropertyWithJsonIgnoreOnly { get; set; }
660+
661+ // JsonIgnoreCondition.Always - should be ignored
662+ [JsonIgnore(Condition = JsonIgnoreCondition.Always)]
663+ [MaxLength(10)]
664+ public string? PropertyWithAlways { get; set; }
665+
666+ // JsonIgnoreCondition.WhenWritingDefault - should be validated (only affects writing)
667+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
668+ [MaxLength(10)]
669+ public string? PropertyWithWhenWritingDefault { get; set; }
670+
671+ // JsonIgnoreCondition.WhenWritingNull - should be validated (only affects writing)
672+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
673+ [MaxLength(10)]
674+ public string? PropertyWithWhenWritingNull { get; set; }
675+
676+ // JsonIgnoreCondition.Never - should be validated (never ignored)
677+ [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
678+ [MaxLength(10)]
679+ public string? PropertyWithNever { get; set; }
680+
681+ // JsonIgnoreCondition.WhenReading - should be ignored (affects reading/deserialization)
682+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenReading)]
683+ [MaxLength(10)]
684+ public string? PropertyWithWhenReading { get; set; }
685+ }
686+ """ ;
687+ await Verify ( source , out var compilation ) ;
688+ await VerifyEndpoint ( compilation , "/json-ignore-conditions" , async ( endpoint , serviceProvider ) =>
689+ {
690+ // Test that WhenWritingDefault is validated
691+ await InvalidPropertyWithWhenWritingDefaultProducesError ( endpoint ) ;
692+
693+ // Test that WhenWritingNull is validated
694+ await InvalidPropertyWithWhenWritingNullProducesError ( endpoint ) ;
695+
696+ // Test that Never is validated
697+ await InvalidPropertyWithNeverProducesError ( endpoint ) ;
698+
699+ // Test that Always and JsonIgnore (without condition) are NOT validated (no error expected)
700+ await InvalidPropertiesWithAlwaysAndDefaultAreIgnored ( endpoint ) ;
701+
702+ // Test that WhenReading is NOT validated (no error expected)
703+ await InvalidPropertyWithWhenReadingIsIgnored ( endpoint ) ;
704+
705+ async Task InvalidPropertyWithWhenWritingDefaultProducesError ( Endpoint endpoint )
706+ {
707+ var payload = """
708+ {
709+ "PropertyWithWhenWritingDefault": "ExceedsMaxLength"
710+ }
711+ """ ;
712+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
713+
714+ await endpoint . RequestDelegate ( context ) ;
715+
716+ var problemDetails = await AssertBadRequest ( context ) ;
717+ Assert . Collection ( problemDetails . Errors , kvp =>
718+ {
719+ Assert . Equal ( "PropertyWithWhenWritingDefault" , kvp . Key ) ;
720+ Assert . Contains ( "maximum length" , kvp . Value . Single ( ) ) ;
721+ } ) ;
722+ }
723+
724+ async Task InvalidPropertyWithWhenWritingNullProducesError ( Endpoint endpoint )
725+ {
726+ var payload = """
727+ {
728+ "PropertyWithWhenWritingNull": "ExceedsMaxLength"
729+ }
730+ """ ;
731+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
732+
733+ await endpoint . RequestDelegate ( context ) ;
734+
735+ var problemDetails = await AssertBadRequest ( context ) ;
736+ Assert . Collection ( problemDetails . Errors , kvp =>
737+ {
738+ Assert . Equal ( "PropertyWithWhenWritingNull" , kvp . Key ) ;
739+ Assert . Contains ( "maximum length" , kvp . Value . Single ( ) ) ;
740+ } ) ;
741+ }
742+
743+ async Task InvalidPropertyWithNeverProducesError ( Endpoint endpoint )
744+ {
745+ var payload = """
746+ {
747+ "PropertyWithNever": "ExceedsMaxLength"
748+ }
749+ """ ;
750+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
751+
752+ await endpoint . RequestDelegate ( context ) ;
753+
754+ var problemDetails = await AssertBadRequest ( context ) ;
755+ Assert . Collection ( problemDetails . Errors , kvp =>
756+ {
757+ Assert . Equal ( "PropertyWithNever" , kvp . Key ) ;
758+ Assert . Contains ( "maximum length" , kvp . Value . Single ( ) ) ;
759+ } ) ;
760+ }
761+
762+ async Task InvalidPropertiesWithAlwaysAndDefaultAreIgnored ( Endpoint endpoint )
763+ {
764+ var payload = """
765+ {
766+ "PropertyWithJsonIgnoreOnly": "ExceedsMaxLength",
767+ "PropertyWithAlways": "ExceedsMaxLength"
768+ }
769+ """ ;
770+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
771+
772+ await endpoint . RequestDelegate ( context ) ;
773+
774+ // Should succeed because these properties are ignored during validation
775+ Assert . Equal ( 200 , context . Response . StatusCode ) ;
776+ }
777+
778+ async Task InvalidPropertyWithWhenReadingIsIgnored ( Endpoint endpoint )
779+ {
780+ var payload = """
781+ {
782+ "PropertyWithWhenReading": "ExceedsMaxLength"
783+ }
784+ """ ;
785+ var context = CreateHttpContextWithPayload ( payload , serviceProvider ) ;
786+
787+ await endpoint . RequestDelegate ( context ) ;
788+
789+ // Should succeed because this property is ignored during reading/deserialization
790+ Assert . Equal ( 200 , context . Response . StatusCode ) ;
791+ }
792+ } ) ;
793+ }
626794}
0 commit comments