Simple PATCH validation in ASP.NET Web API

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."
        ]
    }
}

Ben Foster

About Me

I'm a software engineer and aspiring entrepreneur with 12+ years experience in the tech industry and have worked with startups and SMB’s in areas such as healthcare, recruitment and e-commerce (I even worked in enterprise, once). I founded my first startup Fabrik in 2011.

I now head up the engineering team at Checkout.com. If you're interested in working in an exciting fin-tech company, drop me a message on Twitter.

Creative Commons Licence