Book Image

ServiceStack 4 Cookbook

Book Image

ServiceStack 4 Cookbook

Overview of this book

Table of Contents (18 chapters)
ServiceStack 4 Cookbook
Credits
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Routing using data transfer object attributes


ServiceStack routes incoming HTTP requests to services by a number of mechanisms. Perhaps the most convenient is the Route attribute— simply annotate the class to indicate where ServiceStack should expect requests.

Getting ready

You'll need a request object. For instance, Reidson-Industries is interested in developing a group messaging application for use on the Web and on mobile devices. We could model an incoming request like this:

public class Message
{
  public string Body { get; set; }
  public string Sender { get; set; }
  public string GroupName { get; set; }
}

We might handle requests for this Message object with a MessengerService class, as follows:

public class MessengerService : Service
{
  static List<Message> _messages = new List<Message>();
  public MessageResponse Post(Message request)
  {
    _messages.Add(request);
    return new MessageResponse { Response = "OK" };
  }
}

In previous examples, our service methods have simply returned the object, with the service passing a string back. To add a bit more structure to our contract, a response might be modeled like this:

public class MessageResponse
{
  public string Response { get; set; }
}

How to do It...

To tell ServiceStack where to expect requests that contain Message objects, simply annotate the object with a Route annotation, as follows:

[Route("/message")]
public class Message
{
  public string Body { get; set; }
  public string Sender { get; set; }
  public string GroupName { get; set; }
}

How it works...

Now ServiceStack will accept POST requests containing either JSON data or form data at http://yourserver/message containing strings named Body, Sender, and GroupName. These requests will be deserialized in to an instance of the Message DTO. You can provide more information in the annotation as well— for instance, if you wanted to be able to provide an alternate URL format, you could do the following:

[Route("/message")]
[Route("/message/{GroupName}")]
public class Message
{
  public string Body { get; set; }
  public string Sender { get; set; }
  public string GroupName { get; set; }
}

Now, if ServiceStack sees a request for http://yourserver/message/BestFriends, it will assume that Message is destined for the BestFriends group. You can add multiple routes for the same request object by placing multiple Route annotations on the same object.

We'll need to retrieve messages too. We can do that by querying the /group endpoint. To do this, simply create a request object, as follows:

[Route("/group/{GroupName}")]
public class Group
{
  public string GroupName { get; set; }
}

We'll expand MessengerService to be able to handle this request:

public class MessengerService : Service
{
  static List<Message> _messages = new List<Message>();
  public MessageResponse Post(Message request)
  {
    _messages.Add(request);
    return new MessageResponse { Response = "OK" };
  }

  public GroupResponse Get(Group request)
  {
    return new GroupResponse
    {
      Messages = _messages.Where(message => message.GroupName.Equals(request.GroupName))
      .ToList()
    };
  }
}

The GroupResponse class is a simple DTO to model the expected response. In this example, the GroupResponse class has a single property Messages and simple List<Message> containing the messages that the user has requested:

public class GroupResponse
{
  public List<Message> Messages { get; set; }
}

Note

Note that the code in the previous example will work to get us started building a simple application, but storing all of our incoming messages in static List<Message> with no backing store isn't likely to work out well in practice. We'll refactor this into something more production-ready in a later recipe.

Once you start the app, you can send a form post to the BestFriends group with curl. A command-line HTTP utility, curl makes it easy to craft full-featured HTTP requests. It can specify headers and HTTP methods, send form data, and return any results on the command line.

We want to send an HTTP POST request with enough form data to provide a message to our service, and we'd like to see the response in JSON. We'll use curl with -H to specify the header, -X, to specify the HTTP method, and --data to specify the data to send. Put together, it looks like this:

curl -H "Accept: application/json" -X POST --data  \
    "Body=first post&Sender=Kyle&GroupName=BestFriends" \
    http://myserver/message

Then, you can read the messages that have been sent to the BestFriends group, as follows:

curl -H "Accept: application/json" \     
     http://myserver/group/BestFriends

The result would be the JSON response, as follows:

{"Messages": [{"Body":"first post", "Sender":"Kyle","GroupName":"BestFriends"}]}

There's more...

Let's imagine that we wanted people to be able to search for a specific search term or possibly search for a term within a specified group. We could use the Route annotation to do this easily with one single request type. Implement it like this:

[Route("/message/search")]
[Route("/message/search/{Group}")]
public class Search
{
  public string Group { get; set; }
  public string Query { get; set; }
}

We'll easily expand our MessengerService class to be able to handle this request by adding a new method, as follows:

public GroupResponse Get(Search request)
{
    return new GroupResponse
    {
      Messages = _messages.Where(
        message => message.GroupName.Equals(request.Group)
        && message.Body.Contains(request.Query))
      .ToList()
    };
}

We could make use of this new endpoint with another curl command. First, we'll post a few messages so that we have something to search, as follows:

curl -H "Accept: application/json" -X POST \
     --data "Body=first post&Sender=Kyle"  \
     http://myserver/message/BestFriends
{"Response":"OK"}
curl -H "Accept: application/json" -X POST \
     --data "Body=second post&Sender=Kyle"  \
     http://myserver/message/BestFriends
{"Response":"OK"}

Then, we can easily search by sending a simple GET call:

curl -H "Accept: application/json" \    
    http://myserver/message/search/BestFriends?query=second
{"Messages":
  [
    {"Body":"second post",
     "Sender":"Kyle",
     "GroupName":"BestFriends"}
  ]
}