Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can ServiceStack services contain multiple methods?

Environment is Visual Studio 2012, ServiceStack, ASP.NET Web Application Project (followed https://github.com/ServiceStack/ServiceStack/wiki/Create-your-first-webservice)

Looking through some of the classes in ServiceStack.Examples, I noticed that most of the services contain only one method. Either some override on Execute() or, if a REST service, some override of OnPost/Get/Put/Delete().

How should I approach making a full API set, if I have tens of functions I need implemented RegisterUser(), RemoveUser(), AddFriend(), RemoveFriend() ... One service per method?

public RegisterUserService : IService<User> { public object Execute(User> dto) { ... } }
public RemoveUserService : IService<User> { public object Execute(User> dto) { ... } }
public AddFriendService : IService<Friend> { public object Execute(Friend dto) { ... } }
public RemoveFriendService: IService<RequestDTO4> { public object Execute(Friend dto) { ... } }

I'm pretty lost on how to begin implementing a full API set. I've read the first and second wiki page on 'Creating your first webservice', which I've copied to make 1 service method. But now I want to make 10 or 40 service methods and I'm not sure how to do that.

I noticed that implementing from IRestService<T> allows you up to 4 methods instead of the one Execute() method, simply because each method corresponds to a different HTTP verb. So is there something like that I could write? Basically something like:

public MyService : IService/IRestService/ServiceBase?<User>
{
     public object AddUser(User user) { }
     public object RemoveUser(User user) { }
     public object ModifyUser(User user) { }
}

Just looking for something that doesn't necessarily have to contain all methods in one service class, but as many as reasonably possible ... do I really have to create 1 service for each service method?

Note on pursuing a strictly RESTful architecture: I only read up a little on REST, but it seems like I'd have to strictly follow rules like: treat everything as a resource even if you have to re-design your models, no verbs in the URL names (/Friends, not /GetFriends because REST gives you OnGet(), OnPost(), OnPut(), and OnDelete() ... basically I'm interested in the easiest, quickest, and most painless way of implementing a a few dozen service methods. It's a personal project, so the requirements won't vary too much.

Thanks in advance for guiding me through this first step.

EDIT: Just saw this related question: How to send commands using ServiceStack?

Mythz said there's no "ServiceStack way" to design. The guy's question is pretty much like mine. I'm wondering how to stack a lot of service methods in a service.

EDIT 2: Just saw Need help on servicestack implementation, and Separate or combined ServiceStack services?.

I just tested the code below successfully with working routes:

[Route("/registerUser/setEmail/{Email}")]
[Route("/registerUser/setPassword/{Password}")]
[Route("/registerUser/setPhoneNumber/{PhoneNumber}")]
[Route("/lalal2395823")]
[Route("/test3234/test23423511")]
public class RegisterUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Nickname { get; set; }
    public string PhoneNumber { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

But what I'd like is for each [Route("path")] to go to a different method, instead of having them all parsed in Execute() and having to parse which string isn't null or empty.


My Solution

I decided to take Rickard's advice and make a proper REST API, because it seems simpler and cleaner in the end.

This is now my class using the new ServiceStack API (new as of 9/24/12):

using UserModel = Project.Model.Entities.User;

[Route("/User", "POST")]
[Route("/User/{FirstName}", "POST")]
[Route("/User/{FirstName}/{LastName}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}/{PhoneNumber}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}/{PhoneNumber}/{Email}", "POST")]
public class CreateUser : IReturn<UserModel>
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Nickname { get; set; }
    public string PhoneNumber { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

public class CreateUserService : Service
{
   public object Post(CreateUser request)
    {
        try
        {
            using (var session = FluentNHibernateHelper.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    var user = new UserModel()
                    {
                        FirstName = request.FirstName,
                        LastName = request.LastName,
                        Nickname = request.Nickname,
                        PhoneNumber = request.PhoneNumber,
                        Email = request.Email,
                        Password = request.Password,
                    };
                    session.SaveOrUpdate(user);
                    transaction.Commit();

                    return user;
                }
            }
        }
        catch
        {
            throw;
        }
    }
}
like image 896
Jason Avatar asked Sep 23 '12 05:09

Jason


2 Answers

This is now a lot simpler with ServiceStack's new API Design released in (v3.9.15+).

@Rickard makes a lot of good points on how to re-structure your service so it's more REST-ful which is now easier to achieve with ServiceStack's new API which is now less restrictive and more flexible where the same service can handle multiple different Request DTOs and you're no longer restricted in the Response Type you can return.

like image 171
mythz Avatar answered Nov 19 '22 14:11

mythz


Following the HTTP way you have to turn your way of thinking upside down. You need to think in terms of resources, i.e. users, friends, etc. Using HTTP you already have a finite set of methods, namely Get, Put, Post, and Delete.

Hence, the service API design could look like this:

RegisterUser() => POST /users
RemoveUser() => DELETE /users/{userid}
AddFriend() => POST /users/{userid}/friends
RemoveFriend() => DELETE /users/{userid}/friends/{friendid}
ModifyUser() => PUT /users/{userid}

etc.

To implement for example RemoveFriend in ServiceStack you could do like this:

public class UserFriendService : RestServiceBase<UserFriendRequest>
{
    public override object OnPost(UserFriendRequest request)
    {
        // pseudo code 
        var user = GetUser(request.UserId);
        var friend = GetUser(request.FriendId); // FriendId is a field in the HTTP body
        user.Friends.Add(friend);
        return HttpResult.Status201Created(user, ...);
    }
    //...
}

[Route("/users/{userId}/friends")]
public class UserFriendRequest
{
    public string UserId { get; set; }
    public string FriendId { get; set; }
}
like image 23
Rickard Avatar answered Nov 19 '22 15:11

Rickard