Node.js中使用AWS SNS服務發送短信

前言

Amazon Simple Notification Service (Amazon SNS) is a web service that coordinates and manages the delivery or sending of messages to subscribing endpoints or clients. In Amazon SNS, there are two types of clients—publishers and subscribers—also referred to as producers and consumers. Publishers communicate asynchronously with subscribers by producing and sending a message to a topic, which is a logical access point and communication channel. Subscribers (i.e., web servers, email addresses, Amazon SQS queues, AWS Lambda functions) consume or receive the message or notification over one of the supported protocols (i.e., Amazon SQS, HTTP/S, email, SMS, Lambda) when they are subscribed to the topic. [1]

正如SNS的介紹所述,SNS是AWS提供的一個消息收發服務,它包括了諸如消息推送、短信、電子郵件等服務。AWS官方文檔提供了非常多的內容,但提供的示例代碼是以Java或.Net爲主,關於Node.js的直接資料較少,所以這裏便來介紹如何使用AWS SNS服務發送短信。

Node.js中使用SNS發送短信

在Node.js中使用AWS的服務,需要先安裝aws-sdk依賴。AWS SDK中包括了衆多服務的接口,在這裏我們需要的是AWS.SNS類。首先,需要實例化AWS.SNS對象,其構造函數的參數爲一個對象,通常需要包括accessKeyIdsecretAccessKeyregion等屬性。在AWS IAM中,可生成並下載使用的用戶對應的accessKeyIdsecretAccessKey。需要注意的是,使用的用戶需要在IAM中設置SNS對應的權限。

const AWS = require('aws-sdk');

const options = {
  accessKeyId: 'String',
  secretAccessKey: 'String',
  apiVersion: '2010-03-31',
};

const snsService = new AWS.SNS(options);

通過AWS.SNS類的實例,就可以使用其進行SNS服務的相關操作。本文的主題爲使用SNS服務發送短信,所以接下來即可通過AWS.SNS實例的publish方法以短信形式發送消息。

AWS SDK for Node.js中,publish方法接收一個Object類型的參數,它其中包括MessageMessageAttributesMessageStructurePhoneNumberSubjectTargetArn以及TopicArn屬性。publish是一個SNS中通用的方法,發送郵件、消息推送也是通過它進行完成,所以在發送短信時部分的參數不是必須的。下面是一個發送短信所需最少參數的例子。

const params = {
  Message: text,
  MessageAttributes: {
    'AWS.SNS.SMS.SMSType': {
      DataType: 'String',
      StringValue: 'Transactional', // Transactional or Promotional
    },
    // AWS.SNS.SMS.MaxPrice
    // AWS.SNS.SMS.SenderId
  },
  PhoneNumber: phoneNumber, // 電話號碼,需要遵從E.164格式
};

MessageAttributes中可以包含多個屬性,例如上述例子中的AWS.SNS.SMS.SMSType。MessageAttribute的值主要包括DataTypeStringValueBinaryValue,其根據DataType的值決定需要的是StringValue或是BinaryValueStringValue接受一組以UTF-8編碼的字符串,而BinaryValue可以接受任何二進制值,例如壓縮後的數據、圖片等。

MessageAttributesAWS.SNS.SMS.SMSType屬性中,其值需爲TransactionalPromotional。二者的區別爲Transactional更爲可靠,但其價格通常更爲昂貴,一般用於發送較爲重要的消息(如短信驗證碼等),而Promotional一般用於發送推廣信息。另外AWS.SNS.SMS.MaxPrice爲願意爲發送消息支付以美元爲單位的最高金額;AWS.SNS.SMS.SenderId爲一個在接收設備上顯示的自定義ID,但其支持程度受所在地區限制,如中國便不支持SenderId的顯示。

使用上面定義的參數調用publish方法即可發送短信。publish方法提供了基於Promise的異步使用方法,只需將代碼修改爲:const response = await snsService.publish(params).promise();。同理,AWS SDK中的其它方法也可通過添加.promise()將其異步形式從回調改爲基於Promise。

snsService.publish(params, (err, data) => {
  if (err) {
    // ...
  }
  // ...
})

返回結果

在參數錯誤、權限不足等情況下,調用publish方法將會拋出諸如InvalidParameterAuthorizationError等錯誤,可根據其具體錯誤信息判斷錯誤原因。調用publish方法將會返回下述結果。其中,MessageId爲該消息的唯一標識符,當開啓CloudWatch Logs後可通過該標識符獲取消息的傳輸信息。

{
  "ResponseMetadata":{
    "RequestId":"bfb2a062-c201-5d34-a7d8-f5fd653b27f9"
  },
  "MessageId":"2b38eec7-a3f0-5679-a116-bb5804cadcb4"
}

未收到短信

調用publish接口成功並正常返回結果,不代表短信發送成功。短信發送失敗的原因可能是下列其中一個:

  • 被電話運營商作爲垃圾消息屏蔽
  • 目標已加入黑名單
  • 電話號碼無效
  • 消息正文無效
  • 電話運營商已屏蔽此消息
  • 電話運營商目前無法訪問/不可用
  • 電話已屏蔽SMS
  • 電話已加入黑名單
  • 電話當前無法訪問/可用
  • 電話號碼已退出
  • 此傳輸會超過最高價格
  • 嘗試聯繫電話時發生未知錯誤

publish方法返回的結果無法得知短信發送是否成功,只有在開啓CloudWatch Logs功能後才能從日誌中獲取短信是否發送成功及發送失敗的原因。

發送短信至多個號碼

使用AWS SNS可以實現同時發送短信至多個手機號碼,其中一種實現方法即爲多次調用publish方法向不同號碼發送短信。但重複多次調用publish方法需要耗費更多的時間,所以在此也可選用另一種方法,即使用Topic發送短信至多個號碼。該方法的步驟是在創建一個topic後,爲多個手機號碼訂閱該topic。訂閱後在發送消息時,會將消息發送至訂閱該topic的端點中,此處的端點即爲電話號碼。

在發送信息至多個號碼前,需要創建一個新的Topic並使用subscribe方法爲發送的號碼訂閱該Topic。Topic即一個消息信道,當有消息發送至topic中時,SNS服務將會將該消息發送至訂閱該topic的終端節點中。Topic可通過AWS Console創建,也可通過AWS SDK中的createTopic方法創建。

通過SDK調用createTopic創建Topic,需要包括NameAttributesTags三個參數,其中Name爲必須的參數。Attributes參數。

const params = {
  Name: 'STRING_VALUE', // Topic 名稱
  Attributes: {
    '<attributeName>': 'STRING_VALUE',
    /* '<attributeName>': ... */
  },
  Tags: [
    {
      Key: 'STRING_VALUE', // Tag key
      Value: 'STRING_VALUE', // Tag value
    },
    // ...
  ],
};

const response = await snsService.createTopic(params).promise();
// response.TopicArn

完成topic的創建之後,即可爲需要發送短信的電話號碼訂閱所創建的topic。爲電話號碼訂閱topic需要使用subscribe方法,它必填的參數包括TopicArnEndpoint以及Protocol。其中,Protocol的值必須爲smsTopicArn的值爲創建的Topic Arn地址,Endpoint爲訂閱Topic的電話號碼。另外,它的參數還包括有Attributes以及ReturnSubscriptionArn兩個屬性。Attributes屬性如同publish方法的MessageAttributes屬性,通過它的DeliveryPolicyFilterPolicyRawMessageDelivery配置訂閱的相關設置。

const params = {
  Protocol: 'sms',
  TopicArn: topicArn,
  // Attributes: {
  //   '<attributeName>': 'STRING_VALUE',
  // },
  Endpoint: phoneNumber,
  // ReturnSubscriptionArn: false,
};

const response = await snsService.subscribe(params).promise();

在爲需要發送短信的號碼訂閱topic後,即可調用publish方法發送消息。發送短信至多個號碼時調用publish方法的參數與發送至單個號碼大致相同,唯一需要修改的地方爲不再需要設置PhoneNumber,而是需要設置TopicArn屬性,它的值即爲上面調用createTopic所得到的TopicArn字段。

const params = {
  Message: text,
  MessageAttributes: {
    // 發送短信至topic同樣可設置SMSType, MaxPrice, SenderId三個屬性
    'AWS.SNS.SMS.SMSType': {
      DataType: 'String',
      StringValue: 'Transactional', // Transactional or Promotional
    },
    // AWS.SNS.SMS.MaxPrice
    // AWS.SNS.SMS.SenderId
  },
  TopicArn: topicArn, // 羣發的topic ARN地址
};

const response = await snsService.publish(params).promise();

結束語

AWS的free tier提供了每月100條的免費短信可以使用,但需要注意的是此處的免費短信接收方爲美國號碼。若要發送短信至國內號碼,價格約爲0.01531 USD(約0.11元),該價格相對於國內服務商較爲昂貴。另外,SNS發送短信至國內時失敗率也較高,且在發送失敗的情況下也是按正常價格收取費用。在實際開發中,若不是以海外業務爲主的情況下,可考慮國內服務商提供的短信服務。

參考資料

  1. Amazon Simple Notification Service Developer Guide
  2. AWS Javascript SDK Docs
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章