How to get Rails to respond with json based on headers - ruby-on-rails

I'm building an API with Rails 4 and in my controller I'm using respond_to to differentiate between html and json request which all works fine. While testing my API in Postman, I've added the following header: Content-Type: application/json but in my request I still need to add .json at the end of it like so: https://myapi.com/api/users.json otherwise Rails will respond with html even though the aforementioned header is present.
My question is this: is there a way that Rails can recognise the Content-Type: application/json header and respond with json without me using the .json part in the url like so: https://mypai.com/api/users?

You also need to set the Accept header to Accept: application/json
If you're curious as to why, and the difference between the two headers, the answers to this post explain it best: https://webmasters.stackexchange.com/questions/31212/difference-between-the-accept-and-content-type-http-headers

If you are using any link helper inside a view, simple add the format information, like posts_path(format: :json).

Related

zf2 decodeGzip, why is it protected?

I'm getting back what appears to be gzipped data in a call to a zf2 api. The content-type is gzip, and the content body looks encoded. So I tried this:
$decoded = $response->decodeGzip($response->getContent());
and I got back this error:
Call to protected method Zend\Http\Response::decodeGzip()
Why is it protected? It seems like decoding gzipped data would be a handy thing to be able to do.
You should use $response->getBody(). The getBody() method checks the Content-Encoding header and if this is gzip, it will extract the body from the Gzipped content.
You can check this method in the online repository: Zend\Http\Response

How to POST JSON in body for functional tests in Symfony 1.4

I'm writing some functional tests for a POST API endpoint. I've reviewed the documentation and can't find a way to add content to the POST body. The post method for sfBrowser:
post('some url',array('x'=>'y'))
Only creates POST parameters (in this case x=y). Is there anyway of adding content to the post body using sfBrowser?
From what I have found here, here and here, the POST format takes parameter:value format, so you can send your JSON with some code like:
post('some url', array('json_data' => json_encode($toJson))
and then decode in your action with
$jsonObj = json_decode($request->getParameter('json_data'));
but you need to associate your JSON data with a parameter name in your POST to retrieve it on the server side.
As a side note, after looking at the Symfony code, the parameters are given straight to $_POST except for CSRF, which is tweaked.

Cross-domain ajax post with JSON data going to Rails controller as HTML

I'm making a cross-domain POST request and have set up headers in Rails 3.2 to accept the request appropriately - both the OPTIONS and POST request are received by the controller correctly.
The problem I'm finding is that Rails controller is receiving the JSON data as HTML. Why is this? How can I force the controller to treat it as JSON? (doing this from the client-side would be preferable)
If this is unclear let me know which messages/code I ought to post for better assistance. Thanks
You could try adding the JSON extension to the URL that you're making a request to, i.e. https://your_domain.com/your_url.json, which should force the JSON format.

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.

Load testing multipart form

I'm trying to load-test a Rails application using JMeter. A critical part of the application involves a form that includes both text inputs and file uploads. It works fine in a browser, but when I try to post that page in JMeter, Rails is saving all of the parts of the multipart form as temp files, which causes things to break when it's looking for a string and gets a tempfile instead.
It appears that the difference is that, from a browser, the piece of the multipart request that contains a text input looks like this:
-----------------------------7d93b4186074c
Content-Disposition: form-data; name="field_name"
test
-----------------------------7d93b4186074c
while from JMeter it looks like this:
-----------------------------7d159c1302d0y0
Content-Disposition: form-data; name="field_name"
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
test
-----------------------------7d159c1302d0y0
So apparently Rails sees the former and interprets it as a plain text value and treats it as a string, but sees the latter and saves it to a temp file.
I have not been able to find a setting to convince JMeter not to send the additional headers in the multipart form for non-file fields.
Is there a way to convince Rails to ignore those headers and treat the text/plain text as strings instead of text files? Or a quick way to put a filter in front of my controller that will strip the extra headers?
Alternately, is there a better tool to load-test a Rails application that includes file upload?
Turns out these days you can just tick "use browser compatible headers" in JMeter. Could've saved myself a hell of a lot of time there :-)
So, I have customized JMeter's multipart request posting part in the source code to put out the request that rails understand. The change is easy as shown below but to create compiling Java/JMeter environment took time. :(
Anyways, now I can successfully upload a file by multipart post via JMeter.
in src/protocol/http/org/apache/jmeter/protocol/http/sampler/PostWriter.java
writeStartFileMultipart()
//writeln(out, "Content-Transfer-Encoding: binary"); // $NON-NLS-1$
writeFormMultipart()
/*****
writeln(out, "Content-Type: text/plain; charset=" + charSet); // $NON-NLS-1$
writeln(out, "Content-Transfer-Encoding: 8bit"); // $NON-NLS-1$
*****/
P.S.
A tip tip to create the build environment for 2.4 is
to comment out the 3rd party libraries check in build.xml file.
copy lib/xstream-1.3.1.jar from binary archive into lib/ directory
There may be a better way, but I ended up adding a quick filter to turn the text/plain tempfiles into strings within the parameter hash:
def change_text_files_to_strings
params.each_pair do |key, value|
params[key] = value.read if (value.class.to_s=='Tempfile' && value.content_type.start_with?('text/plain') )
end
end
By the way, it turns out that jmeter is correct here, and rails incorrect: according to RFC 2388, each item in a multipart request should have a content type (not just files), so Rails really shouldn't be using the presence of a content-type header to determine whether it's a file. Ah well.
I also used the solution above as ColdFusion was sending similar headers (minus the Content-Transfer-Encoding) with each piece of form data. I wonder if there's a better way.
EDIT: Anyone know if this has been fixed in Rails 3?
What kind of error do you get? Something like
NoMethodError (undefined method `rewind' for "1":String):
There is an issue with Rack that could explain your problem. See https://github.com/rack/rack/issuesearch?state=open&q=rewind#issue/116
We were also having a similar issue, In addition to the above answers we also correlate the X-CSRF-Token of HTTP Header Manager in that request and were
successfully able to upload the required media as many as times we wanted.

Resources