【问题标题】:How to parse request body from Amazon SNS Notification in rails如何从 Rails 中的 Amazon SNS 通知解析请求正文
【发布时间】:2015-06-23 18:52:04
【问题描述】:

我正在我的 rails 3.0 应用程序中设置一个端点以接收来自 Amazon SNS 服务的推送通知。

Amazon 发布的请求具有 JSON 负载,但他们将请求的内容类型设置为“text/plain”,这导致 Rails 无法解析正文。

来自Amazon's docs 的发布请求示例:

POST / HTTP/1.1
x-amz-sns-message-type: Notification
x-amz-sns-message-id: 22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324
x-amz-sns-topic-arn: arn:aws:sns:us-west-2:123456789012:MyTopic
x-amz-sns-subscription-arn: arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96
Content-Length: 773
Content-Type: text/plain; charset=UTF-8
Host: myhost.example.com
Connection: Keep-Alive
User-Agent: Amazon Simple Notification Service Agent

{
  "Type" : "Notification",
  "MessageId" : "22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324",
  "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
  "Subject" : "My First Message",
  "Message" : "Hello world!",
  "Timestamp" : "2012-05-02T00:54:06.655Z",
  "SignatureVersion" : "1",
  "Signature" : "EXAMPLEw6JRNwm1LFQL4ICB0bnXrdB8ClRMTQFGBqwLpGbM78tJ4etTwC5zU7O3tS6tGpey3ejedNdOJ+1fkIp9F2/LmNVKb5aFlYq+9rk9ZiPph5YlLmWsDcyC5T+Sy9/umic5S0UQc2PEtgdpVBahwNOdMW4JPwk0kAJJztnc=",
  "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
  "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96"
}

所以在我的控制器中,我可以使用请求对象并读取 request.body 并自己解析出来,如下所示:

def receive_notification
  if request.content_type =~ /text\/plain/
    body = request.body.read.force_encoding("UTF-8")
    params.merge(JSON.parse(body))
    request.body.rewind
  end
  # ... go on with rest of controller stuff
end

谁有更好的方法来做到这一点?我们可以将它移回链上,这样我的控制器就不用担心请求对象了吗?编写一个在参数解析之前运行并识别请求来自 Amazon(通过 User-Agent 或那些自定义标头)并将内容类型更改为“application/json”的中间件是不是一个坏主意? (你是怎么做到的?)

【问题讨论】:

    标签: ruby-on-rails json amazon-web-services


    【解决方案1】:

    我最近遇到了这个问题。网上提到的大多数方法都对我不起作用,所以我创建了一个中间件。中间件检测消息是否来自 SNS(通过查找类型标头)并强制 content_type 为application/json

    在我的情况下,消息参数也是作为字符串传递的 JSON(来自 S3 存储桶通知的 SNS 消息)。这不能处理这种情况,但可以很容易地扩展。

    # app/middleware/sns_content_type.rb
    
    class SnsContentType
      def initialize(app, message = "Response Time")
        @app = app
      end
    
      def call(env)
        env['CONTENT_TYPE'] = 'application/json' if env['HTTP_X_AMZ_SNS_MESSAGE_TYPE'].present?
        @app.call(env)
      end
    end
    

    创建中间件后,您需要像这样安装它:

    # config/application.rb
    
    config.middleware.insert_before ActionDispatch::ParamsParser, "SnsContentType"
    

    这会在解析参数之前插入中间件,这意味着参数解析器将看到 JSON 内容类型。

    【讨论】:

    • 有没有办法在控制器(甚至每个动作)的基础上做到这一点?一方面,将更改本地化到需要的地方。并且还要将另一个中间件保留在堆栈之外。与许多不需要的请求相比,很少有请求会需要它。
    • 我试图在控制器级别执行此操作的问题是,您需要在 rails 解析请求之前放置此代码,以便让它解释 json。如果您想将内容完全保留在控制器中,则必须使用 JSON.parse(request.raw) 之类的东西并自己做事。
    【解决方案2】:

    也许试试这个https://stackoverflow.com/a/14664355

    before_filter :set_default_response_format
    
    private
    
    def set_default_response_format
      request.format = :json
    end
    

    【讨论】:

    • 此建议无法解决问题/不起作用。问题在于传入请求的Content-Type。在 Rails 中,request.format 是关于传出响应的内容类型。
    猜你喜欢
    • 2018-06-07
    • 2021-02-22
    • 2021-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-26
    • 2021-06-07
    相关资源
    最近更新 更多