2
\$\begingroup\$

I have an API endpoint to modify a race car, which supports the PUT method:

PUT /api/racecars/{racecarId} 

Since PUT must be idempotent it works as a full replacement of an entity:

[Route("api/racecars/{racecarId}")]
public class RacecarsController : Controller
{
    private readonly IRaceCarsAppService racecarsAppService;

    public RacecarsController (IRaceCarsAppService racecarsAppService)
    {
        this.racecarsAppService = racecarsAppService;
    }

    [HttpPut]
    public IActionResult Put(int racecarId, [FromBody] Racecar model)
    {
        if (model == null)
        {
            return BadRequest("Racecar model is not specified");
        }

        try
        {
            racecarsAppService.Update(racecarId, model.NextServiceDate, model.CrashesCount);

            return Ok();
        }
        catch (DomainRuleViolationException e)
        {
            return Forbidden(e.Message);
        }
    }

    protected IActionResult Forbidden(string errorMessage)
    {
        return StatusCode((int)HttpStatusCode.Forbidden, errorMessage);
    }
}

public class DomainRuleViolationException : Exception
{
    public DomainRuleViolationException(string message) : base(message)
    {
    }
}

public class Racecar
{
    public int RacecarId { get; set; }
    public int CrashesCount { get; set; }
    public DateTime NextServiceDate { get; set; }
}

public interface IRaceCarsAppService 
{
    void Update(int racecarId, DateTime nextServiceDate, int crashesCount);
}

However, our customers fairly asked to introduce partial updates for the racecar entity. Here is what I've come up with so far. First we need to introduce the Patch model with nulls:

public class RacecarPatch
{
    public int RacecarId { get; set; }
    public int? CrashesCount { get; set; }
    public DateTime? NextServiceDate { get; set; }
}

And the Patch method of the controller:

    [HttpPatch]
    public IActionResult Patch(int racecarId, [FromBody] RacecarPatch model)
    {
        if (model == null)
        {
            return BadRequest("Racecar model is not specified");
        }

        try
        {
            if (model.CrashesCount.HasValue)
            {
                racecarsAppService.UpdateCrashesCount(racecarId, model.CrashesCount.Value);
            }

            if (model.NextServiceDate.HasValue)
            {
                racecarsAppService.UpdateNextServiceDate(racecarId, model.UpdateNextServiceDate.Value);
            }

            return Ok();
        }
        catch (DomainRuleViolationException e)
        {
            return Forbidden(e.Message);
        }
    }

public interface IRaceCarsAppService 
{
    void Update(int racecarId, DateTime nextServiceDate, int crashesCount);

    void UpdateCrashesCount(int racecarId, int crashesCount);

    void UpdateNextServiceDate(int racecarId, DateTime nextServiceDate);
}

I know this is a pretty straightforward approach, so I'd be glad to know if there are any alternative options.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.