使用 Ruby 的 Net:HTTP 保留 HTTP 标头中的大小写

发布于 2024-08-30 05:08:19 字数 1484 浏览 4 评论 0原文

尽管 HTTP 规范规定标头不区分大小写; Paypal 及其新的自适应支付 API 要求其标头区分大小写。

使用 ActiveMerchant 的 paypal 自适应支付扩​​展 (http://github.com/lamp/paypal_adaptive_gateway) 似乎尽管标头设置为全部大写,但它们是以混合大小写形式发送的。

下面是发送 HTTP 请求的代码:(

headers = {
  "X-PAYPAL-REQUEST-DATA-FORMAT" => "XML",
  "X-PAYPAL-RESPONSE-DATA-FORMAT" => "JSON",
  "X-PAYPAL-SECURITY-USERID" => @config[:login],
  "X-PAYPAL-SECURITY-PASSWORD" => @config[:password],
  "X-PAYPAL-SECURITY-SIGNATURE" => @config[:signature],
  "X-PAYPAL-APPLICATION-ID" => @config[:appid]
}
build_url action

request = Net::HTTP::Post.new(@url.path)

request.body = @xml
headers.each_pair { |k,v| request[k] = v }
request.content_type = 'text/xml'

proxy = Net::HTTP::Proxy("127.0.0.1", "60723")

server = proxy.new(@url.host, 443)
server.use_ssl = true

server.start { |http| http.request(request) }.body

我添加了代理线路,这样我就可以看到 Charles 发生了什么 - http ://www.charlesproxy.com/

当我查看 charles 中的请求标头时,我看到的是:

X-Paypal-Application-Id ...
X-Paypal-Security-Password...
X-Paypal-Security-Signature ...
X-Paypal-Security-Userid ...
X-Paypal-Request-Data-Format XML
X-Paypal-Response-Data-Format JSON
Accept */*
Content-Type text/xml
Content-Length 522
Host svcs.sandbox.paypal.com

我通过使用curl 运行类似的请求来验证不是 Charles 进行大小写转换。在那次测试中,箱子被保存下来。

Although the HTTP spec says that headers are case insensitive; Paypal, with their new adaptive payments API require their headers to be case-sensitive.

Using the paypal adaptive payments extension for ActiveMerchant (http://github.com/lamp/paypal_adaptive_gateway) it seems that although the headers are set in all caps, they are sent in mixed case.

Here is the code that sends the HTTP request:

headers = {
  "X-PAYPAL-REQUEST-DATA-FORMAT" => "XML",
  "X-PAYPAL-RESPONSE-DATA-FORMAT" => "JSON",
  "X-PAYPAL-SECURITY-USERID" => @config[:login],
  "X-PAYPAL-SECURITY-PASSWORD" => @config[:password],
  "X-PAYPAL-SECURITY-SIGNATURE" => @config[:signature],
  "X-PAYPAL-APPLICATION-ID" => @config[:appid]
}
build_url action

request = Net::HTTP::Post.new(@url.path)

request.body = @xml
headers.each_pair { |k,v| request[k] = v }
request.content_type = 'text/xml'

proxy = Net::HTTP::Proxy("127.0.0.1", "60723")

server = proxy.new(@url.host, 443)
server.use_ssl = true

server.start { |http| http.request(request) }.body

(i added the proxy line so i could see what was going on with Charles - http://www.charlesproxy.com/)

When I look at the request headers in charles, this is what i see:

X-Paypal-Application-Id ...
X-Paypal-Security-Password...
X-Paypal-Security-Signature ...
X-Paypal-Security-Userid ...
X-Paypal-Request-Data-Format XML
X-Paypal-Response-Data-Format JSON
Accept */*
Content-Type text/xml
Content-Length 522
Host svcs.sandbox.paypal.com

I verified that it is not Charles doing the case conversion by running a similar request using curl. In that test the case was preserved.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

两个我 2024-09-06 05:08:19

RFC 确实指定标头键不区分大小写,很遗憾,您似乎遇到了 PayPal API 的烦人要求。

Net::HTTP 正在改变这种情况,尽管我很惊讶它们并没有全部变成小写:

# File net/http.rb, line 1160
    def []=(key, val)
      unless val
        @header.delete key.downcase
        return val
      end
      @header[key.downcase] = [val]
    end

“设置与不区分大小写的密钥相对应的标头字段。”

由于上面是一个简单的类,因此可以对其进行猴子修补。我会进一步思考更好的解决方案。

The RFC does specify that header keys are case-insensitive, so unfortunately you seem to have hit an annoying requirement with the PayPal API.

Net::HTTP is what is changing the case, although I'm surprised they're not all getting downcased:

# File net/http.rb, line 1160
    def []=(key, val)
      unless val
        @header.delete key.downcase
        return val
      end
      @header[key.downcase] = [val]
    end

"Sets the header field corresponding to the case-insensitive key."

As the above is a simple class it could be monkey-patched. I will think further for a nicer solution.

尤怨 2024-09-06 05:08:19

使用以下代码强制区分大小写标头。

class CaseSensitivePost < Net::HTTP::Post
  def initialize_http_header(headers)
    @header = {}
    headers.each{|k,v| @header[k.to_s] = [v] }
  end

  def [](name)
    @header[name.to_s]
  end

  def []=(name, val)
    if val
      @header[name.to_s] = [val]
    else
      @header.delete(name.to_s)
    end
  end

  def capitalize(name)
    name
  end
end

使用示例:

post = CaseSensitivePost.new(url, {myCasedHeader: '1'})
post.body = body
http = Net::HTTP.new(host, port)
http.request(post)

Use following code to force case sensitive headers.

class CaseSensitivePost < Net::HTTP::Post
  def initialize_http_header(headers)
    @header = {}
    headers.each{|k,v| @header[k.to_s] = [v] }
  end

  def [](name)
    @header[name.to_s]
  end

  def []=(name, val)
    if val
      @header[name.to_s] = [val]
    else
      @header.delete(name.to_s)
    end
  end

  def capitalize(name)
    name
  end
end

Usage example:

post = CaseSensitivePost.new(url, {myCasedHeader: '1'})
post.body = body
http = Net::HTTP.new(host, port)
http.request(post)
给妤﹃绝世温柔 2024-09-06 05:08:19

如果您仍在寻找有效的答案。新版本通过使用 to_s 对底层 capitalize 方法进行了一些更改。修复方法是让 to_sto_str 返回 self,以便返回的对象是 ImmutableKey 的实例基字符串类的。

class ImmutableKey < String 
         def capitalize 
               self 
         end 
         
         def to_s
          self 
         end 
         
         alias_method :to_str, :to_s
 end

参考:https://jatindhankhar.in/blog/custom -http-header-and-ruby-standard-library/

If you are still looking for an answer that works. Newer versions have introduced some changes to underlying capitalize method by using to_s. Fix is to make the to_s and to_str return the self so that the returned object is an instance of ImmutableKey instead of the base string class.

class ImmutableKey < String 
         def capitalize 
               self 
         end 
         
         def to_s
          self 
         end 
         
         alias_method :to_str, :to_s
 end

Ref: https://jatindhankhar.in/blog/custom-http-header-and-ruby-standard-library/

妖妓 2024-09-06 05:08:19

我遇到了 @kaplan-ilya 提出的代码的几个问题,因为 Net::HTTP 库尝试检测帖子内容类型,并且我最终得到了 2 个内容类型和其他在不同情况下重复的字段。

因此,下面的代码应该确保一旦选择了特定情况,它将坚持相同的情况。

  class Post < Net::HTTP::Post
    def initialize_http_header(headers)
      @header = {}
      headers.each { |k, v| @header[k.to_s] = [v] }
    end

    def [](name)
      _k, val = header_insensitive_match name
      val
    end

    def []=(name, val)
      key, _val = header_insensitive_match name
      key = name if key.nil?
      if val
        @header[key] = [val]
      else
        @header.delete(key)
      end
    end

    def capitalize(name)
      name
    end

    def header_insensitive_match(name)
      @header.find { |key, _value| key.match Regexp.new(name.to_s, Regexp::IGNORECASE) }
    end
  end

I got several issues with the code proposed by @kaplan-ilya because the Net::HTTP library tries to detect the post content-type, and the I ended up with 2 content-type and other fields repeated with different cases.

So the code below should ensure than once a particular case has been choosen, it will stick to the same.

  class Post < Net::HTTP::Post
    def initialize_http_header(headers)
      @header = {}
      headers.each { |k, v| @header[k.to_s] = [v] }
    end

    def [](name)
      _k, val = header_insensitive_match name
      val
    end

    def []=(name, val)
      key, _val = header_insensitive_match name
      key = name if key.nil?
      if val
        @header[key] = [val]
      else
        @header.delete(key)
      end
    end

    def capitalize(name)
      name
    end

    def header_insensitive_match(name)
      @header.find { |key, _value| key.match Regexp.new(name.to_s, Regexp::IGNORECASE) }
    end
  end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文