ChatGPT解决这个技术问题 Extra ChatGPT

REST API Best practices: Where to put parameters? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers. Closed 7 years ago. Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.

A REST API can have parameters in at least two ways:

As part of the URL-path (i.e. /api/resource/parametervalue ) As a query argument (i.e. /api/resource?parameter=value )

What is the best practice here? Are there any general guidelines when to use 1 and when to use 2?

Real world example: Twitter uses query parameters for specifying intervals. (http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321)

Would it be considered better design to put these parameters in the URL path?


C
Community

If there are documented best practices, I have not found them yet. However, here are a few guidelines I use when determining where to put parameters in an url:

Optional parameters tend to be easier to put in the query string.

If you want to return a 404 error when the parameter value does not correspond to an existing resource then I would tend towards a path segment parameter. e.g. /customer/232 where 232 is not a valid customer id.

If however you want to return an empty list then when the parameter is not found then I suggest using query string parameters. e.g. /contacts?name=dave

If a parameter affects an entire subtree of your URI space then use a path segment. e.g. a language parameter /en/document/foo.txt versus /document/foo.txt?language=en

I prefer unique identifiers to be in a path segment rather than a query parameter.

The official rules for URIs are found in this RFC spec here. There is also another very useful RFC spec here that defines rules for parameterizing URIs.


The official rule URIs and the draft sepc were really useful & interesting! :-)
The 404 error test helps me a lot to avoid putting information into the path that belongs in query parameters, headers, or the request body. Thanks for point that out!
T
Tor Valamo

Late answer but I'll add some additional insight to what has been shared, namely that there are several types of "parameters" to a request, and you should take this into account.

Locators - E.g. resource identifiers such as IDs or action/view Filters - E.g. parameters that provide a search for, sorting or narrow down the set of results. State - E.g. session identification, api keys, whatevs. Content - E.g. data to be stored.

Now let's look at the different places where these parameters could go.

Request headers & cookies URL query string ("GET" vars) URL paths Body query string/multipart ("POST" vars)

Generally you want State to be set in headers or cookies, depending on what type of state information it is. I think we can all agree on this. Use custom http headers (X-My-Header) if you need to.

Similarly, Content only has one place to belong, which is in the request body, either as query strings or as http multipart and/or JSON content. This is consistent with what you receive from the server when it sends you content. So you shouldn't be rude and do it differently.

Locators such as "id=5" or "action=refresh" or "page=2" would make sense to have as a URL path, such as mysite.com/article/5/page=2 where partly you know what each part is supposed to mean (the basics such as article and 5 obviously mean get me the data of type article with id 5) and additional parameters are specified as part of the URI. They can be in the form of page=2, or page/2 if you know that after a certain point in the URI the "folders" are paired key-values.

Filters always go in the query string, because while they are a part of finding the right data, they are only there to return a subset or modification of what the Locators return alone. The search in mysite.com/article/?query=Obama (subset) is a filter, and so is /article/5?order=backwards (modification). Think about what it does, not just what it's called!

If "view" determines output format, then it is a filter (mysite.com/article/5?view=pdf) because it returns a modification of the found resource rather than homing in on which resource we want. If it instead decides which specific part of the article we get to see (mysite.com/article/5/view=summary) then it is a locator.

Remember, narrowing down a set of resources is filtering. Locating something specific within a resource is locating... duh. Subset filtering may return any number of results (even 0). Locating will always find that specific instance of something (if it exists). Modification filtering will return the same data as the locator, except modified (if such a modification is allowed).

Hope this helped give people some eureka moments if they've been lost about where to put stuff!


Why isn't id a filter then? It returns a subset of the resource
@Jonathan. no it returns a specific resource, namely article number 5. A filter is always a way to narrow down a search in a collection of resources. If you want just that specific resource, then there should be a designated way to get that. Filtering means you have the possibility of returning multiple resources. An ID is not a filter, it's a definite single resource. If you had a RANGE of IDs, then it would be a filter, even if the range just included one ID. If the filter also included types of resources, it would return all resources with ID 5, not just the article.
@Jonathan.: like DarrelMiller mentioned, you would expect a request on object/id to return 404 in case of unknown id, while you would expect object?id=id to return and empty list. Also, I would consider that any type of filtering/subsetting should return a list.
Pages is a difficult one, because as you say it can be a filter of a resource (collection of pages), but then at that same time it is a specific resource within that collection. I would always request an article page by locator, not filter. However, the page can be a filter of a list of something, say a list of users. But then the page is inherently a delimiter, aka "start at item (page-1)*perpage and show perpage items". Using it as a filter is correct then, but for different reasons. Calling it "page" is technically wrong. More semantically correct would be to call it "from" or "startAt"
(continued) The semantic meaning of "page" is that it is a specific resource that doesn't change. It comes from physical print. If we never had books or printed stuff, "page" wouldn't really be a word. If you have a dynamic list of items, split into "pages", you should really provide a specific starting point, either numerical, alphabetical or even item-specific, as well as a "how many per page" filter. If I want to reference something in your list, I want specifics. Also I don't want to go to page 5 only to realize you've now changed the internal perpage to 50 instead of 20.
m
manuel aldana

It depends on a design. There are no rules for URIs at REST over HTTP (main thing is that they are unique). Often it comes to the matter of taste and intuition...

I take following approach:

url path-element: The resource and its path-element forms a directory traversal and a subresource (e.g. /items/{id} , /users/items). When unsure ask your colleagues, if they think that traversal and they think in "another directory" most likely path-element is the right choice

url parameter: when there is no traversal really (search resources with multiple query parameters are a very nice example for that)


There are actually pretty clear rules on how a URI is supposed to look, and very little ambiguity on how to apply them to RESTful URIs.
P
PeterWong

IMO the parameters should be better as query arguments. The url is used to identify the resource, while the added query parameters to specify which part of the resource you want, any state the resource should have, etc.


Actually, both the path and the query are used in combination to identify the resource. This was clarified in RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
@DarrelMiller I know this is an old post but I'm interested to know more about the fact query parameters are also used to identify the resource. The link you provided is now dead. I've looked at RFC3986 but I don't see how you deduced this fact. Also, by definition, an identifier parameters should not be optional so it doesn't seem appropriated to use query parameters for identification.
@MickaelMarrache See the first line in section 3.4 tools.ietf.org/html/rfc3986#section-3.4
@DarrelMiller Thanks! My question comes from the fact that generally, intermediary HTTP components don't cache responses of requests that contains a query string. So, it seems that query parameters are more appropriated to search resources according to some criterias and not to uniquely identify a resource.
B
Brett Caswell

As per the REST Implementation,

1) Path variables are used for the direct action on the resources, like a contact or a song ex.. GET etc /api/resource/{songid} or GET etc /api/resource/{contactid} will return respective data.

2) Query perms/argument are used for the in-direct resources like metadata of a song ex.., GET /api/resource/{songid}?metadata=genres it will return the genres data for that particular song.


There isn't actually a REST standard. Per Wikipedia: Unlike SOAP-based web services, there is no "official" standard for RESTful web APIs.[14] This is because REST is an architectural style, unlike SOAP, which is a protocol. Even though REST is not a standard, a RESTful implementation such as the Web can use standards like HTTP, URI, XML, etc.
I don't like the 2 approach. I would rather preffer /api/genres?songid=123 or /api/songs/{song-id}/genres
@Bart, Satish was referring to Variables in the path, which is essentially what you referenced to as your preference.. however, if genres is actually metadata, and not a field of the song entity/resource.. then I could see more sensibility in using a query string on it..
@BrettCaswell got it! thanks for pointing it out. really appreciate it!
C
Community

"Pack" and POST your data against the "context" that universe-resource-locator provides, which means #1 for the sake of the locator.

Mind the limitations with #2. I prefer POSTs to #1.

note: limitations are discussed for

POST in Is there a max size for POST parameter content?

GET in Is there a limit to the length of a GET request? and Max size of URL parameters in _GET

p.s. these limits are based on the client capabilities (browser) and server(configuration).


add-on: witty routes can have versions (distinguished via headers) thus provide evolved functionality with no need to alter code that consumes the rest-full (api) code that you write as in restify -> look for Versioned Routes
C
Community

According to the URI standard the path is for hierarchical parameters and the query is for non-hierarchical parameters. Ofc. it can be very subjective what is hierarchical for you.

In situations where multiple URIs are assigned to the same resource I like to put the parameters - necessary for identification - into the path and the parameters - necessary to build the representation - into the query. (For me this way it is easier to route.)

For example:

/users/123 and /users/123?fields="name, age"

/users and /users?name="John"&age=30

For map reduce I like to use the following approaches:

/users?name="John"&age=30

/users/name:John/age:30

So it is really up to you (and your server side router) how you construct your URIs.

note: Just to mention these parameters are query parameters. So what you are really doing is defining a simple query language. By complex queries (which contain operators like and, or, greater than, etc.) I suggest you to use an already existing query language. The capabilities of URI templates are very limited...


J
Joe Plante

As a programmer often on the client-end, I prefer the query argument. Also, for me, it separates the URL path from the parameters, adds to clarity, and offers more extensibility. It also allows me to have separate logic between the URL/URI building and the parameter builder.

I do like what manuel aldana said about the other option if there's some sort of tree involved. I can see user-specific parts being treed off like that.


M
Matt Whipple

There are no hard and fast rules, but the rule of thumb from a purely conceptual standpoint that I like to use can briefly be summed up like this: a URI path (by definition) represents a resource and query parameters are essentially modifiers on that resource. So far that likely doesn't help... With a REST API you have the major methods of acting upon a single resource using GET, PUT, and DELETE . Therefore whether something should be represented in the path or as a parameter can be reduced to whether those methods make sense for the representation in question. Would you reasonably PUT something at that path and would it be semantically sound to do so? You could of course PUT something just about anywhere and bend the back-end to handle it, but you should be PUTing what amounts to a representation of the actual resource and not some needlessly contextualized version of it. For collections the same can be done with POST. If you wanted to add to a particular collection what would be a URL that makes sense to POST to.

This still leaves some gray areas as some paths could point to what amount to children of parent resources which is somewhat discretionary and dependent on their use. The one hard line that this draws is that any type of transitive representation should be done using a query parameter, since it would not have an underlying resource.

In response to the real world example given in the original question (Twitter's API), the parameters represent a transitive query that filters on the state of the resources (rather than a hierarchy). In that particular example it would be entirely unreasonable to add to the collection represented by those constraints, and further that query would not be able to be represented as a path that would make any sense in the terms of an object graph.

The adoption of this type of resource oriented perspective can easily map directly to the object graph of your domain model and drive the logic of your API to the point where everything works very cleanly and in a fairly self-documenting way once it snaps into clarity. The concept can also be made clearer by stepping away from systems that use traditional URL routing mapped on to a normally ill-fitting data model (i.e. an RDBMS). Apache Sling would certainly be a good place to start. The concept of object traversal dispatch in a system like Zope also provides a clearer analog.


J
Jay

Here is my opinion.

Query params are used as meta data to a request. They act as filter or modifier to an existing resource call.

Example:

/calendar/2014-08-08/events

should give calendar events for that day.

If you want events for a specific category

/calendar/2014-08-08/events?category=appointments

or if you need events of longer than 30 mins

/calendar/2014-08-08/events?duration=30

A litmus test would be to check if the request can still be served without an query params.


N
NorthIsUp

I generally tend towards #2, As a query argument (i.e. /api/resource?parameter=value ).

A third option is to actually post the parameter=value in the body.

This is because it works better for multi parameter resources and is more extendable for future use.

No matter which one you pick, make sure you only pick one, don't mix and match. That leads towards a confusing API.


D
Dario Fumagalli

One "dimension" of this topic has been left out yet it's very important: there are times when the "best practices" have to come into terms with the plaform we are implementing or augmenting with REST capabilities.

Practical example:

Many web applications nowadays implement the MVC (Model, View, Controller) architecture. They assume a certain standard path is provided, even more so when those web applications come with an "Enable SEO URLs" option.

Just to mention a fairly famous web application: an OpenCart e-commerce shop. When the admin enables the "SEO URLs" it expects said URLs to come in a quite standard MVC format like:

http://www.domain.tld/special-offers/list-all?limit=25

Where

special-offers is the MVC controller that shall process the URL (showing the special-offers page)

list-all is the controller's action or function name to call. (*)

limit=25 is an option, stating that 25 items will be shown per page.

(*) list-all is a fictious function name I used for clarity. In reality, OpenCart and most MVC frameworks have a default, implied (and usually omitted in the URL) index function that gets called when the user wants a default action to be performed. So the real world URL would be:

http://www.domain.tld/special-offers?limit=25

With a now fairly standard application or frameworkd structure similar to the above, you'll often get a web server that is optimized for it, that rewrites URLs for it (the true "non SEOed URL" would be: http://www.domain.tld/index.php?route=special-offers/list-all&limit=25).

Therefore you, as developer, are faced into dealing with the existing infrastructure and adapt your "best practices", unless you are the system admin, know exactly how to tweak an Apache / NGinx rewrite configuration (the latter can be nasty!) and so on.

So, your REST API would often be much better following the referring web application's standards, both for consistency with it and ease / speed (and thus budget saving).

To get back to the practical example above, a consistent REST API would be something with URLs like:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

or (non SEO URLs)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

with a mix of "paths formed" arguments and "query formed" arguments.


D
Daniel Watrous

I see a lot of REST APIs that don't handle parameters well. One example that comes up often is when the URI includes personally identifiable information.

http://software.danielwatrous.com/design-principles-for-rest-apis/

I think a corollary question is when a parameter shouldn't be a parameter at all, but should instead be moved to the HEADER or BODY of the request.


j
jfcorugedo

It's a very interesting question.

You can use both of them, there's not any strict rule about this subject, but using URI path variables has some advantages:

Cache: Most of the web cache services on the internet don't cache GET request when they contains query parameters. They do that because there are a lot of RPC systems using GET requests to change data in the server (fail!! Get must be a safe method)

But if you use path variables, all of this services can cache your GET requests.

Hierarchy: The path variables can represent hierarchy: /City/Street/Place

It gives the user more information about the structure of the data.

But if your data doesn't have any hierarchy relation you can still use Path variables, using comma or semi-colon:

/City/longitude,latitude

As a rule, use comma when the ordering of the parameters matter, use semi-colon when the ordering doesn't matter:

/IconGenerator/red;blue;green

Apart of those reasons, there are some cases when it's very common to use query string variables:

When you need the browser to automatically put HTML form variables into the URI

When you are dealing with algorithm. For example the google engine use query strings:

http:// www.google.com/search?q=rest

To sum up, there's not any strong reason to use one of this methods but whenever you can, use URI variables.