is there a way to modify or send custom headers on grape? - ruby

I'm using Goliath and Grape. On my goliath server it calls the grape api like so:
when '/posts' then FrameworksAPI::API.call(env)
On my grape api class, my method is as simple as this:
get '/:id' do
Post.find(params[:id])
end
I'd like to modify the headers - specifically the 'Content-Length' but unsure how to.
Also i'd like to ask an additional question. How do i create callback/filters specifically before the method GET returns the result i'd like to modify the result.

Grape has a header helper for a few versions now.
header 'Content-Length`, 42.to_s
For your second question on modifying the body, try using after do ... at API level.

The return from your FrameworksAPI::API.call(env) method will be a triplet [status_code, headers, body]. So, instead of just returning that from your case you'd do something like:
when '/posts' then
status, headers, body = FrameworksAPI::API.call(env)
headers['whatever'] = blah
[status, headers, body]
You can also change the body, just be careful as the body maybe an array.
There is also a Content-Length middleware that is provided by Goliath. Content-Length is loaded by default although if you set a custom Content-Length it will take precedence. Just be carefull that other middlewares like the formattings don't change the body after you set your content-length.

Related

How to send a post from twillio webhook using the body instead the params in the request?

There is a way to config the Twilio webhooks in the conversation product to send a post request to an endpoint and in the body send the information instead in the params?
You would pass a payload of the JSON you want to send in your post body and then pass in a header called x-www-form-urlencoded which tells Twilio that you want the parameters to be sent in the body as form data. I'm not sure if it's limited to only a few parameters or not but I know that it works with \"To\" and \"From\" (as they need to be URL encoded). It would definitely work with MessageSid.
You could also use the \"Bulk\" post body format, which is just JSON. This would allow you to pass more parameters since it's just JSON. (You don't need to url encode them if you do this, so no need to have x-www-form-urlencoded header.)
{
\"To\": \"+15551235555\",
\"From\": \"+15551234567\",
\"Body\": \"A text message\",
...: ...
}
You should be able to send the information you want, along with the headers, from your endpoint and have it pass through Twilio.
Looks like this:
curl -X POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages -d 'From=%2B15551234567&To=%2B15551235555&Body=Test' -u '{AccountSid}:{AuthToken}'
You can pass any JSON you want as a body using this option but make sure you've set your \"Content-Type\" header to \"application/x-www-form-urlencoded\". This is pretty straightforward and makes it easy to pass in whatever parameters you want.
This isn't limited to text messages! This is exactly how I push data back into a Conversation or Action resource too so it'll work for things like card pushes too! You can use this to programmatically create a response that Twilio will process and then act on in your Conversation or Action instance.
And yeah … if you're going to support a webhook that takes form data then I would suggest adding some basic security checks since anyone could just post random stuff as form data if they wanted and get access to your endpoint. I'd recommend checking the Request Method as well to make sure it's POST.
If you're worried about someone passing in a bad value then you can just check the request body against some regex. I'd recommend checking the Twilio-To and Twilio-From params as well. You could also use the request header too, which is passed along with all webhooks:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: xxx');

Supporting multiple JSON content types in Grape

I’ve been using Grape to write an Evernote-like API, and have been using Collection+JSON (MIME type "application/vnd.collection+json"). I now want to also support JSON API (mime type "application/vnd.api+json").
(The reason I want to do this is so it will be easier to write an Ember client, since Ember Data has support for JSON API built in. It might make more sense to write client-side JavaScript to solve my problem, but I’m stronger in Ruby than JavaScript.)
My goal is to have the server support either format, and choose which one based on the Accept header. Is this a reasonable thing to do, or does it not make sense to have multiple JSON types? It appears that Grape doesn’t support this. If I just have the wrong idea, then the code below is probably not relevant to that answer.
Here are some relevant pieces of my server code:
class NotesServer < Grape::API
content_type :json, ‘application/json’
content_type :json_api, 'application/vnd.api+json'
formatter :json_api, lambda { |object, env| object.to_json_api }
content_type :collection_json, 'application/vnd.collection+json'
formatter :collection_json, lambda { |object, env| object.to_collection_json }
resource :notes do
desc 'Get a note.'
params do
requires :id, type: Integer, desc: 'Note ID.'
end
route_param :id do
get do
NoteRepresenter.new Note.find(params[:id])
end
end
end
…where NoteRepresenter defines both to_json_api and to_collection_json.
The idea is to use the same Representer in either case, but to call the appropriate method depending on the Accept header.
When I run curl against this with any Accept header, the response has Content-Type: application/json, and “to_json” is called on the representer. If I leave out the content_type :json, ‘application/json’ line, I get a 406 Not Acceptable response every time.
I figured out that in the Grape library, in lib/grape/middleware/formatter.rb, the "format" of a request is considered JSON for any of these MIME types, since the vnd.collection+ or vnd.api+ parts are parsed out of the header. So my question is, is this a matter of Grape not supporting what I’m trying to do, or am I trying to do something that makes no sense?
I found a reasonable way to do this that I’mr reasonably happy with, but I don’t think it totally aligns with how Grape expects you to do things.
I created a custom formatter called JsonFormatter, so my server code looks like this:
class NotesServer < Grape::API
formatter :json, JsonFormatter
# …
end
And the formatter itself:
module JsonFormatter
def self.call(object, env)
case env['HTTP_ACCEPT']
when 'application/vnd.api+json'
object.to_json_api
when 'application/vnd.collection+json'
object.to_collection_json
else
object.to_json
end
end
end
So now either to_json_api or to_collection_api (or to_json) will be called on the presenter object, depending on the Accept header. One issue I haven’t solved yet is that the Content-Type is set to application/json in all cases.

Cant set custom header params with rspec and Grape

I use Grape api and i need to write a test with custom header
my code:
it "should accept message" do
post "/api/v1/my/route", post_data, secret: "ASDFGHJKL"
last_response.status.should == 201
end
but the route gets no headers at all,
i also tried headers['secret'] = "ASDFGHJKL"
and also request.env['secret']
nothing works.
how can i pass headers in rspec to grape route?
Did you try header 'secret', 'ASDFGHJKL'?
More on the rack-test docs
Try using #request variable, e.g. #request.env['secret'] = 'ASDF..'. Hope this helps.
Also, take a look at this - Sending custom headers through RSpec

How do use JSON body in REST requests?

I am developing an API using Codeigniter and Phils RESTserver. I know how to send the request body in normal Form format but how can I send it as a JSON object instead?
I do this now:
lastname=bond
I want to do this instead:
{"lastname" : "bond"}
I tried to just replace the Content type header from:
application/x-www-form-urlencoded
In to this:
application/json
This did not do anything. Codeigniter says the POST array is empty.
If I understood correctly you want to create a request that contains a JSON node inside the request body. Assuming this, I think it's not possible to create such a request using simple HTML form tags as your browser always will try to pack your input vars in a querystring like format.
You will need JavaScript to achive this (I think all popular libs like Scriptacoulous or JQuery comes with helper methods for this).

Post request with body_stream and parameters

I'm building some kind of proxy.
When I call some url in a rack application, I forward that request to an other url.
The request I forward is a POST with a file and some parameters.
I want to add more parameters.
But the file can be quite big. So I send it with Net::HTTP#body_stream instead of Net::HTTP#body.
I get my request as a Rack::Request object and I create my Net::HTTP object with that.
req = Net::HTTP::Post.new(request.path_info)
req.body_stream = request.body
req.content_type = request.content_type
req.content_length = request.content_length
http = Net::HTTP.new(#host, #port)
res = http.request(req)
I've tried several ways to add the proxy's parameters. But it seems nothing in Net::HTTP allows to add parameters to a body_stream request, only to a body one.
Is there a simpler way to proxy a rack request like that ? Or a clean way to add my parameters to my request ?
Well.. as i see it, this is a normal behaviour. I'll explain why. If you only have access to a Rack::Request,(i guess that) your middleware does not parse the response (you do not include something like ActionController::ParamsParser), so you don't have access to a hash of parameters, but to a StringIo. This StringIO corresponds to a stream like:
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="param1"
value1
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
What you are trying to do with the Net::HTTP class is to: (1). parse the request into a hash of parameters; (2). merge the parameters hash with your own parameters; (3). recreate the request. The problem is that Net::HTTP library can't do (1), since it is a client library, not a server one.
Therefore, you can not escape parsing some how your request before adding the new parameters.
Possible solutions:
Insert ActionController::ParamsParser before your middleware. After that, you may use the excellent rest-client lib to do something like:
RestClient.post ('http://your_server' + request.path_info), :params => params.merge(your_params)
You can attempt to make a wrapper on the StringIO object, and add, at the end of stream,your own parameters. However, this is not trivial nor advisable.
Might be one year too late, but I had the same issue verifying Paypal IPNs. I wanted to forward back the IPN request to Paypal for verification but needed to add :cmd => '_notify-validate'.
Instead of modifying the body stream, or body, I appended it as part of the URL path, like so:
reply_request = Net::HTTP::Post.new(url.path + '?cmd=_notify-validate')
It seems a bit of a hack, but I think it's worth it if you aren't going to use it for anything else.

Resources