- 示例工程
- 教程
- 后端
【免费下载链接】aws-doc-sdk-examples
Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.
本篇技术指南围绕aws-doc-sdk-examples仓库中 SES v2 周报邮件(Coupon Newsletter)场景的核心 API ——ListContacts展开,完整讲解其请求语法、URI 参数、请求体过滤条件、响应结构与分页机制,并结合仓库内 Python / Rust / Java / .NET 多语言实现,帮助开发者理解如何在实际订阅列表中拉取联系人并驱动批量模板邮件发送。读完本文,你将掌握ListContacts的完整调用细节、参数语义、错误处理与多语言落地方式,可以直接照搬到自己的订阅管理类业务中。
一、场景定位:ListContacts 在周报邮件流程中的角色
ListContacts是 Amazon Simple Email Service (SES) v2 API 中用于列出指定联系人列表(Contact List)内全部联系人的操作。在本仓库的周报优惠券邮件场景中,它是"发送阶段"的前置关键步骤:订阅者通过CreateContact加入weekly-coupons-newsletter联系人列表后,程序必须先调用ListContacts取回所有订阅者邮箱地址,再对每个地址逐一执行模板化SendEmail(详见 场景 README 与 场景规范)。
完整流程可概括为四步:
- 准备应用:创建验证邮箱身份(
CreateEmailIdentity)、创建联系人列表(CreateContactList)、创建邮件模板(CreateEmailTemplate)。 - 收集订阅者:允许用户输入基础邮箱地址,通过子地址扩展(subaddress / plus addressing,即
user+ses-weekly-newsletter-1@example.com这类形式)生成 3 个变体,分别调用CreateContact添加联系人并发送欢迎邮件(SendEmailSimple 格式)。 - 发送周报:调用
ListContacts获取weekly-coupons-newsletter列表中的全部联系人,再对每个邮箱单独调用SendEmail(Template 格式),并通过ListManagementOptions指定联系人列表,使 SES 自动注入退订链接与退订头({{amazonSESUnsubscribeUrl}})。 - 监控与清理:在控制台查看发送指标,最后依次删除模板、联系人列表(可选)与邮箱身份。
ListContacts位于第 3 步入口,其返回结果直接决定了要发送的收件人集合。该 API 的完整参考文档见 content/10_ListContacts.md,本文即以其为骨架展开。
二、请求语法:HTTP 方法与路径构造
ListContacts是一个 HTTPGET请求,完整请求语法如下(来自关联文档):
GET /v2/email/contact-lists/{ContactListName}/contacts?NextToken={NextToken}&PageSize={PageSize} HTTP/1.1 Content-type: application/json { "Filter": { "FilteredStatus": "string", "TopicFilter": { "TopicName": "string", "UseDefaultIfPreferenceUnavailable": "boolean" } } }要点说明:
- HTTP 方法:
GET,用于读取联系人列表内容,不会产生副作用。 - 请求路径:
/v2/email/contact-lists/{ContactListName}/contacts,ContactListName是路径中的必填占位参数。 - 查询字符串:
NextToken与PageSize作为查询参数附加在路径之后,用于控制分页。 - 请求体:虽然方法是
GET,但该操作允许通过 JSON 请求体携带可选过滤条件Filter;Content-type为application/json。
三、URI 请求参数详解
关联文档明确规定了以下三个 URI 参数:
| 参数 | 必填 | 类型 | 说明 |
|---|---|---|---|
ContactListName | 是 | String | 要查询的联系人列表名称。若传入不存在的名称,服务端将抛出NotFoundException。 |
NextToken | 否 | String | 分页令牌字符串。当响应中存在更多联系人时,服务端会在上一次响应的NextToken字段返回令牌;将其原样带入下一次ListContacts调用(保持其余参数一致),即可获取下一页联系人。 |
PageSize | 否 | Integer | 单次调用最多返回的联系人数量。具体返回条数取决于列表中联系人总数与PageSize的对比关系:如果实际联系人数量多于该值,响应会附带NextToken,调用方需携带该令牌继续请求以取回剩余联系人。 |
实际使用时,建议总是按分页循环消费结果,而不是假设一次调用就能返回全部联系人——当列表增长后,缺少分页处理会导致数据不完整。
四、请求体:Filter 过滤条件
请求体仅接受一个可选字段Filter,类型为ListContactsFilter_object,用于对联系人集合进行过滤:
FilteredStatus(String,可选):按订阅状态过滤联系人。该字段取自联系人对象上的订阅状态枚举值(如订阅、退订、待处理等),用于仅筛选处于指定订阅状态的联系人。TopicFilter(对象,可选):按话题(Topic)偏好过滤联系人,其内部包含两个字段:TopicName(String,可选):要按之过滤的话题名称。只有当联系人在该话题上存在偏好记录时才会被匹配返回。UseDefaultIfPreferenceUnavailable(Boolean,可选):当联系人没有针对该话题的显式偏好时,是否回退使用默认偏好参与过滤判断。
Filter是可选的:不传该字段即返回列表中全部联系人(受PageSize分页约束)。周报场景中的 Python 实现正是不带过滤条件、仅按ContactListName查询的典型用法(见下文源码分析)。
五、响应结构与响应元素
成功时服务端返回HTTP 200,响应体 JSON 结构如下:
{ "Contacts": [ { "EmailAddress": "string", "LastUpdatedTimestamp": "number", "TopicDefaultPreferences": [ { "SubscriptionStatus": "string", "TopicName": "string" } ], "TopicPreferences": [ { "SubscriptionStatus": "string", "TopicName": "string" } ], "UnsubscribeAll": "boolean" } ], "NextToken": "string" }响应包含两个顶层字段:
Contacts:当前页返回的联系人对象数组(Array of Contact_objects)。每个联系人对象包含:EmailAddress(String):联系人邮箱地址,是后续SendEmail的收件人取值来源。LastUpdatedTimestamp(Number):联系人信息的最后更新时间戳。TopicDefaultPreferences(数组):联系人针对各话题的默认订阅偏好,每项含SubscriptionStatus(String)与TopicName(String)。TopicPreferences(数组):联系人针对各话题的实际订阅偏好,结构与TopicDefaultPreferences相同。UnsubscribeAll(Boolean):是否已对所有话题执行全局退订。
NextToken(String):若存在更多联系人可分页获取,此字段返回分页令牌;将其复制到后续相同参数的ListContacts调用中即可取下一页。若没有更多数据,该字段为空。
六、分页机制:PageSize 与 NextToken 的配合
关联文档对分页给出了明确的行为约定:
- 首次调用时,设置
PageSize指定单页上限;若列表联系人数量超过该值,服务端在响应中携带NextToken。 - 后续调用时,将上一次响应的
NextToken原样传入查询参数,并保持其余参数(ContactListName、PageSize、Filter)与首次调用完全一致,才能正确连续翻页。 - 当响应中不再返回
NextToken(或Contacts为空)时,说明已遍历完所有联系人。
这一令牌式分页设计意味着调用方应使用while循环或分页器(Paginator)来聚合全部联系人,而不是假定单次响应即完整数据。
七、错误处理:关联文档声明的异常集合
关联文档列出了ListContacts可能抛出的错误:
| 异常 | HTTP 状态码 | 含义 |
|---|---|---|
BadRequestException | 400 | 输入无效(例如非法的ContactListName、PageSize或Filter取值)。 |
NotFoundException | 404 | 访问的资源不存在(如指定的联系人列表不存在)。 |
TooManyRequestsException | 429 | 对同一操作发起了过多请求,触发了限流。 |
此外,所有 SES v2 操作还共享一组通用错误(Common Errors),本文不展开。在实际业务代码中,NotFoundException是最常见且必须显式处理的异常——它通常意味着联系人列表尚未创建或被误删。
八、源码级实现:多语言调用印证
1. Python:sesv2客户端直调 + 逐联系人发信
场景的 Python 实现位于 newsletter.py,send_coupon_newsletter方法中的核心调用片段(约 198-229 行)如下:
# 获取联系人列表 try: contacts_response = self.ses_client.list_contacts( ContactListName=CONTACT_LIST_NAME ) except ClientError as e: if e.response["Error"]["Code"] == "NotFoundException": print(f"Contact list '{CONTACT_LIST_NAME}' does not exist.") return else: raise e # 向每个联系人发送周报模板邮件 coupon_items = load_file_content("sample_coupons.json") for contact in contacts_response["Contacts"]: email_address = contact["EmailAddress"] self.ses_client.send_email( FromEmailAddress=self.verified_email, Destination={"ToAddresses": [email_address]}, Content={ "Template": { "TemplateName": TEMPLATE_NAME, "TemplateData": coupon_items, } }, ListManagementOptions={"ContactListName": CONTACT_LIST_NAME}, )从源码可以确认三个关键实践:
- 只传必填参数:
list_contacts仅传入ContactListName,未使用分页参数与Filter,属于小列表场景的简洁写法;若列表规模增长,应补充PageSize/NextToken循环。 - 错误分支:对
NotFoundException单独捕获并友好提示后返回,其余ClientError直接上抛。 - 响应字段消费:从
contacts_response["Contacts"]中逐项读取EmailAddress,作为Destination.ToAddresses的唯一收件人——这正对应关联文档响应结构中的Contacts[].EmailAddress字段,同时保证了每封邮件独立发送,便于退订追踪。
2. Rust:list_contacts()链式调用
Rust 实现位于 newsletter.rs(约 241-247 行),同样采用"先取列表、再逐个发信"的模式:
let contacts = self .client .list_contacts() .contact_list_name(CONTACT_LIST_NAME) .send() .await?; // 将 contacts 收集后逐项发送... let contacts = match self.client.list_contacts().send().await { Ok(list_contacts_output) => { list_contacts_output.contacts.unwrap().into_iter().collect() } ... };Rust 侧将contacts从响应中unwrap并收集为迭代集合,随后逐项处理。仓库还提供了独立的命令行示例 bin/list-contacts.rs 供快速验证 API 行为。
3. Java / .NET:同一操作的不同 SDK 形态
按 71_metadata.md 中的代码片段标签约定,各语言实现均以sesv2.{Action}命名规范对齐:
- Java(SDK v2):代码位于 javav2/example_code/ses,片段标签为
sesv2.java2.newsletter.ListContacts。 - Python(SDK v3):片段标签为
python.example_code.sesv2.ListContacts,即上文引用的list_contacts调用。 - Rust(SDK v1):片段标签为
sesv2.rust.list-contacts。
.NET 实现见 dotnetv3/SESv2/README.md。无论使用哪种语言,请求/响应语义均与关联文档保持一致:名称参数必填、响应以Contacts数组承载结果、NextToken承担分页续接职责。
4. 测试验证
仓库为场景提供了自动化测试。Python 侧测试见 newsletter_test.py,Rust 侧测试见 tests/test_newsletter.rs。这些测试通过 mock 或集成方式校验list_contacts的调用参数与错误分支,可用于验证上述调用形态的正确性。
九、实战小结:ListContacts 最佳实践清单
结合关联文档、场景规范与多语言源码,ListContacts的工程落地建议如下:
- 必填校验:调用前确认
ContactListName已存在,或对NotFoundException做幂等处理(场景规范中该错误属于"失败场景并提示用户"的级别)。 - 分页必做:生产环境列表可能超过单页上限,务必使用
NextToken循环拉取;关联文档强调后续调用需携带相同参数,否则翻页会错乱。 - 按需过滤:若业务需要区分订阅状态或话题偏好,通过请求体
Filter的FilteredStatus与TopicFilter提前裁剪数据,减少下游发信量。 - 逐人发信:周报场景要求每个联系人独立调用
SendEmail(配ListManagementOptions注入退订信息),因此ListContacts返回的EmailAddress应逐条消费,而非合并批量发送。 - 限流感知:发送阶段场景代码在沙箱环境下会在邮件之间休眠约 2 秒(Python 实现中为
sleep(1.1)),以规避TooManyRequestsException;读取联系人阶段若遇到 429,也应实现重试或降频策略。
综上所述,ListContacts虽是一个结构简单的GET操作,但它的分页令牌、过滤条件与响应字段直接决定了订阅型邮件系统的数据完整性与发送准确性。通过本文对关联文档的逐项拆解和仓库源码的对照印证,读者可以快速将其集成进自己的 SES v2 工作流中。
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0
- 示例工程
- 教程
- 后端
【免费下载链接】aws-doc-sdk-examples
Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below.
相关推荐
使用 Amazon SES v2 CreateContactList API 创建联系列表:语法、参数与 Weekly Mailer 场景实战
使用 Amazon SES v2 CreateContactList API 创建联系列表:语法、参数与 Weekly Mailer 场景实战 导读 本文围绕
示例工程教程后端使用 AWS SDK for .NET 构建 Amazon SES v2 优惠券新闻邮件工作流:从联系人列表到模板化群发
使用 AWS SDK for .NET 构建 Amazon SES v2 优惠券新闻邮件工作流:从联系人列表到模板化群发 Amazon SES v2 API 是
示例工程教程后端AWS SDK for Go V2 操作 Amazon DynamoDB 实战指南:表基础操作、PartiQL 单条与批量查询场景
AWS SDK for Go V2 操作 Amazon DynamoDB 实战指南:表基础操作、PartiQL 单条与批量查询场景 本文以 aws doc sd
示例工程教程后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考