json 如何在Rails中解析Amazon SNS Notification的请求正文

afdcj2ne  于 2023-03-31  发布在  其他
关注(0)|答案(3)|浏览(123)

我在我的rails3.0应用程序中设置了一个端点,以接收来自AmazonSNS服务的推送通知。
Amazon发布的请求有一个JSON有效负载,但他们将请求的content-type设置为“text/plain”,这导致Rails无法解析正文。
来自Amazon's docs的post请求示例:

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对象并读取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”,这是一个坏主意吗?(如何做到这一点?)

pgccezyw

pgccezyw1#

我最近遇到了这个问题。网上提到的大多数方法对我都不起作用,所以我创建了一个中间件。中间件检测消息是否来自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内容类型。

ahy6op9u

ahy6op9u2#

在2023年第一季度,Amazon SNS推出了对从主题交付的HTTP消息的自定义Content-Type标头的支持。以下是发布帖子:https://aws.amazon.com/about-aws/whats-new/2023/03/amazon-sns-content-type-request-headers-http-s-notifications/
您必须修改Amazon SNS订阅的DeliveryPolicy属性,将headerContentType属性设置为application/json或支持的任何其他值。您可以在此处找到支持的所有值:https://docs.aws.amazon.com/sns/latest/dg/sns-message-delivery-retries.html#creating-delivery-policy

{
    "healthyRetryPolicy": {
        "minDelayTarget": 1,
        "maxDelayTarget": 60,
        "numRetries": 50,
        "numNoDelayRetries": 3,
        "numMinDelayRetries": 2,
        "numMaxDelayRetries": 35,
        "backoffFunction": "exponential"
    },
    "throttlePolicy": {
        "maxReceivesPerSecond": 10
    },
    "requestPolicy": {
        "headerContentType": "application/json"
    }
}

您可以通过调用SubscribeSetSubscriptionAttributes API操作来设置DeliveryPolicy属性:

或者,您也可以使用AWS CloudFormation来设置此策略。

vm0i2vca

vm0i2vca3#

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

before_filter :set_default_response_format

private

def set_default_response_format
  request.format = :json
end

相关问题