Rails format specifier differences - ruby-on-rails

I'm about to lead a training seminar on REST for some coworkers, and I'd like to verify something regarding Rails routing.
Our app in its current form allows clients to specify format in three different ways:
1.
/path/to/resource.json
2.
/path/to/resource?format=json
3.
Accept header of the request
My question pertains to the first 2 options: is there any inherent difference in what these specifications do? Specifically, do they set only the Accept header, or the Content-Type header as well?

Well, 1 and 2 are not exactly different, since Rails typically generates routes like:
/something(.:format)
That means "there is an optional parameter format delimited with a dot". Parameters, however, can also be specified in query string, which is not part of the route.
So the second way of querying for JSON will make the route system think that the format is not in the route at all. When it comes to the controller, however, Rails will already have that query string parsed and will find the format when the time comes to respond.
That said, if you hit plain /path/to/resource without format specified anywhere, you'll get the same result as 2: you hit a route assuming there's no format given. Still, Rails will parse the headers and determine the format it should respond with.
As for what the client needs to set: accept header only, Content-Type only makes sense when the user himself sends an entity, and it's only related to "how should Rails parse incoming parameters", it's not related to response. Of course, by default Rails does its best to set Content-Type of response to be sensible.

Please checkout the following initializers:
https://github.com/rails/rails/blob/756baf296b3cb3f7bc40d5843e259276695071ab/actionpack/lib/action_dispatch/http/response.rb#L113
This is where how they look up for headers content type to set for.
if content_type = self[CONTENT_TYPE]
type, charset = content_type.split(/;\s*charset=/)
#content_type = Mime::Type.lookup(type)
#charset = charset || self.class.default_charset
end
so you can even programatically set content_type to header, or params or as a .format

Related

Accept/Content-Type header based processing in Quart and Quart-Schema

Because I am rewriting a legacy app, I cannot change what the clients either send or accept. I have to accept and return JSON, HTML, and an in-house XML-like serialization.
They do, fortunately set headers that describe what they are sending and what they accept.
So right now, what I do is have a decoder module and an encoder module with methods that are basically if/elif/else chains. When a route is ready to process/return something, I call the decoder/encoder module with the python object and the header field, which returns the formatted object as a string and the route processes the result or returns Response().
I am wondering if there is a more Quart native way of doing this.
I'm also trying to figure out how to make this work with Quart-Schema. I see from the docs that one can do app.json_encoder = <class> and I suppose I could sub in a different processor there, but it seems application global, there's no way to set it based on what the client sends. Optimally, it would be great if I could just pass the results of a dynamically chosen parser to Quart-Schema and let it do it's thing on python objects.
Thoughts and suggestions welcome. Thanks!
You can write your own decorator like the quart-schema #validation_headers(). Inside the decorator, check the header for the Content-Type, parse it, and pass the parsed object to the func(...).

Ruby On Rails - Use "Format" As A URL GET Parameter?

I have a search page where I update the URL params on the page as filters are added or removed by the user. This allows me to deep link into the page (ie. going to /search?location=new+york&time=afternoon will set the location and afternoon filters).
I also have a filter named format. I noticed that passing in ?format=whatevervalue to the URL and then reloading the page with that param causes Rails to return a Completed 406 Not Acceptable error. It seems that format is a reserved Rails URL parameter.
Is there anyway to unreserve this parameter name for a particular endpoint?
In the context of an URL in Ruby on Rails there are at least four reserved parameter names: controller, method, id, format.
You cannot use these keys for anything else than for their intended purpose.
If you try to you will override the value internally set by Rails. In your example by setting ?format=whatevervalue you override the default format (html) and your application will try to find and render a whatevervalue template instead of the html formatted template. This will obviously not work.
Fun fact: Instead of using the default Rails path format like /users/123/edit you could use query parameters instead like this: /?controller=users&id=123&method=edit&format&html.
My suggestion is: Do not try to fight Rails conventions. Whenever you try to work around basic Rails conventions it will hurt you later on because it makes updates more difficult, common gems might break, unexpected side-effects will happen. Just use another name for that parameter.

What is the media type of an OpenAPI schema?

Whenever searching for this I find resources on how to specify the media type of a resource that the schema defines, but I can't see an answer on what the actual media type of the schema itself is.
Given the way HTTP works, it makes sense to me that if I request the right content type with the Accept header, my server can respond appropriately.
Thus, if I request /products with Accept: application/json I would get products in JSON format, but if I requested openapi-whatever I would get the OpenAPI schema.
I think I can probably use either application/openapi+json or application/openapi+yaml, but I can't see anything about it in the actual specification.
I'm not sure whether or not I actually want to use the Accept header for this difference, but I certainly want to respond with the correct Content-Type header in any case.
The OpenAPI Initiative's Technical Steering Committee (TSC) approved the following media types:
application/vnd.oai.openapi (YAML variant)
application/vnd.oai.openapi+json (JSON only variant)
with an optional version parameter:
application/vnd.oai.openapi;version=2.0
However, these media types are not yet registered with IANA.
This seems to be newer (Sept. 2021):
application/openapi+yaml
application/openapi+json
https://www.ietf.org/archive/id/draft-polli-rest-api-mediatypes-00.html

Is a url query parameter valid if it has no value?

Is a url like http://example.com/foo?bar valid?
I'm looking for a link to something official that says one way or the other. A simple yes/no answer or anecdotal evidence won't cut it.
Valid to the URI RFC
Likely acceptable to your server-side framework/code
The URI RFC doesn't mandate a format for the query string. Although it is recognized that the query string will often carry name-value pairs, it is not required to (e.g. it will often contain another URI).
3.4. Query
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). ...
... However, as query components
are often used to carry identifying information in the form of
"key=value" pairs and one frequently used value is a reference to
another URI, ...
HTML establishes that a form submitted via HTTP GET should encode the form values as name-value pairs in the form "?key1=value1&key2=value2..." (properly encoded). Parsing of the query string is up to the server-side code (e.g. Java servlet engine).
You don't identify what server-side framework you use, if any, but it is possible that your server-side framework may assume the query string will always be in name-value pairs and it may choke on a query string that is not in that format (e.g. ?bar). If its your own custom code parsing the query string, you simply have to ensure you handle that query string format. If its a framework, you'll need to consult your documentation or simply test it to see how it is handled.
They're perfectly valid. You could consider them to be the equivalent of the big muscled guy standing silently behind the mob messenger. The guy doesn't have a name and doesn't speak, but his mere presence conveys information.
"The "http" scheme is used to locate network resources via the HTTP protocol. This section defines the scheme-specific syntax and semantics for http URLs." http://www.w3.org/Protocols/rfc2616/rfc2616.html
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
So yes, anything is valid after a question mark. Your server may interpret differently, but anecdotally, you can see some languages treat that as a boolean value which is true if listed.
Yes, it is valid.
If one simply want to check if the parameter exists or not, this is one way to do so.
URI Spec
The only relevant part of the URI spec is to know everything between the first ? and the first # fits the spec's definition of a query. It can include any characters such as [:/.?]. This means that a query string such as ?bar, or ?ten+green+apples is valid.
Find the RFC 3986 here
HTML Spec
isindex is not meaningfully HTML5.
It's provided deprecated for use as the first element in a form only, and submits without a name.
If the entry's name is "isindex", its type is "text", and this is the first entry in the form data set, then append the value to result and skip the rest of the substeps for this entry, moving on to the next entry, if any, or the next step in the overall algorithm otherwise.
The isindex flag is for legacy use only. Forms in conforming HTML documents will not generate payloads that need to be decoded with this flag set.
The last time isindex was supported was HTML3. It's use in HTML5 is to provide easier backwards compatibility.
Support in libraries
Support in libraries for this format of URI varies however some libraries do provide legacy support to ease use of isindex.
Perl URI.pm (special support)
Some libraries like Perl's URI provide methods of parsing these kind of structures
$uri->query_keywords
$uri->query_keywords( $keywords, ... )
$uri->query_keywords( \#keywords )
Sets and returns query components that use the keywords separated by "+" format.
Node.js url (no special support)
As another far more frequent example, node.js takes the normal route and eases parsing as either
A string
or, an object of keys and values (using parseQueryString)
Most other URI-parsing APIs following something similar to this.
PHP parse_url, follows as similar implementation but only returns the string for the query. Parsing into an object of k=>v requires parse_string()
It is valid: see Wikipedia, RFC 1738 (3.3. HTTP), RFC 3986 (3. Syntax Components).
isindex deprecated magic name from HTML5
This deprecated feature allows a form submission to generate such an URL, providing further evidence that it is valid for HTML. E.g.:
<form action="#isindex" class="border" id="isindex" method="get">
<input type="text" name="isindex" value="bar"/>
<button type="submit">Submit</button>
</form>
generates an URL of type:
?bar
Standard: https://www.w3.org/TR/html5/forms.html#naming-form-controls:-the-name-attribute
isindex is however deprecated as mentioned at: https://stackoverflow.com/a/41689431/895245
As all other answers described, it's perfectly valid for checking, specially for boolean kind stuff
Here is a simple function to get the query string by name:
function getParameterByName(name, url) {
if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
and now you want to check if the query string you are looking for exists or not, you may do a simple thing like:
var exampleQueryString = (getParameterByName('exampleQueryString') != null);
the exampleQueryString will be false if the function can't find the query string, otherwise will be true.
The correct resource to look for this is RFC6570. Please refer to section 3.2.9 where in examples empty parameter is presented as below.
Example Template Expansion
{&x,y,empty} &x=1024&y=768&empty=

mvc.net DateTime with Time part in URI

I have a set of actions that are returning time-series data with-in ranges specifiable to the minute.
They work fine with querystrings,
i.e.
/mycontroller/myaction?from=20091201 10:31&to=20091202 10:34
with or without URL encoded colons, but I thought it would be nice to have a pretty URL
/mycontroller/myaction/from-20091201 10:31/to-20091202 10:34
but this now strikes fear in the hear of IIS as it doesn't like colons in the URI so I get 'Bad Request' responses.
My question then, is what's a recommended/standard course of action to ensure I can keep the time in there?
Do I need to write a custom ModelBinder to parse my own datetime format? Should the actions just take strings for from and to and parse with a custom format eg "YYYYMMDD-HHmm". Can I specify a custom format somewhere? If so where? Or should I just give this up as folly and stick with querystring parameters?
Oh, and I see a lot of people go on about RESTful URLs; from what I've read there's nothing that says query strings aren't RESTful - it's more about appropriate use of existing HTTP action types.
You're right REST doesn't mean if it's its not in a folder structure its not REST.
The path structure is there to describe the resource. Querystrings can still be used to describe a filtered subset of such a resource. A date range fully qualifies as a filter criteria and should thus be perfectly RESTful being passed in as a querystring.

Resources