11using System ;
2+ using System . Collections ;
23using System . IO ;
34using System . Threading . Tasks ;
45using JsonApiDotNetCore . Internal ;
6+ using JsonApiDotNetCore . Models ;
57using JsonApiDotNetCore . Serialization ;
68using JsonApiDotNetCore . Services ;
79using Microsoft . AspNetCore . Mvc . Formatters ;
@@ -37,7 +39,7 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
3739 {
3840 var body = GetRequestBody ( context . HttpContext . Request . Body ) ;
3941
40- object model = null ;
42+ object model = null ;
4143
4244 if ( _jsonApiContext . IsRelationshipPath )
4345 {
@@ -48,10 +50,29 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
4850 model = _deSerializer . Deserialize ( body ) ;
4951 }
5052
53+
5154 if ( model == null )
5255 {
5356 _logger ? . LogError ( "An error occurred while de-serializing the payload" ) ;
5457 }
58+
59+ if ( context . HttpContext . Request . Method == "PATCH" )
60+ {
61+ bool idMissing ;
62+ if ( model is IList list )
63+ {
64+ idMissing = CheckForId ( list ) ;
65+ }
66+ else
67+ {
68+ idMissing = CheckForId ( model ) ;
69+ }
70+ if ( idMissing )
71+ {
72+ _logger ? . LogError ( "Payload must include id attribute" ) ;
73+ throw new JsonApiException ( 400 , "Payload must include id attribute" ) ;
74+ }
75+ }
5576 return InputFormatterResult . SuccessAsync ( model ) ;
5677 }
5778 catch ( Exception ex )
@@ -62,6 +83,40 @@ public Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
6283 }
6384 }
6485
86+ /// <summary> Checks if the deserialized payload has an ID included </summary
87+ private bool CheckForId ( object model )
88+ {
89+ if ( model == null ) return false ;
90+ if ( model is ResourceObject ro )
91+ {
92+ if ( string . IsNullOrEmpty ( ro . Id ) ) return true ;
93+ }
94+ else if ( model is IIdentifiable identifiable )
95+ {
96+ if ( string . IsNullOrEmpty ( identifiable . StringId ) ) return true ;
97+ }
98+ return false ;
99+ }
100+
101+ /// <summary> Checks if the elements in the deserialized payload have an ID included </summary
102+ private bool CheckForId ( IList modelList )
103+ {
104+ foreach ( var model in modelList )
105+ {
106+ if ( model == null ) continue ;
107+ if ( model is ResourceObject ro )
108+ {
109+ if ( string . IsNullOrEmpty ( ro . Id ) ) return true ;
110+ }
111+ else if ( model is IIdentifiable identifiable )
112+ {
113+ if ( string . IsNullOrEmpty ( identifiable . StringId ) ) return true ;
114+ }
115+ }
116+ return false ;
117+
118+ }
119+
65120 private string GetRequestBody ( Stream body )
66121 {
67122 using ( var reader = new StreamReader ( body ) )
0 commit comments