-
-
Save ianbattersby/3056291 to your computer and use it in GitHub Desktop.
namespace ServiceStackReSTExample | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Net; | |
using System.Runtime.Serialization; | |
using System.Threading; | |
using ServiceStack.Common.Web; | |
using ServiceStack.Logging; | |
using ServiceStack.Logging.Support.Logging; | |
using ServiceStack.ServiceHost; | |
using ServiceStack.ServiceInterface; | |
using ServiceStack.ServiceInterface.ServiceModel; | |
using ServiceStack.WebHost.Endpoints; | |
// Warning: Contrived example ahead | |
public class Movie | |
{ | |
public string Title { get; set; } | |
public List<string> Actors { get; set; } | |
} | |
public class MovieResponse | |
{ | |
public string Id { get; set; } | |
public ResponseStatus ResponseStatus { get; set; } | |
public List<Movie> Movies { get; set; } | |
} | |
[Description("Movie web service")] | |
[RestService("/movies", "GET,OPTIONS")] | |
[DataContract] | |
public class MovieLookup | |
{ | |
} | |
public class MovieLookupService : RestServiceBase<MovieLookup> | |
{ | |
public override object OnGet(MovieLookup request) | |
{ | |
// So what I really want to do here is return a 300 Multiple Chocies with links to the other two model-based services. | |
// It seems sensible to use HttpResult opposed to hijacking IHasOptions directly, however I get caught in a loop. Possible | |
// HEAD issue? Most likely. | |
return new HttpResult(new object(), base.RequestContext.ContentType, HttpStatusCode.MultipleChoices); | |
// Obviously I would be returned an empty object here in practice, but some links to the services. | |
// As we don't have a preferred link we won't be using the LOCATION header (as per RFC2616). | |
} | |
} | |
[Description("Movie by title web service")] | |
[RestService("/movies/title/{title}", "GET,OPTIONS")] | |
[DataContract] | |
public class MovieLookupTitle | |
{ | |
[DataMember] | |
public string Title { get; set; } | |
} | |
public class MovieLookupTitleService : RestServiceBase<MovieLookupTitle> | |
{ | |
public override object OnGet(MovieLookupTitle request) | |
{ | |
return new MovieResponse() | |
{ | |
// Lookup in DB for this title.. | |
Id = Guid.NewGuid().ToString(), | |
Movies = new List<Movie> { new Movie() { Title = "Nelly packs her trunk", Actors = new List<string> { "Demis Bellot", "Ian Battersby" } } }, | |
}; | |
} | |
} | |
[Description("Movie by actor web service")] | |
[RestService("/movies/actor/{actor}", "GET,OPTIONS")] | |
[DataContract] | |
public class MovieLookupActor | |
{ | |
[DataMember] | |
public string Actor { get; set; } | |
} | |
public class MovieLookupActorService : RestServiceBase<MovieLookupActor> | |
{ | |
public override object OnGet(MovieLookupActor request) | |
{ | |
return new MovieResponse() | |
{ | |
// Lookup in DB for this actor | |
Id = Guid.NewGuid().ToString(), | |
Movies = new List<Movie> { | |
new Movie() { Title = "Nelly packs her trunk", Actors = new List<string> { "Demis Bellot", "Ian Battersby" }}, | |
new Movie() { Title = "Planes, Trains, and Strawberries", Actors = new List<string> { "Ian Battersby", "Rob Ashton" }} | |
}, | |
}; | |
} | |
} | |
public class AppListenerHost | |
: AppHostHttpListenerBase | |
{ | |
private static ILog log; | |
public AppListenerHost(string appName) | |
: base(appName, typeof(Movie).Assembly) | |
{ | |
LogManager.LogFactory = new DebugLogFactory(); | |
log = LogManager.GetLogger(typeof(AppListenerHost)); | |
} | |
public override void Configure(Funq.Container container) | |
{ | |
SetConfig(new EndpointHostConfig | |
{ | |
GlobalResponseHeaders = | |
{ | |
{ "Access-Control-Allow-Origin", "*" }, | |
{ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" }, | |
}, | |
WsdlServiceNamespace = "http://www.servicestack.net/types", | |
}); | |
} | |
} | |
class Program | |
{ | |
public const string AppName = @"Ian's Movie App"; | |
public const string ListeningOn = @"http://localhost:83/"; | |
static void Main(string[] args) | |
{ | |
using (var appHost = new AppListenerHost(AppName)) | |
{ | |
appHost.Init(); | |
appHost.Start(ListeningOn); | |
Console.WriteLine("Started listening on: " + ListeningOn); | |
Console.WriteLine("AppHost Created at {0}, listening on {1}", DateTime.Now, ListeningOn); | |
Thread.Sleep(System.Threading.Timeout.Infinite); | |
} | |
System.Console.WriteLine("ReadLine()"); | |
System.Console.ReadLine(); | |
} | |
} | |
} |
It's hard to find examples but my understanding is that you would include LINK rels as below. I believe the convention is to use the "rel" to provide a URI where documentation can be found, this would effectively be the same as the metdata pages.
<link rel="/rels/movie/title" href="/movie/title" />
<link rel="/rels/movie/actor" href="/movie/actor" />
See RFC5988: http://tools.ietf.org/html/rfc5988
Incidentally I'm starting to think I have the 300 wrong, it might be returned as 200 :=/
OK, think I did get the 300 thing wrong - but would be good to know how to do.
http://martinfowler.com/articles/richardsonMaturityModel.html#level3
Realise the above example is XML based, HEADER-version most likely something like;
Link: </movie/title>; rel="http://localhost:83/rels/movie/title"
Link: </movie/actor>; rel="http://localhost:83/rels/movie/actor"
In which case you can just return a custom contentType, headers and body, e.g:
new HttpResult {
ContentType = "application/xml",
Headers = {
{"Link","</movie/title>; rel=\"http://localhost:83/rels/movie/title\""},
{"Link","</movie/actor>; rel=\"http://localhost:83/rels/movie/actor\""},
},
ResponseText = "<link rel=...", //If you also want to return it in the body.
};
Thanks Demis, been tied up but will follow this up tomorrow.
What is an example of the HTTP wire response you wish to achieve with the HEAD request?