I am fetching html content directly from my blog as:
response = Net::HTTP.get_response(uri)
respond_to do |format|
format.html { render :text => response.body }
end
Although at the blog engine (WordPress) I am adding header Access-Control-Allow-Origin: * how ever I noticed that its not passed within the response.
However, if I use postman to get the page or view the page into browser directly, I can see that the header is there.
EDIT
I can see other headers passed, ex:
cache-control: no-cache, must-revalidate, max-age=0
content-type: text/html; charset=UTF-8
date: Tue, 24 Jul 2018 06:37:57 GMT
expires: Wed, 11 Jan 1984 05:00:00 GMT
Any idea?
response.body will return you body part not header part. you can convert response to hash and check header like below:
> url = "https://stackoverflow.com/questions/51492025/does-ruby-strip-headers-from-response"
> uri = URI.parse(url)
> response = Net::HTTP.get_response(uri)
#=> #<Net::HTTPOK 200 OK readbody=true>
> response.to_hash
#=> {"cache-control"=>["private"], "content-type"=>["text/html; charset=utf-8"], "last-modified"=>["Tue, 24 Jul 2018 07:04:00 GMT"], "x-frame-options"=>["SAMEORIGIN"], "x-request-guid"=>["22a4b6b6-3039-46e2-b4de-c8af7cad6659"], "strict-transport-security"=>["max-age=15552000"], "content-security-policy"=>["upgrade-insecure-requests"], "accept-ranges"=>["bytes", "bytes"], "age"=>["0", "0"], "content-length"=>["31575"], "date"=>["Tue, 24 Jul 2018 07:04:46 GMT"], "via"=>["1.1 varnish"], "connection"=>["keep-alive"], "x-served-by"=>["cache-bom18221-BOM"], "x-cache"=>["MISS"], "x-cache-hits"=>["0"], "x-timer"=>["S1532415886.990199,VS0,VE280"], "vary"=>["Accept-Encoding,Fastly-SSL"], "x-dns-prefetch-control"=>["off"], "set-cookie"=>["prov=a7dfe911-76a1-f1c1-093b-3fc8fe79af65; domain=.stackoverflow.com; expires=Fri, 01-Jan-2055 00:00:00 GMT; path=/; HttpOnly"]}
You can access specific header as below by passing header name:
> response['Cache-Control']
#=> "private"
for more details read: https://ruby-doc.org/stdlib-2.5.1/libdoc/net/http/rdoc/Net/HTTP.html
If you want to pass through headers that are being served from the host that you're fetching from, you first need to stash the response from your blog in a different variable name. Let's call it blog_response (this is because response is a preexisting special method name in a rails controller instance.).
blog_response = Net::HTTP.get_response(uri)
Then you need to grab the header you care about from the blog_response like this:
header_name, header_value = blog_response.each_header.find do |name, value|
name =~ /pattern-matching-a-header-name-i-care-about/i #case insensitive regex matching recommended for http headers
end
Then you need to set them in your controller before you render the response, e.g.:
response.headers[header_name] = header_value
respond_to do |format|
format.html { render :text => blog_response.body }
end
This example is obviously only for one header, but you can copy multiple headers by just iterating through, matching and setting them in your response like so:
blog_response.each_header.select do |name, value|
if name =~ /pattern-matching-header-names-i-care-about|some-other-pattern-i-care-about/i #case insensitive regex matching recommended for http headers
response.headers[name] = value
end
end
If you want to pass all headers through just do this:
blog_response.each_header do |name, value|
response.headers[name] = value
end
respond_to do |format|
format.html { render :text => blog_response.body }
end
Net::HTTPResponse (that is your response) mixes in Net::HTTPHeader. Thus, you can get an individual header as response['Access-Control-Allow-Origin'], iterate over them with response.each_header, or even get them all as a hash using response.to_hash.
Related
I've read other answers on this topic, such as:
Parsing HTTParty response
HTTParty parsing JSON in Rails
However, I still can't figure out how to parse a response I'm receiving.
response.parsed_response = HTTParty.get(url, query: params) returns:
=> #<HTTParty::Response:0x89435d0 parsed_response="http://foo.com", #response=#<Net::HTTPOK 200 OK readbody=true>, #headers={"cache-control"=>["no-cache", "no-store"], "date"=>["Tue, 21 Feb 2017 23:10:47 GMT"], "expires"=>["Thu, 01 Jan 1970 00:00:00 GMT"], "p3p"=>["CP=\"ALL IND DSP COR CUR ADM TAIo PSDo OUR COM INT NAV PUR STA UNI\""], "pragma"=>["no-cache"], "server"=>["Apache-Coyote/1.1"], "set-cookie"=>["bar.Agent.p=c1921b97d1f8a0918621c48bd32ded2b; Domain=.bar.com; Expires=Fri, 19-Feb-2027 23:10:47 GMT; Path=/"], "content-length"=>["366"], "connection"=>["Close"]}>
I need the URL that appears after parsed_response. The other answers seemed to break down hashes that appear after parsed_response, but I'm just looking for the url that appears after parsed_response (and it only appears there in the response).
I tried:
puts response which returns the entire response above.
puts response.parsed_response which returns:
http://foo.com
=> nil
This usually works for me
response = HTTParty.get(url, options)
puts response.body
i was under the assumption that create_session if the endpoint.ex was configured to use the cookie store, would set the SET-COOKIE response header
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
plug Plug.Session,
log: :debug,
store: :cookie,
key: "some_key",
signing_salt: "some_salt"
this is my authentication controller ( just a part of it)
def callback(%{ assigns: %{ ueberauth_auth: auth } } = conn, params) do
params = build_params(auth)
user = find_or_create_user params
conn = put_session(conn, :current_user, user)
IO.inspect conn.resp_headers
IO.inspect get_session(conn, :current_user)
render conn, "index.html"
#Helpers.redirect!(conn, "/")
end
def build_params(auth) do
%{email: auth.info.email, github_token: auth.credentials.token, github_user: auth.info.nickname}
end
def find_or_create_user(params) do
case DBRepo.get_by(User, email: params.email) do
nil ->
User.changeset(%User{}, params)
|> DBRepo.insert
results ->
results
end
end
IO.inspect conn.resp_headers
returns
[{"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "vh8l2deodne1k2iloa4c3e4qdpmh857n"}, {"x-frame-options", "SAMEORIGIN"}, {"x-xss-protection", "1; mode=block"}, {"x-content-type-options", "nosniff"}]
IO.inspect get_session(conn, :current_user)
returns the user as expected
You don't see the session cookie in resp_headers because Plug.Session sets that cookie just before the response is actually sent, using Plug.Conn.register_before_send. If you make a request using any HTTP client (browser, curl, etc), you'll see the Set-Cookie header.
defmodule MyApp.PageController do
use MyApp.Web, :controller
def index(conn, _params) do
conn
|> put_session(:foo, :bar)
|> text("")
end
end
$ curl -I localhost:4000
HTTP/1.1 200 OK
server: Cowboy
date: Mon, 20 Feb 2017 08:57:36 GMT
content-length: 0
set-cookie: _my_app_key=SFMyNTY.g3QAAAABbQAAAANmb29kAANiYXI.F0G6lsgPxsYjq97tonLy1gRkOBUVcfwqKZdozgGRG-c; path=/; HttpOnly
content-type: text/plain; charset=utf-8
cache-control: max-age=0, private, must-revalidate
x-request-id: uoplksup9ndakf5sdr5shpjsjhvu849v
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
I have Server working with active models (not active record), and client side with activeresource .
For example server side controller have such code
...
def update
if #supplier.update_attributes(params[:supplier])
respond_to do |format|
format.any { head :ok }
end
else
respond_with(#supplier.errors,:status => :unprocessable_entity)
end
end
...
Active model has next code
...
class Subscriber < BaseModel
include ActiveModel::Validations
include ActiveModel::Serialization
include ActiveModel::Callbacks
extend ActiveModel::Naming
validates :email, :format => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
def update_attributes(attributes={})
self.attributes.merge!(attributes)
self.save
end
def save
if self.valid?
super
else
false
end
end
...
I type next code from rails console of client application
s = Supplier.find(13)
s.email = ''
s.save
return false because of invalid email
this is errors object from server before sending with respond_with
#<ActiveModel::Errors:0x000001012e9d78 #base=#<Supplier:0x000001032bf8c8 #attributes={:id=>13, :company_name=>.......}, #validation_context=nil, #errors=#<ActiveModel::Errors:0x000001012e9d78 ...>>, #messages={:email=>["is invalid"]}>
and client has next values of s
.... #remote_errors=#<ActiveResource::ResourceInvalid: Failed. Response code = 422. Response message = .>, #validation_context=nil, #errors=#<ActiveResource::Errors:0x000001034991d0 #base=#<Supplier:0x000001034af048 ...>, #messages={}>>
Why I can't take errors in client side (messages is empty hash)?
UPD: client get {} in body of response
"--- !ruby/object:Net::HTTPClientError \nbody: \"{}\"\nbody_exist: true\ncode: \"422\"\nheader: \n content-type: \n - application/json; charset=utf-8\n x-ua-compatible: \n - IE=Edge\n cache-control: \n - no-cache\n x-runtime: \n - \"0.128106\"\n content-length: \n - \"2\"\n server: \n - WEBrick/1.3.1 (Ruby/1.9.2/2011-02-18)\n date: \n - Thu, 29 Dec 2011 10:57:50 GMT\n connection: \n - close\n set-cookie: \n - _api_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRkkiJTM5OWQwNTZhMzI1MzcxYzdjZGI2NzNkZWZjNWE0OTQwBjsAVA%3D%3D--b9a0f65001dd994f269800f1c56259523ab669b1; path=/; HttpOnly\nhttp_version: \"1.1\"\nmessage: \"\"\nread: true\nsocket: \n"
UPD2 if controller action looks next
#supplier.update_attributes(params[:supplier])
respond_with(#supplier)
than response looks next
"--- !ruby/object:Net::HTTPClientError \nbody: \"{\\\"email\\\":[\\\"is invalid\\\"]}\"\nbody_exist: true\ncode: \"422\"\nheader: \n...
so I think problem is that client and server use different logic for serialization
class Errors < ActiveModel::Errors
# Grabs errors from a json response.
def from_json(json, save_cache = false)
array = Array.wrap(ActiveSupport::JSON.decode(json)['errors']) rescue []
from_array array, save_cache
end
client expects server uses errors key for errors hash ({:errors=>{:email=>["is invalid"]}}) but server doesn't!!(just {:email=>["is invalid"]})
What I miss ???
So I found problem myself.
This is a bug of rails 3.1 controller responder
It fixed in rails 3.2.0.rc1
pull request
https://github.com/rails/rails/pull/3272/files
https://github.com/rails/rails/pull/3046/files
I use render_component, https://github.com/vhochstein/render_component in Rails 3.
When my controller returns 304 result, the render_component fails with this error:
undefined method `redirect_url' for []:Array
on this line:
response = component_response(options, true)[2]
** if response.redirect_url **
redirect_to response.redirect_url
else
render :text => response.body, :status => response.status
end
The reason is because controller.dispatch(action, request) (result of component_response) returns an array with:
[304, {"ETag"=>"....", "Cache-Control"=>"max-age=0, private, must-revalidate"}, []]
instead of the 200 result which looks like:
[200, {"Content-Type"=>"text/html; charset=utf-8", "ETag"=>"\"...\"", "Cache-Control"=>"max-age=0, private, must-revalidate }, #<ActionDispatch::Response:0x007f8eee1918b8 #writer=...
Tried to add request_env["Cache-Control"] = "no-store, no-cache, must-revalidate" as a header to the ActionDispatch::Request but no luck. I checked the header is received by the controller.
I have a method in my controller which uses send_data like this:
def show
expires_in 10.hours, :public => true
send_data my_image_generator, :filename => "image.gif", :type => "image/gif"
end
Using expires_in results in headers being sent like this:
HTTP/1.1 200 OK
Connection: close
Date: Fri, 25 Jun 2010 10:41:22 GMT
ETag: "885d75258e9306c46a5dbfe3de44e581"
Content-Transfer-Encoding: binary
X-Runtime: 143
Content-Type: image/gif
Content-Disposition: inline; filename="image.gif"
Content-Length: 1277
Cache-Control: max-age=36000, public
What I would like to do is add an header like Expires: (some exact date) to keep the user agent from revalidating. But I don't see how to make send_data set that header?
I guess I could set it explicitly in the response.headers hash, but surely there must be a wrapper for that (or something)?
I came across this syntax and I like it :-)
response.headers["Expires"] = 1.year.from_now.httpdate
Apparently there seems to be no way to pass expires to send_data - instead you must set it yourself in response.headers and take care of formatting the date appropriately:
response.headers["Expires"] = CGI.rfc1123_date(Time.now + period)
Note that the max-age directive in the Cache-Control header overrides the Expires header if both are present. See RFC2616 Section 14.9.3 for more details.
The code in your question should actually work on recent Rails:
`expires_in 10.hours, :public => true`