
Splitting HTTP request methods
When you're building a REST service, it's good practice to make efficient use of the HTTP protocol to assist you in your design. While full coverage of what makes for good RESTful design is outside the scope of this book, we will cover how to use ServiceStack's built-in tools to help you reuse code.
One project I am acquainted with started out with an RPC-style design. The service owner realized that they had well over 65 endpoints, such as /createNewGroup
, /renameGroup
, and so on, making it hard for developers to remember the exact name of the endpoint for the function they need. This was overhauled, and the RESTful approach was applied. The end result had only four endpoints and still served the same user base with the same functionality. The end result was a much simpler service that was easier to learn, deploy, and debug—but the process created a lot of churn for the teams that consumed that service to get to that end goal.
Basically, this redesign consisted of replacing endpoints that facilitated only small operations to a more RESTful design based on resources. The R in REST stands for Resource. The idea is to design an API that provides different resources and then allows consuming teams to perform different operations on that resource. This cleans up our API considerably—instead of /getMessagesFromGroup
, /createNewGroup
, and /renameGroup
, you would just have /groups
. Our service can infer from the type of request being sent to the resource what the client is requesting. This has many benefits—less code, greater code reuse, and importantly, an easy-to-use API for clients.
Getting ready
Let's take the ReidsonMessenger
service that we built up in the Routing using data transfer object attributes recipe in Chapter 1, Configuration and Routing. It currently expects to find HTTP GET
requests that contain a Group
object from the ServiceModel
project. Currently, this specific endpoint returns all the messages from the specified group. What if you also wanted to allow a New Group
functionality? We could create a /newGroup
endpoint that accepts HTTP POST
messages with a new ServiceModel
type, but then we're starting to have a proliferation of endpoints and message types. It's better to design our services to avoid this proliferation.
How to do it…
We'll start out with the ReidsonMessenger
service where we left off, but we'll make some changes to facilitate the new group creation functionality that we need. Since we're refactoring some of our existing code, let's start with a test, as follows:
[Test] public void ShouldCreateNewGroupsOnRequest() { var service = new MessengerService(); var response = service.Post( new Group { GroupName = "NewGroup", Creator = "Me" }); Assert.That(response.Name.Equals("NewGroup")); }
We're specifying that we'd like the service to expect a Group
message arriving as an HTTP POST
. It will contain a new Creator
property that's a string containing the user who wants to create the group. At the moment, if we try to run this test, it won't even compile—we need to create the Creator
property, and we need to handle the Post
method on MessengerService
. Let's do those two things next.
Changing the Group
model is a simple refactor; we should end up with code that looks like the following:
[Route("/groups/{GroupName}")] public class Group { public string GroupName { get; set; } public string Creator { get; set; } }
Next up, we'll add a new Post
method to MessengerService
that accepts Group
objects. We can implement this fairly simply for now by just placing a message in our _messages
list with GroupName
set to that of the request and Sender
set to the string specified in the Creator
property.
Let's take a look at both of the methods that deal with Group
requests in MessengerService
after providing this implementation:
public object Get(Group request) { return new GroupResponse { Messages = _messages.Where(message => message.GroupName.Equals(request.GroupName)) .ToList() }; } public object Post(Group request) { _messages.Add(new Message { Sender = request.Creator, GroupName = request.GroupName, Body = request.Creator + " created " + request.GroupName + " group." }); return new GroupResponse { Name = request.GroupName, Messages = _messages.Where(message => message.GroupName.Equals(request.GroupName)) .ToList() }; } }
Our tests should pass now, and our new functionality is ready. However, if we check the metadata that ServiceStack generates for this new functionality, we'll see the following for the Group
message type:

What's happened is that the service will now accept the GET
or POST
messages at /groups/{GroupName}
—an unintended side effect. We wanted GET
/groups/bestfriends
to get all the messages on the bestfriends
group, but we didn't want to enable posting to /groups/newGroupName
necessarily. This is easy to fix—we simply need to fix the [Route]
annotation on the Group
object so that it only works on GET
methods and add another route to handle both, like this:
[Route("/groups/")] [Route("/groups/{GroupName}","GET")] public class Group { public string GroupName { get; set; } public string Creator { get; set; } }
Now, if we check our service metadata, we'll see what we were expecting—the /group
endpoint accepts all HTTP verbs, but /groups/{GroupName}
only accepts HTTP GET
, which is depicted in the following screenshot:
