Is it bad practice to allow specifying parameters in URL for POST - rest

Should parameters for POST requests (elements of the resource being created) be allowed to be added to the URL as well as in the body?
For example, let say I have a POST to create a new user at
/user
With the full set of parameters name, email, etc... in the body of the request.
However, I've seen many API's would accept the values in either the body or URL parameters like this:
/user?name=foo&email=foo#bar.com
Is there any reason this second option, allowing the parameters in the URL is bad practice? Does it violate any component of REST?

The intent of a query parameter is to help identify the target resource for a request. The body of a POST should be used to specify instructions to the server.
The query component contains non-hierarchical data that, along with
data in the path component (Section 3.3), serves to identify a
resource within the scope of the URI's scheme and naming authority
(if any).
    -- RFC 3986 Section 3.4
The hierarchical path component and optional query component serve
as an identifier for a potential target resource within that origin
server's name space.
    -- RFC 7230 Section 2.7.1

The Udacity Web Development course, be Steve Huffman (the man behind Reddit), recommends only using POST requests to update server side data. Steve highlights why using GET parameters to do so can be problematic.

Related

What is the "Restful" way to command a server?

I have a REST endpoint to create an application configuration as such
POST /applications
With a body
{
"appName" : "my-new-app"
}
I returns a newly created application configuration:
{
"appName": "my-new-app",
"appId": "2ed17ff700664dad9bb32e400d39dc68",
"apiKey": "$2a$10$XVDH9F3Ix4lx2LdxeJ4ZOe7H.bw/Me5qAmaIGF.95lUgkerfTG7NW",
"masterKey": "$2a$10$XVDH9F3Ix4lx2LdxeJ4ZOeSZLR1hVSXk2We/DqQahyOFFY6nOfbHS",
"dateCreated": "2021-03-28T11:00:07.340+00:00",
"dateUpdated": "2021-03-28T11:00:07.340+00:00"
}
Note: The keys are auto-generated in the server and not passed from the client.
My question here is, what's the RESTful way to command the server to reset the keys for example:
PUT /applications/my-new-app/update_keys is not noun-based and thus, not restful, also passing a command as query parameter does not also seem to be restful since this is not a GET method rather it's a PUT (update) method.
Here's one way to send a command that is as much as possible RESTful:
Endpoint:
POST /application/:appName/actions
Example Payload:
{
"actions" : [
{
"action" : "name_of_command",
"arguments" : {
"arg1" : "param1"
}
},
{
"action" : "reset_keys",
"arguments" : {
}
}
]
}
Actions would be nouns that are part of the endpoint, and the server will process actions that are submitted (or posted) within the endpoint. And an array of actions would be best suited to allow multiple actions to be sent. And each action having arguments would also be desirable for future actions that would need arguments.
what's the RESTful way to command the server to reset the keys for example:
How would you do it with a web site?
You would be looking at some web page like /www/applications/my-new-app; within the data or the metadata you would find a link. Following that link would bring you to a form; the form would have input controls describing what fields you need to provide to send the message, in addition to any "hidden" inputs. When you click the submit button, your user agent would collect your inputs, construct from them the appropriate message body, then use the form metadata to determine what request method and uri to use.
The client never has to guess what URI to use, because the server is providing links to guide the way.
Hypertext is at the heart of the uniform interface
REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.
Because the server is providing the URI for each of the links, you've got some freedom ot choose which resource "handles" which message.
One interesting way to resolve this to look at HTTP's rules for cache invalidation. The short version is that successful unsafe requests (PATCH/POST/PUT) invalidate the representations of the target-uri.
In other words, we take advantage of cache-invalidation by sending the command to the resource that we are trying to change.
So, assuming that retrieving the representation of the app occurred via a request like:
GET /applications/my-new-app HTTP/x.y
Then we would ask the server to change that resource by sending a request with that same target-uri. Something analogous to:
POST /applications/my-new-app HTTP/x.y
Content-Type: text/plain
Please rotate the keys
Form submissions on the web are usually a representation of key/value pairs, so a more likely spelling would be:
POST /applications/my-new-app HTTP/x.y
Content-Type: applications/x-www-form-urlencoded
action=Please%20rotate%20the%20keys
Your form that describes this request my have an "action" input control, that accepts text from the client, or more likely in this case action would be a hidden control with a pre-defined value.
Note: if we have multiple actions that should invalidate the /applications/my-new-app representations, we would probably use POST for all of them, and resolve the ambiguity at the server based on the request-body (if our routing framework gives us the degree of control we need, we can use that - but more common would be to have a single POST handler for each Content-Type, and parse the request body "by hand".
POST serves many useful purposes in HTTP, including the general purpose of “this action isn’t worth standardizing.” -- Fielding 2009
PUT /applications/my-new-app/update_keys is not noun-based and thus, not restful,
That's not true: REST doesn't care what spelling conventions you use for your resource identifiers. For example
https://www.merriam-webster.com/dictionary/get
https://www.merriam-webster.com/dictionary/post
https://www.merriam-webster.com/dictionary/put
https://www.merriam-webster.com/dictionary/update
These all work fine, just like every other resource on the web.
You absolutely can design your resource model so that editing the update_keys document also modifies the my-new-app document.
The potential difficulty is that general purpose components are not going to know what is going on. HTTP PUT means "update the representation of the target resource", and every general purpose component knows that; the origin server is allowed to modify other resources as a consequence of the changes to the "update-keys" resource.
But we don't have a great language for communicating the general purpose components all of the side effects that may have happened. Without some special magic, previously cached copies of my-new-app, with the original, unrotated, keys, will be left lying around. So the client may be left with a stale copy of the document that describes the app.
(An example of "some special magic" would be Linked Cache Invalidation, which affords describing caching relationships between resources using web linking. Unforunately, LCI has not been adopted as a standard, and you won't find the described link relations in the IANA registry.)

The difference between XPOST and XPUT

I am learning Elasticsearch, I found that XPOST and XPUT are in general the same when 'update' or 'replace' documents. They all change the field values.
curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
{
"name": "Jane Doe"
}'
curl -XPOST 'localhost:9200/customer/external/1/_update?pretty' -d '
{
"doc": { "name": "Jane Doe" }
}'
So they all changed the name field to "Jane Doe". I am wondering whats the difference between XPOST and XPUT in the above context.
The two commands are not at all the same. The first one (with PUT) will update a full document, not only the field you're sending.
The second one (with POST) will do a partial update and only update the fields you're sending, and not touch the other ones already present in the document.
firstly, -X is a flag of curl.
please see -X in the man page. It is same as --request. You can specify which HTTP method to use (POST, GET, PUT, DELETE etc)
http://curl.haxx.se/docs/manpage.html
Regarding POST and PUT, they are HTTP methods or "verbs".
ElasticSearch provides us with a REST API. According to REST practices, POST is for create and PUT is for updating a record.
Please see:
http://www.restapitutorial.com/lessons/httpmethods.html
HTTP PUT:
PUT puts a file or resource at a specific URI, and exactly at that URI. If there's already a file or resource at that URI, PUT replaces that file or resource. If there is no file or resource there, PUT creates one. PUT is idempotent, but paradoxically PUT responses are not cacheable.
HTTP 1.1 RFC location for PUT
HTTP POST:
POST sends data to a specific URI and expects the resource at that URI to handle the request. The web server at this point can determine what to do with the data in the context of the specified resource. The POST method is not idempotent, however POST responses are cacheable so long as the server sets the appropriate Cache-Control and Expires headers.
The official HTTP RFC specifies POST to be:
Annotation of existing resources;
Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles;
Providing a block of data, such as the result of submitting a form, to a data-handling process;
Extending a database through an append operation.
HTTP 1.1 RFC location for POST
Difference between POST and PUT:
The RFC itself explains the core difference:
The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource. If the server desires that the request be applied to a different URI, it MUST send a 301 (Moved Permanently) response; the user agent MAY then make its own decision regarding whether or not to redirect the request.
Using the right method, unrelated aside:
One benefit of REST ROA vs SOAP is that when using HTTP REST ROA, it encourages the proper usage of the HTTP verbs/methods. So for example you would only use PUT when you want to create a resource at that exact location. And you would never use GET to create or modify a resource.
upvote if it helps you :)
PUT method is idempotent so if you hit payload with put method, it will create first time only and if you hit the same request again and again, it won't create new records, it will simply update the previously created.
On the other hand if you hit the payload with POST method no of times you will create a no of entries of same payload.

Is it proper REST design to send all data for a PUT on the URI?

If I have very simple data to send to the server, is it okay to set up a URI scheme where all of it can be sent on the URI instead of in the body? For example, suppose I'm setting user preferences. I envision something like this:
PUT
/preferences/{setting-name}/{setting-value}
This allows my client code to be very simple because I can put the entirety of the message in the URI. Is that okay? Or should I be doing something more like:
PUT
/preferences/{setting-name}
...with the value in the content? Thanks!
Your first URI implies that ther is a (sub-)resource at
/preferences/{setting-name}/{setting-value}
But there is not resource, only a value. Using such an URI is not RESTful since it does not address a resource.
Your second URI is slightly better since it addresses a subresource {setting-name} of the preferences resource.
But I would prefer a third approach:
POST /preferences
Content-Type: application/json
{
"setting-name": "setting-value",
}
to put one preference. Note the usage of POST instead of PUT. PUT is generally interpreted as containing a complete representation of the resource in the request body. Such a request should overwrite all settings. Using POST is generally interpreted as overwriting only the setting specified in the body. Other settings are meant to be unchanged.
Note:
What makes me wonder is the front of your URI. Is there really only one set of preferences? Can and should all clients access the same set of preferences? If, for example, the preferences are user-based, you should use URIs like
/user/{user-id}/preferences/{setting-name}
to access the {setting-name} preference value of user {user-id}.

How to use URI as a REST resource?

I am building a RESTful API for retrieving and storing comments in threads.
A comment thread is identified by an arbitrary URI -- usually this is the URL of the web page where the comment thread is related to. This design is very similar to what Disqus uses with their system.
This way, on every web page, querying the related comment thread doesn't require storing any additional data at the client -- all that is needed is the canonical URL to the page in question.
My current implementation attempts to make an URI work as a resource by encoding the URI as a string as follows:
/comments/https%3A%2F%2Fexample.org%2Ffoo%2F2345%3Ffoo%3Dbar%26baz%3Dxyz
However, before dispatching it to my application, the request URI always gets decoded by my server to
/comments/https://example.org/foo/2345?foo=bar&baz=xyz
This isn't working because the decoded resource name now has path delimiters and a query string in it causing routing in my API to get confused (my routing configuration assumes the request path contains /comments/ followed by a string).
I could double-encode them or using some other encoding scheme than URI encode, but then that would add complexity to the clients, which I'm trying to avoid.
I have two specific questions:
Is my URI design something I should continue working with or is there a better (best?) practice for doing what I'm trying to do?
I'm serving API requests with a Go process implemented using Martini 'microframework'. Is there something Go or Martini specific that I should do to make the URI-encoded resource names to stay encoded?
Perhaps a way to hint to the routing subsystem that the resource name is not just a string but a URL-encoded string?
I don't know about your url scheme for your application, but single % encoded values are valid in a url in place of the chars they represent, and should be decoded by the server, what you are seeing is what I would expect. If you need to pass url reserved characters as a value and not have them decoded as part of the url, you will need to double % encode them. It's a fairly common practice, the complexity added to the client & server will not be that much, and a short comment will do rightly.
In short, If you need to pass url chars, double % encode them, it's fine.

a restful api only uses clean urls - no url variables or post variables

A restful api has to use either get, post, put or delete request methods. The behavaiour and data submitted is entirely determined by the uri string. No query paramters or post variables.
Is this true ?
Valid : http://example.com/foo/84
Not valid : http://example.com/foo/?value=84
Valid :
$.ajax({
  type: 'POST',
  url: "http://example.com/foo/84",
  success: success,
  dataType: dataType
});
Not valid :
$.ajax({
type: 'POST',
url: "http://example.com/foo/",
data: 84,
success: success,
dataType: dataType
});
edit
Two answers so far, and the contradict each other.
Here goes a third answer that contradicts the other two.
RESTful URI is almost an oxymoron. The semantics of the URI is irrelevant to REST, the only thing that matters to REST is that one URI identifies only one resource. Other than that, an URI is an atomic identifier, and its semantics are irrelevant.
For REST it doesn't matter if the URI pointing to a user resource for Joe Doe is:
http://example.com/users/joedoe
Or:
http://example.com/users?username=joedoe
Or:
http://example.com/jif892mfd02-18f2
Or even:
ftp://example.com/users/joedoe.json
It doesn't matter! URIs don't need to have any meaning in a RESTful application. People spend so much time designing meaningful URIs for their REST application, while they should be concerned with their media types. When you click a link on a webpage, you don't care about the semantics of the URI, you only care about the label. The same thing happens with a client using a REST API. The documentation of your media type should describe what links are available and what they do, through labels, and you simply follow them.
If you're concerned with the semantics of the URI, this is a sign that your clients are building URIs from some template in documentation, and you're not using HATEOAS, which means you're not doing REST at all.
POST variables are definitely OK otherwise how would you submit a new resource or update it?
GET parameters are fine to specify how the resource should be rendered. So indeed http://example.com/foo/?value=84 is not right - the URL doesn't represent a resource.
However, http://example.com/user/84?fields=first_name,last_name would be ok. In that case, you would use the additional query parameters to specify that you only want the first name and last name for that resource.
Saying that http://example.com/foo/?value=84 is not valid is not entireley true. What i mean is that as long that it is a valid URL it will work and you'll be able to get the parameters via get or post.
On the other hand, REST is an architecture and one of its demands is a clean URL (one that does not include params with a '?') so such a url is not considered a REST like URL.
So if you intend to build a REST based application, you should only use clean urls.
EDIT:
I see from the comments below you have a problem understanding what is REST so i'll try to give a simple example:
In order to get data you will probably use http://example.com/foo/84 as a get request and the rest FW knows to get resource foo with id 84.
In order to post data about foo, you might call: http://example.com/foo/84 as a POST request and now the Rest FW know that since its a post request it will call the method responsible for handling post and not the one for handling get
To delete, you call the same URL with DELETE action and i think you know the rest.
So, although you have the same URL it really matters if its a GET/POST/PUT/DELETE request.