tl;dr

nginx 既灵活方便,又功能丰富,可以帮助我们实现添加跨域头、URL 重写以及隐藏敏感信息等功能。本文介绍在使用 MailChimp
进行邮件营销中遇到的一个普遍问题,分析了浏览器发起跨域请求时,在浏览器和服务器之间的交互过程,在此基础上提供了一个使用 nginx
反向代理实现的、灵活经济的解决方案。


MailChimp是最大的邮件营销解决方案提供商,官方网站 https://mailchimp.com。

通过 MailChimp 可以定制邮件模板、创建自己的邮件列表、发起各种营销活动,并支持统计有多少人打开了邮件以及其他一些 SNS 功能,客户可以随时订阅或取消订阅邮件。

MailChimp 提供了多种订阅表单,如普通表单、嵌入表单、弹窗表单以及由第三方集成的表单……用于收集用户邮箱及其他一些个人信息,然而这些表单都存在一些问题,如普通表单必须进行 captcha 校验,嵌入表单及弹窗表单不支持注册后自动跳转到自己的页面,并且所有这些表单还需要用户在收件箱里收取确认邮件并确认订阅,对于一家迫切需要更多订阅用户实现邮件营销的公司来说,为了尽可能多地获取订阅用户,这些校验和确认能省则省。而第三方集成的表单则需要按月收费,每月十几到上百美元不等,好钢要用在刀刃上,这也是一笔能省则省的开支。


MailChimp 还提供了 API 接口,其中 与列表成员相关的 API 接口 包含列表成员的创建、读取、编辑以及删除功能,因此,可以通过 AJAX 方式进行邮件订阅,订阅流程中是否加入 captcha 校验完全自主,用户订阅之后也不需要进行邮箱确认,并且完全免费。

但这些 API 也存在两个问题,一是不支持跨域,二是 MailChimp 提供的 API key 是全权限的,意味着一旦泄露,捣蛋分子就可以利用 MailChimp 的 API 接口对你的 MailChimp 账号为所欲为。

因此,我们如果希望使用 MailChimp 的 API 实现用户订阅,就需要解决上述两个问题,兼具灵活性和功能性的 nginx 是个不错的选择。


首先我们需要一个 nginx,在本地试验的话用 docker 就可以了,在命令行终端执行 docker pull nginx,稍等片刻就可把 nginx 最新版的 docker 镜像拉取下来了。如果网络状况不佳,还可以配置 daocloud 的 Docker 加速器。

打开终端窗口运行 docker run --name temp-nginx nginx,再打开一个终端窗口,然后使用 docker cp temp-nginx:/etc/nginx/nginx.conf. 和 docker cp temp-nginx:/etc/nginx/conf.d/default.conf. 这两个命令从容器中将 nginx 相关的配置拷贝出来,拷贝出来的配置文件主要作为我们自己编写配置文件的参考,即使之前没有配置过 nginx,也可以依葫芦画瓢完成基本的配置。

nginx.conf 文件中通过 include 指令包含了 default.conf 文件的内容,我们首先把两个文件简化合并成一个文件,并删除不必要的注释,命名为 mailChimp.conf。

user nginx;
worker_processes 1;error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;events {worker_connections 1024;
}http {server {listen 80;location / {root   /usr/share/nginx/html;index  index.html index.htm;}}
}

目的达到,我们可以“卸磨杀驴”,随后运行 docker stop temp-nginx && docker rm temp-nginx 将前面创建的容器停止并删除。


为了方便调试,我们同时编写一个 shell 脚本,用于随时使用创建的 mailChimp.conf 配置文件启动 docker 容器,并将 localhost 的 8080 端口绑定到容器的 80 端口上,这样我们就可以向 http://localhost:8080/ 发起请求,并在命令行终端查看日志,一旦配置出现问题可以随时使用 ctrl+c 停止 docker 容器,修改配置后重新运行该脚本即可。

#!/usr/bin/env bashdocker rm mailChimp-nginxdocker run --name mailChimp-nginx -v /path/to/mailChimp.conf:/etc/nginx/nginx.conf:ro -it -p 8080:80 nginx

接下来我们开始编写自己的配置文件,实现指向 MailChimp 的代理,并在请求中添加 API key 进行鉴权,同时在响应中添加跨域头,以满足跨域需求。

user nginx;
worker_processes 1;error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;events {worker_connections 1024;
}http {server {listen 80;location / {root   /usr/share/nginx/html;index  index.html index.htm;}location /maillist/subscribe {if ($request_method = GET) {return 403;}add_header Access-Control-Allow-Origin * always;add_header Access-Control-Allow-Methods PUT,OPTIONS;add_header Access-Control-Allow-Headers Content-Type;if ($request_method = OPTIONS) {return 204;}proxy_set_header Authorization 'apikey {apiKey}';rewrite /maillist/subscribe/(.*)$ /3.0/lists/{listId}/members/$1 break;proxy_pass https://us{siteNumber}.api.mailchimp.com;}}
}

在上面的配置文件中,{apiKey} 用我们从 MailChimp 创建的 API key 代替,{listId} 用需要实现订阅的邮件列表的 ID 代替,而 {siteNumber} 则是 API key 的后缀数字。


我们仍然使用 docker 镜像中 /usr/share/nginx/html 目录的内容作为网站首页,同时添加一个 /maillist/subscribe 路径。

由于 MailChimp 的 API key 是全权限的,为避免用户直接通过浏览器访问 /maillist/subscribe/ 获取 MailChimp 的 /3.0/lists/{listId}/members/ 的内容,首先需要对 HTTP Method 为 GET 的请求返回 403 Forbidden,在配置文件中通过 if 指令进行 Method 判断,并通过 return 指令返回 403 代码。

基于我们浏览器进行跨域请求的了解,浏览器在正式发起跨域请求之前,先要发起一个 Method 为 OPTIONS 的请求(Preflighted Requests,预检请求),通过该请求获取服务端的响应以了解服务端是否支持跨域、支持哪些 Method、以及支持哪些 Header 等信息,简言之,获取服务器支持的 HTTP 请求特性,这些响应头在 OPTIONS 请求的响应以及正式请求响应中必须一致。

因此我们接着进行 HTTP 响应头的设置,由于后面我们对请求及响应进行了代理转发,而转发的 MailChimp 响应中不包含 Access-Control-Allow-Origin 跨域响应头,因此这里使用 add_header 添加 Access-Control-Allow-Origin 时,需要在后面加上 always,表示始终加上 Access-Control-Allow-Origin。

此外还需要添加 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 响应头。

经试验,如果采用 MailChimp 列表成员添加的 API,使用 POST Method,实现订阅邮件的添加,则当用户通过其他途径退订了列表之后,无法重新订阅,因此改为采用 MailChimp 列表成员编辑的 API,这是一个 PUT Method,请求 URL 路径中包含用户邮箱地址的 MD5 值,当请求的用户在列表中不存在时为添加,当用户存在时则为修改。因此这里的 Access-Control-Allow-Methods 设置为 PUT,OPTIONS

MailChimp API 仅支持 JSON 格式的数据,这需要在请求中设置 Content-Type 为 application/json; charset=utf-8,同时还需要在 nginx 的响应中设置 Access-Control-Allow-Headers 为 Content-Type

设置完毕,对于 Method 为 OPTIONS 的请求,我们不需要返回 body,因此再次使用 if 指令进行 Method 判断,并返回 204 代码表示无正文。

最后,就是代理转发的设置了,首先使用 proxy_set_header 指令设置鉴权请求头,然后使用 rewrite 指令进行 URL 重写,使用正则表达式获取 /maillist/subscribe/ 之后的 E-Mail 的 MD5,拼接成 MailChimp API 请求的 URL,最后,通过 proxy_pass 指令实现代理转发。


使用 Fetch 从前端发起请求,相关代码如下,API key、list ID 等敏感信息已被隐藏:

var headers = new Headers()
headers.append('Content-Type', 'application/json; charset=utf-8')var body = '{"email_address":"test@te.st","status":"subscribed"}'fetch('http://localhost:8080/maillist/subscribe/f1c49ab3e7dd54b1daee1f4bdc0a1f78', {method: 'PUT',headers,body
}).then(res => res.json()).then(json => {console.log(json)
}).catch(err => {console.log(err)
})

从控制台上看到结果如下:


实际项目中,body 使用 JSON.stringify 进行序列化,请求路径中的参数还需要使用 MD5 库进行 MD5 值计算。

最终的配置可以在 nginx 的 docker 镜像基础上创建一个包含了上述配置的 docker 镜像,然后部署到 DaoCloud 或其他提供 docker 容器服务的站点上去。下面是一个部署在 DaoCloud 的代理,http://mario-studio.daoapp.io/,使用 Vue 创建了一个超级简单的 订阅表单,欢迎大家订阅我的邮件列表,虽然还不知道我会发些什么(手动比心

用 nginx 代理 MailChimp API 并支持跨域相关推荐

  1. 详解nginx代理天地图做缓存解决跨域问题

    这篇文章主要介绍了详解nginx代理天地图做缓存解决跨域问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 作为一个GISer开发者,天地图是经常在项目中以底图的形式出现 ...

  2. Nginx配置服务器静态文件支持跨域访问

    2019独角兽企业重金招聘Python工程师标准>>> 在server中配置 add_header Access-Control-Allow-Origin *;add_header ...

  3. 阿里云个人站点基于nginx代理搭建https协议支持

    阿里云个人站点基于nginx代理搭建https协议支持 准备工作 购买免费个人版dv证书 配置nginx 开放防火墙端口 检测是否成功 准备工作 1.个人服务器 2.安装了ssl_moudle的ngi ...

  4. 如何判定API接口是否支持跨域访问

    1.背景 同一个h5页面需要访问多个不同的域名下的地址 2.判定方式 方式一 在浏览器控制台执行如下代码: var xhr = new XMLHttpRequest(); xhr.open('GET' ...

  5. 入门Nginx之-静态资源服务器及跨域配置

    简介 这里静态资源就以之前的一个项目文章地址为例,源码 Github,项目本身很简单,只是分别对第三方的服务端.自己的服务端发起请求. 不论是调用第三方服务端接口,还是自己的后端服务,如果跨域未在服务 ...

  6. 让 QtWebkit 支持跨域CROS - nowboy的CSDN博客 - 博客频道 - CSDN.NET

    让 QtWebkit 支持跨域CROS - nowboy的CSDN博客 - 博客频道 - CSDN.NET 让 QtWebkit 支持跨域CROS 2013-05-23 22:05 450人阅读 评论 ...

  7. 同源策略_如何支持跨域

    欢迎大家阅读<朝夕Net社区技术专刊> 我们致力于.NetCore的推广和落地,为更好的帮助大家学习,方便分享干货,特创此刊!很高兴你能成为忠实读者,文末福利不要错过哦! 01 PART ...

  8. SpringBoot2.1.5 (22)--- SpringBoot设置支持跨域请求

    SpringBoot2.1.5 (22)--- SpringBoot设置支持跨域请求 现代浏览器处于安全的考虑,在http/https请求时必须遵守同源策略,否则即使跨域的http/https 请求, ...

  9. 如何在nginx下配置网关,实现跨域请求

    1.在nginx下配置网关,实现跨域请求 ​ 首先进入虚拟机,打开nginx,开机自启命令 docker update nginx --restart=always 进入nginx下的conf目录下 ...

最新文章

  1. 技术大咖齐聚爱数智慧人机交互技术论坛 | CNCC2021
  2. 在JSP中读取POST的JSON数据
  3. javaee实验报告心得_准大四学生七月青软实训总结
  4. 解读ASP.NET 5 MVC6系列(10):Controller与Action
  5. C#对象映射器之Mapster
  6. 欢乐纪中A组赛【2019.8.10】
  7. 安全公司笔试面试题总结
  8. Unity Shader: Shader粒子广告牌
  9. ES6 Symbol之浅入解读
  10. django路由层 虚拟环境 视图层
  11. 你想学的都在这里!传智播客java就业班教程
  12. 【手把手带你学JavaSE】第八篇:抽象类和接口
  13. MySQL复制一张表数据到另一张新表
  14. ORB_SLAM3系统框图
  15. 本地安装MySQL详细步骤
  16. matlab怎么绘制零极点,matlab中画系统零极点的方法
  17. 《你是我的荣耀》制片人张萌犯受贿罪:被判处有期徒刑三年
  18. 企业架构TOGAF介绍
  19. 第一代计算机磁鼓,磁鼓计算机
  20. 安装mysql tomat jdk

热门文章

  1. JavaScript创建元素的三种方法特点及对元素的操作
  2. JavaScript中的数组与伪数组的区别
  3. 了解一下MongoDB中的写关注(write concern)
  4. JS中NaN、NULL、undefined、详解
  5. CSS在ASP.NET中使用
  6. 第一次使用VS Code时你应该知道的一切配置
  7. 云计算技术 — 云计算的商业模式与部署模式
  8. 5G LAN — Overview
  9. Go 语言编程 — 变量与常量
  10. 堆(heap)和栈(stack)有什么区别