6

Assume i've read a lot about versioning a restful api, and I decided to not version the the service through the uri, but using mediatypes (format and schema in the request accept header):

What would be the best way to implement a wcf service or a web api service to serve requests defining the requested resource in the uri, the format (eg. application/json) and the schema/version (eg player-v2) in the accept header?

WCF allows me to route based on the uri, but not based on headers. So I cannot route properly.

Web Api allows me to define custom mediatypeformatters, routing for the requested format, but not the schema (eg. return type PlayerV1 or PlayerV2).

I would like to implement a service(either with WCF or Web Api) which, for this request (Pseudo code):

api.myservice.com/players/123 Accept format=application/json; schema=player-v1

returns a PlayerV1 entity, in json format

and for this request:

api.myservice.com/players/123 Accept format=application/json; schema=player-v2

returns a PlayerV2 entity, in json format.

Any tips on how to implement this?

EDIT: To clarify why I want to use content negotiation to deal with versions, see here: REST API Design: Put the “Type” in “Content-Type”.

j0k
  • 22,600
  • 28
  • 79
  • 90
codeclash
  • 2,053
  • 19
  • 17

1 Answers1

2

What you are bringing here does not look to me as versioning but it is is more of content negotiation. Accept header expresses wishes of the client on the format of the resource. Server should grant the wishes or return 406. So if we need more of a concept of Contract (although Web API unline RPC does not define one) then using resource is more solid.

The best practices for versioning have yet to be discussed fully but most REST enthusiast believe using the version in the URL is the way to go (e.g. http://server/api/1.0.3/...). This also makes more sense to me since in your approach using content negotiation server has to keep backward compatibility and I can only imagine the code at the server will get more and more complex. With using URL approach, you can make a clean break: old clients can happily use previous while new clients can enjoy the benefits of new API.


UPDATE

OK, now the question has changed to "Implementing content-negotiation in a RESTful AP".

Type 1: Controller-oblivious

Basically, if content negotiation involves only the format of the resource, implementing or using the right media type formatter is enough. For example, if content negotiation involves returning JSON or XML. In these cases, controller is oblivious to content negotiations.

Type 2: Controller-aware

Controller needs to be aware of the request negotiation. In this case, parameters from the request needs to be extracted from the request and passed in as parameter. For example, let's imagine this action on a controller:

public Player Get(string schemaVersion)
{
    ...
}

In this case, I would use classic MVC style value providers (See Brad Wilson's post on ValueProviders - this is on MVC but Web API's value provider looks similar):

public Player Get([ValueProvider(typeof(RequestHeadersSchemaValueProviderFactory))]string schemaVersion)
{
    ...
}
Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • 2
    Let's not start a discussion about which way is better (version in URI or not) since that's not the point here. I could rephrase the question: "Implementing content-negotiation in a RESTful API..", the implementation challenge would still be the same, wouldn't it? – codeclash May 15 '12 at 12:09
  • The value provider would give me access to the requested scheme, but in any case the return type in your example would be Player, whereas I would actually need to return either a Player or PlayerV2, ... depending on the requested scheme. Am I missing something? – codeclash May 15 '12 at 13:48
  • @cardinal in that case, define response as HttpResponseMessage and just set the content to right object content. – Aliostad May 15 '12 at 13:54
  • I've been thinking about your solution, bit honestly, it smells somehow like a workaround. Basically, it implies that I have to implement the "return this if requested schema is that" work myself, for each action (if we speak mvc). What I'm looking for is something like (I'm just brainstorming here) a MediaContentProvider in analogy to MediaTypeFormatter, where I can bind a class to a schema (analogy formatter: bind a serializer to a format). – codeclash May 15 '12 at 22:13
  • Wouldn't it be nice if I could configure: a) those are the formats I can handle, eg. xml, json. b) those are schemes I support. c) for a resource with that uri, use that action (returning the data for the requested schema) serialized by the formatter registered for the requested format. – codeclash May 15 '12 at 22:19
  • @cardinal `it smells like a workaround`: depends. You did not tell me need this v1, v2 in all controllers. How come you need it in all? This question now is a different one, post another question and explain what you want to achieve. – Aliostad May 15 '12 at 22:43
  • I'm not really happy with your answer (some bits helped though), but put the blame on me: It might not have been clear from the start what exactly I was looking for, and you're probably right: I should rephrase my question once I'm sure my use case is clear to everybody, and give concrete examples from the start. Thx for your suggestions, I'll accept your answer, you provided both help and advice. – codeclash May 19 '12 at 19:39
  • Could you elaborate more on versioning? Or at least point me to the right source since this is currently what I need. Thank you! – Alex Avrutin May 23 '12 at 00:42
  • 1
    @AlexAvrutin versioning usually involves including version number in the URL: `/api/1.2.3/cars/123` – Aliostad May 23 '12 at 07:55
  • I think versioning in the URL is done often, but it makes things a bit dirty on both the Uri and in the coding. If you make you controllers content aware they can return the correct response. I'm assuming you could use a WebAPI filter to respond based on the content-type requested. http://barelyenough.org/blog/2008/05/versioning-rest-web-services/ – PilotBob Jul 19 '12 at 18:19