Search code examples
c#asp.net-mvcasp.net-mvc-4asp.net-web-api-routing

Making a generic query / getbyspecification in web api controller?


I have a typical API with some CRUD operations. I typically need to get certain objects, based on different parameters.

One way to do it would be to have methods like:

GetProjectsByCustomerId(int customerId);
GetProjectsBySigneeId(int signeeId);

However, in my service layer (ProjectService in this case) I usually use a method such as the following where ProjectSpecification typically has quite a lot of fields and even lists:

public IEnumerable<Project> GetBySpecification(ProjectSpecification projectSpecification)

That means, in my dream world I would like to have endpoints such as:

  • /api/projects (empty specification, return full list)
  • /api/projects?customerid=2 (gets projects for customer with id 2)
  • /api/projects?signeeid=2,3 (get projects with signee id 2 and 3)

My question is - how is this done

My first attempt was adding this in my ProjectController (calling my ProjectService):

public class ProjectsController : ApiController
{
    public IEnumerable<Project> GetProjects(ProjectSpecification projectSpecification)
    {
        var projects = _projectService.GetBySpecification(projectSpecification);
        return projects;
    }
}

But lets say I open this URL:

/api/Projects?CustomerId=2

This is not parsed into a ProjectSpecification viewmodel. However, if I change my controller signature to:

public IEnumerable<Project> GetProjects(int customerid) { }

It would work, because it's a simple type.

I could of course build some parameter-hell, but I guess there is something super obvious MVC magic I am missing - probably in the routing? :-)


Solution

  • Referencing documentation

    Parameter Binding in ASP.NET Web API : [FromUri]

    To force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter.

    For example assuming

    public class ProjectSpecification {
        public int CustomerId { get; set; }
        //...other properties
    }
    
    public class ProjectsController : ApiController {
        [HttpGet]
        public IHttpActinoResult GetProjects([FromUri]ProjectSpecification projectSpecification) {
            return Ok(projectSpecification);
        }
    }
    

    The client can put the CustomerId value in the query string.

    For example:

    /api/Projects?CustomerId=2
    

    and Web API will use them to construct a ProjectSpecification with the CustomerId set to 2 .