Grails controller rendering method render vs respond - grails

I just realised that for a Grails controller there is another rendering method 'respond'.
What's the difference between respond and render method if we want to render a view in the controller.

The respond method uses content negotiation to respond with the most appropriate content type based on the requests 'ACCEPT' header.
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8, application/json
This way the consumer of your site can choose how they wish to be returned data. This may not be the best option if you want to force a specific return type. For example: You are building a REST api and only want to return json or xml, if the user asks for test.html then they may be returned your data in a format that you do not wish to support. Otherwise respond can be an easy way to support multiple return formats without programming them each separately.
Render explicitly defines the format you wish to return your data in :
(Examples from documentation)
render Book.list(params) as JSON
render Book.get(params.id) as XML
// render with status code
render(status: 503, text: 'Failed to update book ${b.id}')
More information:
Respond: http://grails.org/doc/latest/ref/Controllers/respond.html
Render:http://grails.org/doc/latest/ref/Controllers/render.html

Related

Require content-type 'application/json' for appropriate requests in Rails

I have an API that handles almost exclusively application/json in Rails. A few endpoints can handle application/x-www-form-urlencoded for file-uploads.
I want to enforce the content-types: when a client adds anything other than application/json I want to send an 406 - not acccepted error back, in my ApplicationController
before_action :require_content_type_json
def require_content_type_json
return if request.content_type == Mime::JSON.to_s
head status: 406
end
However, RFC 7231 (http) states that:
A sender that generates a message containing a payload body SHOULD
generate a Content-Type header field in that message unless the
intended media type of the enclosed representation is unknown to the
sender. If a Content-Type header field is not present, the recipient
MAY either assume a media type of "application/octet-stream"
([RFC2046], Section 4.5.1) or examine the data to determine its type.
I roughtly interpret that as "when the request comes with a body, the content-type must be provided by the client, else the server should assume "application/octet-stream".
Since I cannot handle octet-stream, I want to return 406 - not accepted too, there. In other words: when there is a body, a header setting the content-type to "application/json" should be present. Always.
So, how do I detect whether a user should have sent a content-type header along? Is it enough to simply check for !request.body.empty?: body is not empty?
Or should I assume that only GET/OPTIONS/HEAD requests have no body and all others do and should require a content-type?
Does Rails have any helpers or classes in place to deal with this?

Rails RESTful API: the proper error format when content negotiation fails

What error format must be used by RESTful API when content negotiation fails (ActionController::UnknownFormat is raised):
when a controller responds to the only one format (e. g. JSON) and the user has requested another one (e. g. XML), should the error be generated as JSON object or XML one?
when a controller responds to several formats and the user has requested neither of them, which one should be used during error generation: one of the 'known' by the controller or the one, having been requested by the user?
I think you are under no obligation to respond to any invalid request with the same format as the request was made. Imagine getting a request with payload in unknown binary format - what are you supposed to do in such a case.
ActionController::UnknownFormat should trigger a 406 Not Acceptable response, probably will in Rails 5.
You should rescue the ActionController::UnknownFormat and respond with proper HTTP code as well as set an Accept header listing all formats that your API supports (if Rails doesn't do it by default, I'm not sure)

Return JSON instead of HTML error page on heroku

I’m creating rails–powered app, which acts as JSON API, and is hosted on heroku.
Right now, if exception is raised, heroku returns me proper http response code, and customisable HTML page as response. However, since I’m not using HTML format, and even if I set Accept: application/json header that HTML response is returned – which is incorrect for me. Is it possible to customise response, and return some kind of JSON? (If not, response without body will be also fine)
You should catch exceptions in the controller, and head :not_found or something similar.
http://guides.rubyonrails.org/action_controller_overview.html#rescue_from
http://rails.rubyonrails.org/classes/ActionController/Base.html#M000466

HTTP HEAD Request and System.Web.Mvc.FileResult

I'm using BITS to make requests to a ASP.NET MVC controller method named Source that returns a FileResult. I know the type FilePathResult uses HttpResponse.TransmitFile, but I don't know if HttpResponse.TransmitFile actually writes the file to the response stream regardless of the request type.
My question is, does FileResult only include the header information on HEAD requests, or does it transmit the file regardless of the request type? Or, do I have to account for HEAD requests myself?
The result is forced to react on a request by YOUR ACTION CODE. If you do not do anything special on different request types (e.g. [HttpGet]-Attribute, HttpMethodConstraints in the Route, etc...) The file is just written to the response stream.

request.format returning */*

I'm currently developing an API for my application on RoR
As an example, I created some XML, loaded with all the info I need to create the object, let's say a Person, and using Curl I submitted it to my application
I'm able to call exactly the create action I want from the controller and the hash params of the object are being passed correctly
But now I need to apply a different behaviour if request was made or not with XML, what is bothering me is why in the controller request.format gives */*.
Any clues?
curl -v -H "Content-Type: application/xml; charset=utf-8" --data-ascii #client.xml http://foo.com:3000/clients?api_key=xxx
def create
logger.debug request.format # produces "*/*"
if request.format.xml?
# never gets here
end
end
*/* means that the user-agent accepts all formats and doesn't care what format you give it. I believe Safari does this, among others. By default, curl sends an Accept header of */*.
Here is a dump of the headers curl sends by default:
User-Agent: curl/7.18.1 (i386-apple-darwin9.6.0) libcurl/7.18.1 zlib/1.2.3
Host: example.com
Accept: */*
Content-Type:
However, in this case, it looks like you want to send back XML if the payload sent to you was XML? If that's the case, you want to check the request's Content-Type header directly. i.e., request.content_type is the method you want.
Addenda: I thought a bit more about this, and I think the best approach is to first check request.format and only if that is inconclusive check request.content_type. Essentially, the HTTP spec provides for clients being able to tell servers that "I'm giving you XML, but I want JSON back." The Accept header is how clients tell you what they want back, and if someone actually sends it, you should honor that. Only use the request's Content-Type as a hint if the client didn't specify.
*/* simply means that all MIME types are accepted.
Looking at the code for the request.format method, the MIME type is determined by the file extension, or if that's not present then by the value of the HTTP Accept header. So you either need to pass Curl an XML file saved to disk, or get Curl to set the Accept header to an XML MIME type (e.g. text/xml) when it makes the request to your API.

Resources