Skip to content

Instantly share code, notes, and snippets.

@kellabyte
Created April 10, 2012 23:29
Show Gist options
  • Save kellabyte/2355599 to your computer and use it in GitHub Desktop.
Save kellabyte/2355599 to your computer and use it in GitHub Desktop.
Web API ideas
/*
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:as="http://atomserver.org/namespaces/1.0/"
xmlns:os="http://a9.com/-/spec/opensearchrss/1.1/">
<os:totalResults>65801</os:totalResults>
<os:startIndex>0</os:startIndex>
<os:itemsPerPage>2</os:itemsPerPage>
<as:endIndex>153</as:endIndex>
<link href="/myserver/v1/widgets/acme?start-index=153&amp;max-results=2" rel="next" />
<link href="/myserver/v1/widgets/acme?start-index=0&amp;max-results=2" rel="self" />
<author>
<name>AtomServer APP Service</name>
</author>
<title type="text">acme entries</title>
<updated>2007-10-05T19:17:42.750Z</updated>
<id>tag:atomserver.org,2007:v1:acme</id>
<entry>
<id>/myserver/v1/widgets/acme/205390.en.xml</id>
<as:entryId>205390</as:entryId>
<title type="text"> Entry: acme 205390.en</title>
<author>
<name>AtomServer Atom Service</name>
</author>
<link href="/myserver/v1/widgets/acme/205390.en.xml" rel="self" />
<link href="/myserver/v1/widgets/acme/205390.en.xml/2" rel="edit" />
</entry>
</feed>
*/
// Option 1 - AtomFormatter understands how to deal with it's own model. Feed, Entry, etc.
public class StoryController : ApiController
{
public HttpResponseMessage Get(int id)
{
Story story = GetMeAStory(id);
var response = new HttpResponseMessage(HttpStatusCode.OK);
Entry entry = ConvertToEntry(story); // Why do we need this?
response.Content = new ObjectContent<SyndicationItem>(entry, atomFormatter);
return response;
}
}
// I'm not convinced we need media type specific models and translate twice. Atom example:
// Story (our model) -> Entry (atom model) -> Atom Representation
//
// I think we can do better.
// Option 2
var formatter = new AtomFormatter();
formatter.MapModel<Story>()
.ToTitle(x => x.Topic)
.ToAuthor(x => x.Owner);
// Option 2b - Since Atom is specifically XML, why not use it.
var formatter = new AtomFormatter();
formatter.MapModel<Story>()
.To("/feed/title", x => x.Topic)
.To("/feed/author/name", x => x.Owner);
// AtomFormatter.MapModel<T>() would return an AtomModelMap, so we still have SRP as the mapping and
// formatting are still independent.
public class StoryController : ApiController
{
public HttpResponseMessage Get(int id)
{
Story story = GetMeAStory(id);
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = this.Request.Represent(story); // The proper formatter is used based on conneg
return response;
}
}
// Option 2 and 2b don't require copying data for the purpose of mapping a translation like option 1 does.
// We've also eliminated a double translation that occurred in option 1.
@DarkSmith
Copy link

Why not simply use the view-model pattern with various view rendering strategies ?

@kellabyte
Copy link
Author

@DarkSmith feel free to fork the gist and create an alternative :)

I didn't like having an intermediate model just for the purpose of translation because that forces us into translating twice. I get to use multiple formatters for a given model and map to them without having to use a model in the middle. I don't believe copying data is required to do this representation.

Please fork the gist and show the alternative. Seeing the code will be easier to understand what you mean :)

@pedroreys
Copy link

Given that the available transitions/links are dependent of the resource state, I'm assuming the Story object would be responsible to know it's available transitions/links.

The formatter then would have to know how to retrieve the links from the story object in the response message. Is that correct?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment