Is it possible to route requests to different applications via Content-Type?
Solution 1
Nginx's idiomatic approach to this kind of problems is via map
. Please see my answer at StackOverflow.
Basically, you define a map
in http
section
map $any_variable $my_upstream {
# Default value:
default upstream1;
# Exact match:
application/vnd.vendorname+json;v=1 upstream2;
# Regexp:
~^application.*vnd.vendorname-v1\+json upstream3;
}
You may mix exact matches and regexps in one map.
Then you simply use $my_upstream
in you server
or location
section(s):
location / {
proxy_pass http://$my_upstream$uri;
}
Nginx evaluates map variables lazily, only once (per request) and when you are using them.
Solution 2
Sure; Apache's mod_rewrite
could do this with a little bit of RewriteCond
, though I'm a bit too rusty to give you an example off the top of my head. In nginx, though, it'd look something like the following (assuming you had two upstreams defined; one for your jsonapp and the other for... otherstuff):
if ($content_type = application/vnd.vendorname-v1+json) {
proxy_pass http://jsonapp/
break;
}
proxy_pass http://otherstuff/
Related videos on Youtube
Adam Lassek
Updated on September 18, 2022Comments
-
Adam Lassek almost 2 years
I'm designing a JSON API, and I'd like to version the API using content negotiation of some kind. I'm currently planning on using Vendor MIME Types to do this.
While I can definitely do this at the application level, I'm thinking it would be best to make this happen at the HTTP server level. Is this possible with Apache or nginx?
The Content-Type would look something like:
application/vnd.vendorname-v1+json
or possibly using parameters:application/vnd.vendorname+json;v=1
-
Alexander Azarov almost 13 years
Content-type
is a response's header. In order to select a backend based on request, you must rely on some header from request. Which one? -
Austen Holmes almost 13 yearsThe Accept: header, which the client uses to specify which Content-Types it would like to receive when performing content negotiation.
-
-
Adam Lassek almost 13 yearsWould this proxy be transparent to the outsider? I want both applications to be accessible by the same url.
-
womble almost 13 yearsYes, of course, that's the whole point of a proxy.
-
kolbyjack almost 13 yearsBe careful when using variables with proxy_pass. From the wiki entry: "A special case is using variables in the proxy_pass statement: The requested URL is not used and you are fully responsible to construct the target URL yourself."
-
Adam Lassek almost 13 yearsbut what, exactly, would
jsonapp
andotherstuff
be in your example? Should I create entries in/etc/hosts
and map those to the two different apps first? (I'm new to nginx, sorry) -
Adam Lassek almost 13 yearsI disagree. Content negotiation should be the job of the HTTP server. This will actually greatly simplify API development. While misconfiguration could indeed cause a problem, that possibility is not limited to this use case.
-
Adam Lassek almost 13 yearsMore information about the API: we will be developing a JSON API for AJAX clients using Sinatra+Rack. If API versioning is done through content negotiation, then separate versions can be deployed in isolation of one another. This is a simple, elegant design following REST principles. What you describe would actually create more dependencies between versions.
-
hookenz almost 13 yearsI didn't say you couldn't negotiate the content. PHP does that. I'm talking about the version number!
-
Adam Lassek almost 13 yearsThe version number would define what content you get, such as breaking changes to the api. That is why I think content negotiation is appropriate.
-
Adam Lassek almost 13 years(to clarify further, when I say 'content' I mean the specific format of the JSON being returned. For instance, the resource location
/people/:id
may not change between v1 and v2, but the format of the JSON you get back may change dramatically. The 'identity' of the resource hasn't changed, but it's representation has.) -
womble almost 13 yearsThey should be whatever you've defined your "upstreams" to be; see wiki.nginx.org/NginxHttpProxyModule#proxy_pass and wiki.nginx.org/HttpUpstreamModule