-
-
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?