代码之家  ›  专栏  ›  技术社区  ›  Theo

如何访问ruby CGI脚本中的原始请求体?

  •  0
  • Theo  · 技术社区  · 6 年前

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

    {"data":"a"}
    

    我想把整个身体用JSON.parse 去处理它。做这件事的标准方法是什么?这个 Ruby docs

    我只是在一个 blog post

    CGI试图将请求体解析为表单参数,因此JSON的blob很尴尬地最终成为唯一的参数键。

    这种方法似乎有效

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

    data = 填充:使用此主体

    {"data":"a="}
    

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

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

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

    2 回复  |  直到 6 年前
        1
  •  2
  •   mikej heading_to_tahiti    6 年前

    正如您可能已经知道的,当参数和它们的值被urlencoded时,它们用一个 = : name=Theo&language=ruby 等等。

    这就是为什么第一个参数的名称在 =

    相反,在CGI脚本中,您可以直接从stdin读取请求体。

    request_body = $stdin.read
    

    注意,当您实例化 CGI 对象,它将读入stdin中的所有内容并尝试将其解析为 params 搞砸。

    cgi 用于构建响应的库,您需要在代码的前面阅读stdin, 之前 创建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
  •  1
  •   Vishnuprasad R    6 年前

    但有两种方法可以实现这一点。

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

      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. $stdin.read() 在创建CGI实例之前。 但这可能会阻止您使用其他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