Search code examples
c#.netrestasp.net-web-api2

Custom validation - Required only when method is put


I have two API endpoints, post and put:

    [HttpPost]
    [Route("projects")]
    public IHttpActionResult Create([FromBody] ProjectDTO projectDto)
    {
        if (ModelState.IsValid)
        {
                var project = MappingConfig.Map<ProjectDTO, Project>(projectDto);
                _projectService.Create(project);

                return Ok("Project successfully created.");
        }
        else 
        { 
            return BadRequest(ModelState); 
        }
    }

    [HttpPut]
    [Route("projects")]
    public IHttpActionResult Edit([FromBody] ProjectDTO projectDto)
    {
        if (ModelState.IsValid)
        {
            var project = _projectService.GetById(projectDto.ProjectId);
            if (project == null)
                return NotFound();

            project = Mapper.Map(projectDto, project);
            _projectService.Update(project);

            return Ok("Project successfully edited.");
        }
        else 
        { 
            return BadRequest(ModelState); 
        }
    }

DTO looks like this:

    public class ProjectDTO
    {
           public int ProjectId { get; set; }
           [Required(ErrorMessage = "Name field is required.")]
           public string Name { get; set; }
           [Required(ErrorMessage = "IsInternal field is required.")]
           public bool IsInternal { get; set; }
    }

I'm trying to validate field ProjectId. ProjectId field should be required only in HttpPut method when I'm editing my entity.

Is it possible to make custom validation RequiredIfPut or something like that where that field will be required only when editing, but not when creating?


Solution

  • Here is what you can do using custom validation attribute:

     public class RequiredWhenPutAttribute : ValidationAttribute
        {
            protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {
                if (System.Web.HttpContext.Current.Request.HttpMethod == "PUT")
                {
                    var obj = (ProjectDTO)validationContext.ObjectInstance;
                    if (obj.ProjectId == null)
                    {
                        return new ValidationResult("Project Id is Required");
                    }
                }
                else
                {
                    return ValidationResult.Success;
                }
            }
        }
    
        public class ProjectDTO
        {
            [RequiredWhenPut]
            public int? ProjectId { get; set; }
        }
    

    Update:

    In response to your comment, in order to make the solution more general, you can add a ParentDto class from which other classes are inherited and the shared property needs to be in the ParentDto class, as the following:

     public class RequiredWhenPutAttribute : ValidationAttribute
        {
            protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {
                if (HttpContext.Current.Request.HttpMethod == "PUT")
                {
                    var obj = (ParentDto)validationContext.ObjectInstance;
                    if (obj.Id == null)
                    {
                        return new ValidationResult(ErrorMessage);
                    }
                }
                else
                {
                    return ValidationResult.Success;
                }
            }
        }
        public class ParentDto
        {
            [RequiredWhenPut(ErrorMessage = "Id is required")]
            public int? Id { get; set; }
        }
        public class UserDTO : ParentDto
        {
            // properties
        }
        public class ProjectTypeDTO : ParentDto
        {
            // properties
        }
        public class ProjectDTO : ParentDto
        {
             // properties
        }