如何从rails中的Amazon SNS Notification解析请求正文

时间:2015-04-17 04:36:42

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

我在我的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&#34,这是一个坏主意。 ;? (你怎么做的?)

1 个答案:

答案 0 :(得分:2)

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

在我的情况下,message参数也是作为字符串传递的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"

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