【问题标题】:How can I access the raw request body in ruby CGI scripts?如何访问 ruby​​ CGI 脚本中的原始请求正文?
【发布时间】:2019-02-20 12:59:24
【问题描述】:

在我作为 CGI 程序运行的 ruby​​ 脚本中,我需要访问 HTTP POST 请求的主体。请求正文包含 JSON 数据:

{"data":"a"}

我想取整个身体并用 JSON.parse 解析它来处理它。这样做的规范方法是什么? Ruby docs 没有提及请求正文。

我只在blog post 中发现了一个提示

CGI 尝试将请求正文解析为表单参数,因此一团 JSON 尴尬地最终成为唯一的参数键。

这种方法似乎有效

puts cgi.params.keys.first # prints {"data":"a"}

但一旦 data 的值是包含用于填充的 = 的 base64 编码字符串就会失败:使用此正文

{"data":"a="}

导致以下输出(末尾缺少字符):

puts cgi.params.keys.first # prints {"data":"a

解决这个问题的正确方法是什么?

【问题讨论】:

    标签: ruby cgi


    【解决方案1】:

    您可能已经知道,在对参数及其值进行 urlencoded 时,它们用 = 分隔:name=Theo&language=ruby 等等。

    这就是为什么第一个参数的名称在= 之前的字符处停止的原因。如那篇博文中所述,使用第一个密钥的方法并不可靠。

    相反,在 CGI 脚本中,您可以直接从标准输入读取请求正文,例如

    request_body = $stdin.read
    

    注意,当您实例化 CGI 对象时,它会从标准输入读取所有内容并尝试将其解析为 params 哈希。

    这意味着,如果您仍想使用 cgi 库来构建您的响应,则需要在代码的前面,创建 CGI 对象之前从标准输入读取。例如

    # minimal example that just outputs the request body
    require 'cgi'
    
    request_body = $stdin.read
    
    cgi = CGI.new
    
    cgi.out("status" => "OK", "type" => "text/plain", "connection" => "close") do
      request_body
    end
    

    【讨论】:

      【解决方案2】:

      显然,在 Ruby 中没有简单的解决方案。

      但是有两种方法可以实现。

      1. 重新定义CGI::parse(params) 方法。 CGI 模块中的这个方法负责将 POST 和 GET 参数解析为 params 哈希。您可以在代码中重新定义此方法,以便在 params 哈希中添加一个名为 RAW_DATA 的额外参数。

        def CGI::parse(query)
           params = {}
           query.split(/[&;]/).each do |pairs |
           key, value = pairs.split('=', 2).collect {
             | v | CGI::unescape(v)
            }
        
           next unless key
        
           params[key] || = []
           params[key].push(value) if value
         end
        
         #Add RAW_DATA to params
         params[:RAW_DATA] = query
        
         params.default = [].freeze
         params
        end
        
      2. 在创建 CGI 实例之前使用$stdin.read()。 但这可能会阻止您使用其他 CGI 功能。

        所以你可以用 StringIO 对象临时替换 $stdin。

        require 'cgi'
        require 'stringio'
        
        raw_data  = $stdin.read()
        
        real_stdin = $stdin
        $stdin     = StringIO.new(raw_data)
        STDIN      = $stdin
        
        cgi = CGI.new
        
        #Your CGI code here
        
        #........
        
        $stdin = real_stdin
        STDIN  = $stdin
        

      【讨论】:

        猜你喜欢
        • 2015-10-19
        • 2011-04-29
        • 2015-11-20
        • 2020-07-19
        • 2018-11-27
        • 1970-01-01
        • 2010-11-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多