The “model” facility seems intended to reduce duplication — when specifying the requests and responses to various methods on a resource, the same body will frequently be specified multiple times. So one can define a “model” and then “refer” to it within various request and response “payload” sections.
So I’ve defined a model like so:
### An Event
* Model (application/vnd.document+json)
{
"_type": "document",
...
}
and I can then refer to it in various places:
* Response 200
[An Event][]
which works great; the output includes the entire model body in place of the cross-reference.
However, I can’t figure out how to combine this reference with a request or response header.
I’ve tried various formats along these lines:
* Request
* Headers
If-Match: some-etag-value
* Body
[An Event][]
or
* Request
* Headers
If-Match: some-etag-value
[An Event][]
with no luck.
Is there any way to do this?
There is currently no way to alter referenced model's payload. Neither it's planned in any of API Blueprint Milestones.
Feel free to add it to the milestones and propose its syntax, if you will. Before doing so please consider any potential synergy with planned API Blueprint Object Traits.
Related
I'm trying to learn how to create an API (I use Laravel in the backend and Postman to send requests), but I have a basic doubt when sending data to be processed in the backend.
I see that there are several ways to send data to the backend, but I'm not sure which is the right way to do it.
For example, with Postman I have seen that the sending can be done as parameters through the URI:
www.example.com/api/v1/orders?limit=10&offset=20
I can also do it in the body of the request through the tags
form data
x-www-form-urlencoded
raw
other ...
I understand that I can make the request along with sending data in several ways. I would like to know what should be the correct, standard or optimal way to do it for usual requests such as getting a series of records with a filtering, an order or a pagination.
I would also like to know if the way of sending data should depend on the verb to be used in the request.
My main question/problem is that I would like the way users use the API to be as simple or suitable as possible for them. I'm clear that I want to always return the data (when necessary) in JSON format but I'm not clear on how it should be sent.
Please, could someone clarify these doubts (maybe a link to a page where this kind of doubts are dealt with).
Thank you very much in advance.
It depends:
GET, HEAD and DELETE don't have a request body so all parameters have to be send via URL
POST can be easily sent via form data in Laravel
For PUT/PATCH I prefer application/json because PHP sends it via php://input stream which can have some problems in Laravel sometimes
You can also combine URL parameters and the request body. Compound types (for example models) can only be send as one via request body while it might suffice to send an id via URL parameter.
I guess, nearly more important is the overall format and documentation. The format should be consistent, easy to understand and maybe standardized (for example: https://jsonapi.org/format/#crud).
Keep in mind that forms do two things by default:
Only having methods GET and POST
Only having ectypes application/x-www-form-urlencoded, multipart/form-data and text/plain
If you want to enforce something else, you have to use scripts/libraries to do this.
Nowadays, it appears that JSON content (for POST, PUT, and PATCH) is the most popular and readable. It is well recognizable and clean. Examples in the documentation are easy to read.
I would go for JSON for both, incoming parameters and the outgoing response. This regards parameters related to the business logic of your application.
At the same time, for GET, HEAD, and DELETE methods, you don't have a payload at all. For parameters related to controlling the API (i.e. not strictly related to the business logic of the application, but to the API itself) I'd go for query parameters. This applies to parameters like limit, offset, order_by, etc.
P.S. There is only one caveat related to the JSON format. If your API happens to have file parameters you may face the problem. You can still use JSON format, but in such a case, you should encode your files (e.g. using base64) and put it as string parameters of your JSON. This may be demanding for the consumers of your API ;) This will also enlarge your files and will probably force you to process these files in memory. The alternative is to use multipart/form-data as a request Content-Type - this way you can have both, the form and separate "space" for files. It's worth keeping this case in mind when you decide.
Some GCP API methods require an empty request body, others require {} in the body. I can't figure out any pattern.
Examples of methods that require an empty request body, and return an error if called with {}:
https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics/getIamPolicy
https://cloud.google.com/iam/reference/rest/v1/roles/list
Examples of methods that require {} in the body, and return an error if called with an empty body:
https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/getIamPolicy
https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics/create
Confusingly, all four of these docs say that the request body must be empty! For the second group, I'd say that's a bug: the body must be non-empty; it must be {}.
This is pretty annoying - it feels like random difference peppered across the methods? Is there any rhyme or reason here? Couldn't the body {} methods accept an empty body?
Some ideas that don't seem to explain the difference:
Since many products use IAM, those functions could have quirky behavior. But see above - getIamPolicy is different between products.
Different product teams could decide on different local conventions. But see above - the pubsub API has calls in each camp.
The first two links that you shared are HTTP GET methods, which should not have a body, as it should only retrieve data, and all the information can be passed through the URL and some query parameters.
The other two links are HTTP PUT methods, which expect a payload to update the current content of a given entity.
You can find more explanation about how the HTTP methods are defined in the IETF RFC 2616, explaining the HTTP protocol.
I'd like to be able to document the parameters as if they were URL parameters, since I like how that bit of documentation renders a handy table. However, in my API, I would like those paremeters to plug into the JSON body rather than the URL. Is there a way to achieve this?
The dedicated syntax for describing, discussing (and thus also validating) message-body is in the making.
It will be based on the Markdown Syntax for Object Notation, similar to the actual URI Parameters description syntax (eventually these two should converge).
Also see related How to specify an optional element for a json request object and Is it possible to document what JSON response fields are? questions.
I am trying to make a RESTful api and have some function which needs credentials. For example say I'm writing a function which finds all nearby places within a certain radius, but only authorised users can use it.
One way to do it is to send it all using GET like so:
http://myapi.heroku.com/getNearbyPlaces?lon=12.343523&lat=56.123533&radius=30&username=john&password=blabla123
but obviously that's the worst possible way to do it.
Is it possible to instead move the username and password fields and embed them as POST variables over SSL, so the URL will only look like so:
https://myapi.heroku.com/getNearbyPlaces?lon=12.343523&lat=56.123533&radius=30
and the credentials will be sent encrypted.
How would I then in Sinatra and Ruby properly get at the GET and POST variables? Is this The Right Way To Do It? If not why not?
If you are really trying to create a restful API instead if some URL endpoints which happen to speak some HTTP dialect, you should stick to GET. It's even again in your path, so you seem to be pretty sure it's a get.
Instead of trying to hide the username and password in GET or POST parameters, you should instead use Basic authentication, which was invented especially for that purpose and is universally available in clients (and is available using convenience methods in Sinatra).
Also, if you are trying to use REST, you should embrace the concept of resources and resoiurce collections (which is implied by the R and E of REST). So you have a single URL like http://myapi.heroku.com/NearbyPlaces. If you GET there, you gather information about that resource, if you POST, you create a new resource, if you PUT yopu update n existing resource and if you DELETE, well, you delete it. What you should do before is th structure your object space into these resources and design your API around it.
Possibly, you could have a resource collection at http://myapi.heroku.com/places. Each place as a resource has a unique URL like http://myapi.heroku.com/places/123. New polaces can be created by POSTing to http://myapi.heroku.com/places. And nearby places could be gathered by GETing http://myapi.heroku.com/places/nearby?lon=12.343523&lat=56.123533&radius=30. hat call could return an Array or URLs to nearby places, e.g.
[
"http://myapi.heroku.com/places/123",
"http://myapi.heroku.com/places/17",
"http://myapi.heroku.com/places/42"
]
If you want to be truly discoverable, you might also embrace HATEOAS which constraints REST smentics in a way to allows API clients to "browse" through the API as a user with a browser would do. To allow this, you use Hyperlink inside your API which point to other resources, kind of like in the example above.
The params that are part of the url (namely lon, lat and radius) are known as query parameters, the user and password information that you want to send in your form are known as form parameters. In Sinatra both of these type of parameters are made available in the params hash of a controller.
So in Sinatra you would be able to access your lon parameter as params[:lon] and the user parameter as params[:user].
I suggest using basic or digest authentication and a plain GET request. In other words, your request should be "GET /places?lat=x&lon=x&radius=x" and you should let HTTP handle the authentication. If I understand your situation correctly, this is the ideal approach and will certainly be the most RESTful solution.
As an aside, your URI could be improved. Having verbs ("get") and query-like adjectives ("nearby") in your resource names is not really appropriate. In general, resources should be nouns (ie. "places", "person", "books"). See the example request I wrote above; "get" is redundant because you are using a GET request and "nearby" is redundant because you are already querying by location.
I have a RESTful URL that requires either the offset or the prefix request parameter (but not both).
GET /users?offset=0&count=20
GET /users?prefix=J&count=20
What's the best way to enforce this rule? Spring has the #RequestParam annotation with the 'required' property for optional parameters, but I want to enforce an "either-or" rule on these two parameters. I know I could do it in the code, but is there another way to do it?
Also, what's the proper way to handle "impractical" requests? Say I have 100 million users; the following request, although properly RESTful, is not something I want to support:
GET /users <-- Gets all 100 million users, crashes server and browser!
What should I send back?
You can create two methods and choose one of them with #RequestMapping's params attribute:
#RequestMapping(..., params = {"prefix", "!offset"})
public String usersWithPrefix(#RequestParam("prefix") ...) { ... }
#RequestMapping(..., params = {"offset", "!prefix"})
public String usersWithOffset(#RequestParam("offset") ...) { ... }
what's the proper way to handle "impractical" requests?
The lesser-practiced principles of REST include the requirement that resources be "discoverable". If you are asked for a complete list of 800 million users and you don't want to provide it, you might instead consider serving a page that describes in some way how to filter the collection: for example, an XForms document or HTML containing a FORM element with fields for offset/prefix/count, or a URI template with the appropriate parameters
Or you could just send a "413 Entity too large" error - edit: no you can't. Sorry, I misread the description of whath this code is for
If you decide to go down the route of just sending the first page, I think I would send it as an HTTP redirect to /users?offset=0&count=20 so that the client has a better idea they've not got the full collection (and if your response contains a link to access subsequent pages, even better)