Occasionally we need to issue PATCH requests against a resource.
We recently changed the way in which a cover image was specified for a publication in the Fabrik API. Previously we were using the PATCH verb to move media for a publication. Our controller looked something like this:
public void Patch(int siteId, int postId, MoveMediaCommand command)
{
var post = session.TryLoadFromSite<Domain.Post>(siteId, postId);
post.MoveMedia(command.MediaItemId, command.Position);
}
We could therefore issue the following request to make a media item the default for a publication (first one in the collection):
PATCH /sites/1/posts/10/media
{
mediaitemid: 10,
position: 0
}
In this case, position
was a required parameter and the API would return a 400 error if it was not set.
Now I needed to support passing another parameter. I wanted to allow either parameter to be set in the request body but validate at least one was present.
I could have done that validation at the controller level but even better, we can add custom validation to our model by implementing System.ComponentModel.DataAnnotations.IValidateableObject
(thanks to Tugberk for reminding of me this fact):
[DataContract]
public class PatchMediaCommand : IValidatableObject
{
[DataMember(IsRequired = true)]
[Required(ErrorMessage = "The media item identifier is required.")]
[Display(Name = "Media Item Id", Description = "Required. The id of the media item you wish to move.")]
public virtual int MediaItemId { get; set; }
[DataMember]
[Display(Name = "Position", Description = "Required. The 0-based new position of the media item.")]
public virtual int? Position { get; set; }
[DataMember]
[Display(Name = "Make Cover Image", Description = "Whether to make the media item the cover image for the publication.")]
public virtual bool? MakeCoverImage { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!(Position.HasValue || MakeCoverImage.HasValue))
{
yield return new ValidationResult("You must set either the Position or MakeCoverImage parameters.");
}
}
}
Now, if neither parameter is present in the request body the ASP.NET Web API model state validation will automatically kick in and I’ll get a 400 (Bad Request) error as expected:
{
"message": "The request is invalid.",
"modelState": {
"command":[
"You must set either the Position or MakeCoverImage parameters."
]
}
}