Does the locale belong on the path or as a request parameter on the URI? - rest

I have seen the locale added to an URI as a path variable:
/en-US/blah/
or
/blah/en-US
and I have seen it as a request parameter:
/blah?lang=en-US
Is there a standard way that I should be doing it? If not what are the pros and cons of each approach?
I kind of like the request parameter approach because it doesn't require you to pass the locale with every URI (unless you use the second approach and add the locale to the end of the path). Any additional thoughts?

I believe the "standard way" is to use an HTTP "accept language" header. Other than that, if you think it ought to be a parameter (because it's a system-system call or you might request a language other than the browser locale) then it's just a parameter the same as anything else and you should handle it in a similar fashion.

Fun fact: even with this notation "/blah/en-US" it could still be a request parameter. Magic of mod_rewrite and friends.
If you need it as request parameter or part of the url depends of what you want to achieve. If you want to serve static content, you should have it be part of the path. If you want to act dynamically on the chosen locale, you should use it as request parameter, since you don't want to have your scripts replicated several times over different paths just to add different locales.

Related

What is the best practice to design the rest api url if one resource identifier is a path

It is straightforward to put resource id into url if it is a int or long type. e.g.
GET files/123
But my problem is that my resource identifier is a path. e.g. /folder_1/folder_2/a.sh because the underlying implementation is a filesystem. So I can not put it as part of rest api url because it is conflict with url path.
Here's approaches what I can think of:
Put the path id as the request param. e.g.
GET files?path=/folder_1/folder_2/a.sh
Encode/decode the path to make it qualifier as part of url.
Introduce another int/long id for this resource in backend. And map it to the path. The int/long type resource id is stored in database. And I need to maintain the mapping for each CURD operation.
I am not sure whether approach 1 is restful, approach 2 needs extra encoding/decoding, and approach 3 needs extra work to maintain the mapping.
I wonder what is the best practice to design the rest api url for this kind of case.
Simple:
#GET
#Path("/files/{path:.+}")
#Produces({MediaType.TEXT_PLAIN})
public String files(
#PathParam("path") String path
) {
return path;
}
When you query files/test1/tes2 via url output is:
test1/tes2
Just put the path after a prefix, for example:
GET /files/folder_1/folder_2/a.sh
There isn't a conflict, since when the request path starts with your known prefix (/files/, in the above example), you know that the rest should be parsed as the path to the file, including any slashes.
Well, my experience designing "restful" APIs shows that you have to take into consideration future extensions of your API.
So, the guidelines work best when followed closely when it makes sense.
In your specific example, the path of the file is more of an attribute of the file, that can also serve as its unique ID.
From your API client's perspective, /files/123 would make perfect sense, but /files/dir1/file2.txt is debatable.
A query parameter here would probably help more, much like what you would do if you wanted to retrieve a filtered list of files, rather than the whole collection.
On the other hand, using a query parameter would also help for future extensions, since supporting /files/{path} would also mean conflicts when attempting to add sub-resources to your files endpoint.
For example, let's assume that you might need in the future another endpoint /files/attributes. But, having such an endpoint, would exclude any possibility for your clients to match a file named attributes.

Naming a GET with alternative query options

Assume you have a REST service that already gets users by id, so the url looks something like
GET /users/{userId}
But you want to create a duplicate web service that gets users by email, so:
GET /users/{email}
Which is better?
Method 1:
Same method:
/users/{input}
...
if(isEmail(input)) queryByEmail(input);
else queryById(input);
Method 2:
Different Method:
GET /users/{userId}
GET /usersByEmail/{email}
Since there is no actual overlap between email addresses and IDs. I would just use same endpoint for both. Especially if GET /users/{id} is already a published interface.
So, I would go with 1st method.
GET /users/{identifier}
Then on the API server you have to add a small check, whether {identifier} is a number or not.
I would also like to note, that "pretty URLs" do not make it REST :) You probably will want to watch this lecture: https://www.youtube.com/watch?v=pspy1H6A3FM
My personal preference would be,
GET /users/id/{id}
GET /users/email/{email}
But it all depends on what you the rest endpoints to look like.
Which is better?
REST doesn't care; from the perspective of the client, the URI is opaque. The clients concerns are following links/submitting forms/completing templates.
Information encoded into the URI is done at the server's discretion and for its own exclusive use.
So you can use any spelling you like. As a rule, it's a good idea to conform to local spelling conventions (in much the same way that your variable names in code should conform to your coding conventions). But your clients shouldn't need to know the details of those conventions.
/users/{input}
...
if(isEmail(input)) queryByEmail(input);
else queryById(input);
Note that you aren't necessarily deeply committed to one approach; that's part of the point of decoupling the identifiers from the representations. For instance, your implementation could just as easily look like
/users/{input}
...
if(isEmail(input)) redirectTo(/users/email/{input});
else redirectTo(/users/id/{input});
which allows clients that have bookmarked the original URI to arrive at the correct resource.

REST - GET best practices for special characters

We have REST API's. I was trying to figure out the best way to do a Get with some special characters.
Currently, we have something like this: http://myhost.com/api/book/name=HarryPotter
The above URL works just fine, but gets complicated when certain special character's are included in the queryparam like '&' or '/', which will result in "No operation matching request path ... is found, HTTP Method : GET, ContentType : /, Accept : /,"
for ex: http://myhost.com/api/book/name=Dark/Thirty.
This will consider the '/' in 'Dark/Thirty' as an URL separator.
What is the best practice to be able to search such queries. Is using a JSON a better practice, if yes should I be using a GET or a POST? I believe it should be POST, as any slash in the query param is treated as an Url separator.
Meaning: even this would fail for GET. http://myhost.com/api/book/search={"name"="Dark/Thirty"}
And since this is actually not a POST i do not want to use it. As I am just listing out the books that meet my search criteria and not modifying or adding anything.
Any guideline in tackling similar problems?
This link is a good read. In essence, if your Dark/Thirty is an identifier (i.e. uniquely identifies a resource), then modify it (in a predictable pattern) so that it does not have the special characters; e.g., DarkThirty or dark-thirty. If it is, however, a search term, then you would be better served not to make it RESTful, but just pass it as a normal parameter; that's what they're for.
The difference between GET and POST is not what characters are in it, but what the objective is. GET is for getting stuff: it should be free of side effects. A search, or retrieval of a page should be a GET. POST effects changes to a server. It is improbable you would need to make an operation that both requires sending more data than URL allows, and at the same time makes no changes on the server but simply renders a new page (allowing for exceptions like Shazam or TinEye).
Dealing with special characters in GET parameters is the job of URL encoding; if you have http://myhost.com/api/search?q=Dark%FThirty for a search, your site is no less good. There are two primary drivers for REST, as I understand them: human-friendliness and SEO-friendliness. Search does not need to be either. REST exists to identify resources, in my understanding; and search results from a query are not a resource.
To summarise, I'd go with:
http://myhost.com/api/book/dark-thirty (the resource is the book)
http://myhost.com/api/search?q=Dark%FThirty (the resource is the search procedure, with arguments)
URL encoding sounds like the easiest thing to do in your case, particularly since you already have a URL structure set up for your application that looks like http://myhost.com/api/book/name={internal-identifier} where internal-identifier resolves to your book name (encoded, of course).
From the REST perspective, it doesn't particularly matter whether the URL represents a query that can return a collection of resource representations or uniquely identifies a specific resource. You can use this structure for both.

G-WAN: how to get rid of the "?" in URL and how to set default language?

In G-WAN the default URL is in the form mydomain.com/?hello.c
I want to get rid of the ? to have URLs that look like mydomain.com/hello
The user manual mentions substituting a different character such as ' for ?. In that case the URL would look like mydomain.com/'hello.c
But I don't want to use a different character, I want to get rid of the special character completely. Is that possible?
The default language for G-WAN is C. So mydomain.com/?hello means mydomain.com/?hello.c
How do I change the default to a different language, say Java, so that mydomain.com/?hello now means mydomain.com/?hello.java
Can I set different default languages for different virtual hosts?
Finally, how do I change the URL format for passing parameters? According to the user manual the default format is:
mydomain.com/?hello.c&name=Eva
I want to change it to:
mydomain.com/hello?name=Eva
Is that possible?
This has already been asked many times, and a few solutions are found here:
G-WAN handler rewriting solution
You should note, however, that the way you mean to pass arguments as ?something=answer instead of & only applies to the first argument passed. You can't do ?this=that?somethingelse=this because only the first can be ? and the rest must be &. In fact you can ignore using ? completely and only use & with virtually unlimited arguments so it's in fact better to stick to only using &.
It's important to note for future reference to anyone asking similar questions, G-WAN gives you the entire headers through multiple steps of the HTTP transaction and being that you can modify them with c/c++, you can change anything at all that you want before the requests are handled by the server or sent back to the client. The only limitation is your knowledge and imagination.

How to version REST URIs

What is the best way to version REST URIs? Currently we have a version # in the URI itself, ie.
http://example.com/users/v4/1234/
for version 4 of this representation.
Does the version belong in the queryString? ie.
http://example.com/users/1234?version=4
Or is versioning best accomplished another way?
Do not version URLs, because ...
you break permalinks
The url changes will spread like a disease through your interface. What do you do with representations that have not changed but point to the representation that has? If you change the url, you break old clients. If you leave the url, your new clients may not work.
Versioning media types is a much more flexible solution.
Assuming that your resource is returning some variant of application/vnd.yourcompany.user+xml all you need to do is create support for a new application/vnd.yourcompany.userV2+xml media type and through the magic of content negotiation your v1 and v2 clients can co-exist peacefully.
In a RESTful interface, the closest thing you have to a contract is the definition of the media-types that are exchanged between the client and the server.
The URLs that the client uses to interact with the server should be provided by the server embedded in previously retrieved representations. The only URL that needs to be known by the client is the root URL of the interface. Adding version numbers to urls only has value if you construct urls on the client, which you are not suppose to do with a RESTful interface.
If you need to make a change to your media-types that will break your existing clients then create a new one and leave your urls alone!
And for those readers currently saying that this makes no sense if I am using application/xml and application/json as media-types. How are we supposed to version those? You're not. Those media-types are pretty much useless to a RESTful interface unless you parse them using code-download, at which point versioning is a moot point.
I would say making it part of the URI itself (option 1) is best because v4 identifies a different resource than v3. Query parameters like in your second option can be best used to pass-in additional (query) info related to the request, rather than the resource.
Ah, I'm putting my old grumpy hat on again.
From a ReST perspective, it doesn't matter at all. Not a sausage.
The client receives a URI it wants to follow, and treats it as an opaque string. Put whatever you want in it, the client has no knowledge of such a thing as a version identifier on it.
What the client knows is that it can process the media type, and I'll advise to follow Darrel's advice. Also I personally feel that needing to change the format used in a restful architecture 4 times should bring huge massive warning signs that you're doing something seriously wrong, and completely bypassing the need to design your media type for change resiliance.
But either way, the client can only process a document with a format it can understand, and follow links in it. It should know about the link relationships (the transitions). So what's in the URI is completely irrelevant.
I personally would vote for http://localhost/3f3405d5-5984-4683-bf26-aca186d21c04
A perfectly valid identifier that will prevent any further client developer or person touching the system to question if one should put v4 at the beginning or at the end of a URI (and I suggest that, from the server perspective, you shouldn't have 4 versions, but 4 media types).
You should NOT put the version in the URL, you should put the version in the Accept Header of the request - see my post on this thread:
Best practices for API versioning?
If you start sticking versions in the URL you end up with silly URLs like this:
http://company.com/api/v3.0/customer/123/v2.0/orders/4321/
And there are a bunch of other problems that creep in as well - see my blog:
http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html
These (less-specific) SO questions about REST API versioning may be helpful:
Versioning RESTful services?
Best practices for web service REST API versioning
There are 4 different approaches to versioning the API:
Adding version to the URI path:
http://example.com/api/v1/foo
http://example.com/api/v2/foo
When you have breaking change, you must increment the version like: v1, v2, v3...
You can implement a controller in you code like this:
#RestController
public class FooVersioningController {
#GetMapping("v1/foo")
public FooV1 fooV1() {
return new FooV1("firstname lastname");
}
#GetMapping("v2/foo")
public FooV2 fooV2() {
return new FooV2(new Name("firstname", "lastname"));
}
Request parameter versioning:
http://example.com/api/v2/foo/param?version=1
http://example.com/api/v2/foo/param?version=2
The version parameter can be optional or required depending on how you want the API to be used.
The implementation can be similar to this:
#GetMapping(value = "/foo/param", params = "version=1")
public FooV1 paramV1() {
return new FooV1("firstname lastname");
}
#GetMapping(value = "/foo/param", params = "version=2")
public FooV2 paramV2() {
return new FooV2(new Name("firstname", "lastname"));
}
Passing a custom header:
http://localhost:8080/foo/produces
With header:
headers[Accept=application/vnd.company.app-v1+json]
or:
headers[Accept=application/vnd.company.app-v2+json]
Largest advantage of this scheme is mostly semantics: You aren’t cluttering the URI with anything to do with the versioning.
Possible implementation:
#GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v1+json")
public FooV1 producesV1() {
return new FooV1("firstname lastname");
}
#GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v2+json")
public FooV2 producesV2() {
return new FooV2(new Name("firstname", "lastname"));
}
Changing Hostnames or using API Gateways:
Essentially, you’re moving the API from one hostname to another. You might even just call this building a new API to the same resources.
Also,you can do this using API Gateways.
I wanted to create versioned APIs and I found this article very useful:
http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http
There is a small section on "I want my API to be versioned". I found it simple and easy to understand. The crux is to use Accept field in the header to pass version information.
If the REST services require authentication before use, you could easily associate the API key/token with an API version and do the routing internally. To use a new version of the API, a new API key could be required, linked to that version.
Unfortunately, this solution only works for auth-based APIs. However, it does keep versions out of the URIs.
If you use URIs for versioning, then the version number should be in the URI of the API root, so every resource identifier can include it.
Technically a REST API does not break by URL changes (the result of the uniform interface constraint). It breaks only when the related semantics (for example an API specific RDF vocab) changes in a non backward compatible way (rare). Currently a lot of ppl do not use links for navigation (HATEOAS constraint) and vocabs to annotate their REST responses (self-descriptive message constraint) that's why their clients break.
Custom MIME types and MIME type versioning does not help, because putting the related metadata and the structure of the representation into a short string does not work. Ofc. the metadata and the structure will frequently change, and so the version number too...
So to answer your question the best way to annotate your requests and responses with vocabs (Hydra, linked data) and forget versioning or use it only by non backward compatible vocab changes (for example if you want to replace a vocab with another one).
I'd include the version as an optional value at the end of the URI. This could be a suffix like /V4 or a query parameter like you've described. You might even redirect the /V4 to the query parameter so you support both variations.
I vote up for doing this in mime type but not in URL.
But the reason is not the same as other guys.
I think the URL should be unique (excepting those redirects) for locating the unique resource.
So, if you accept /v2.0 in URLs, why it is not /ver2.0 or /v2/ or /v2.0.0? Or even -alpha and -beta? (then it totally becomes the concept of semver)
So, the version in mime type is more acceptable than the URL.