Search code examples
c#resturl-routingservicestackdto

How do I map multiple Request DTOs to a single Route


I have been experimenting with the ServiceStack framework to develop a simple REST API. I am having troubles trying to define the Routes properly to handle the different URLs I desire.

I have a simple DomainModel class:

public class Student
{
    public int Id { get; set; } // unique
    public string Email { get; set; } // not unique

    // some other properties and business logic
}

I would like to create a REST service which responds to HTTP requests to the following URLs:

  • /students
    • GET returns all students
  • /students/123
    • GET returns student with Id = 123
  • /students?id=123
    • GET returns student with Id = 123
  • /[email protected]
    • GET returns all students with matching Email

I have tried using the following Request DTOs to accomplish this:

[Route("/students", "GET")]
public class GetAllStudents : IReturn<StudentList>
{
}

[Route("/students", "GET")] // I want this to be for /students?Id=
[Route("/students/{Id}", "GET")]
public class GetStudentById : IReturn<Student>
{
    public int Id { get; set; }
}

[Route("/students", "GET")] // I want this to be for /students?Email=
public class GetStudentsByEmail : IReturn<StudentList>
{
    public string Email { get; set; }
}

As you can see, I am only trying to use two different Response DTOs:

public class StudentList : List<Student>
{
}

public class Student
{
    public int Id { get; set; }
    public String Email { get; set; }
}

Note: This Reponse DTO Student class is separate than my DomainModel class; they are in different namespaces.

Unfortunately, when I try to use these routes, every request to /students?<anything> ends up returning all students. The only Route that does work is if I make a GET request to /students/123, then I get back Student with Id 123. I assume this isn't working because I am re-using the "/students" Route definition for multiple Request DTOs.

I have been reading through the ServiceStack wiki and examples but I can not figure out how to accomplish what I am attempting. The most detailed documentation I can find about advanced routing is the "Smart Routing" section of the "New API" page but it doesn't really address this situation either.

How can I re-use the same Route definition for mulitple Request DTOs?


Solution

  • Having ByXXX suffixes on your Request DTOs is a code-smell in ServiceStack who wants to encourage the design of message-based coarse-grained interfaces. Please see this earlier answer on How to design a Message-Based API with ServiceStack and why you should group services by response types and call semantics

    In this case you have a service doing a Get on a Unique Id returning a single Student and you also wish to have a Search on Email which returns multiple Students. So in this case I would have the following services:

    [Route("/students/{Id}")]
    public GetStudent : IReturn<Student>
    {
        public int Id { get; set; }
    }
    

    Which would handle just /students/123.

    [Route("/students")]
    public FindStudents : IReturn<List<Student>>
    {
        public int? Id { get; set; }
        public string Email { get; set; }
    }
    

    Which can be used to handle:

    Although I personally wouldn't include an Id here since it's a Get instead of a Filter.