mailchimp教程
by Timur (Tima) Zhiyentayev
通过帖木儿(蒂玛)日因耶捷
如何将MailChimp集成到JavaScript Web应用程序中 (How to integrate MailChimp in a JavaScript web app)
If you are a blogger, publisher, or business owner who does content marketing, having a newsletter is a must. In this tutorial, you will learn how to add Mailchimp integration to a simple JavaScript app. You’ll ultimately build a form for guest users to subscribe to a newsletter.
如果您是从事内容营销的博客作者,发布者或企业所有者,则必须拥有时事通讯。 在本教程中,您将学习如何将Mailchimp集成添加到一个简单JavaScript应用程序。 您最终将建立一个供访客用户订阅新闻通讯的表单。
I wrote this tutorial for a junior/mid-career web developer. The tutorial assumes some basic knowledge of React, JavaScript, and HTTP.
我为初级/中级Web开发人员编写了本教程。 本教程假定您具有React,JavaScript和HTTP的一些基本知识 。
You’ll start the tutorial with a boilerplate app, gradually add code to it, and finally test Mailchimp API integration.
您将以样板应用程序开始本教程,逐步向其中添加代码,最后测试Mailchimp API集成。
The boilerplate app is built with React, Material-UI, Next, Express, Mongoose, and MongoDB. Here’s more about the boilerplate.
样板应用程序使用React,Material-UI,Next,Express,Mongoose和MongoDB构建。 这是有关样板的更多信息。
As mentioned above, our goal is to create a feature that allows a guest user to subscribe to a MailChimp newsletter. The user subscribes by manually adding their email address to a form on your website. Here is an overview of the data exchange that will occur between the client (browser) and server:
如上所述,我们的目标是创建一个允许来宾用户订阅MailChimp新闻通讯的功能。 用户通过手动将其电子邮件地址添加到您网站上的表单进行订阅。 这是客户端(浏览器)和服务器之间将进行的数据交换的概述:
A user adds their email address to the form and clicks
submit
用户将其电子邮件地址添加到表单中,然后单击“
submit
- The click triggers a client-side API method that sends the email address from the user’s browser to your app server 单击将触发客户端API方法,该方法将从用户的浏览器将电子邮件地址发送到您的应用服务器
- The client-side API method sends a POST request to a unique Express route 客户端API方法将POST请求发送到唯一的Express路由
- The Express route passes the email address to a server-side API method that sends a POST request to Mailchimp’s server Express路由将电子邮件地址传递到服务器端API方法,该方法将POST请求发送到Mailchimp的服务器
- The email address is successfully added to your Mailchimp list 电子邮件地址已成功添加到您的Mailchimp列表中
Specifically, you will achieve the following by the end of this tutorial:
具体来说,您将在本教程结束时实现以下目标:
Create a
Subscribe
page with a subscription form使用订阅表单创建
Subscribe
页面Define an API method called
subscribeToNewsletter()
using thefetch()
method使用
fetch()
方法定义一个称为subscribeToNewsletter()
的API方法Define an Express route
'/subscribe'
定义快递路线
'/subscribe'
Define a
subscribe()
API method that sends a POST request to Mailchimp's API server定义一个subscription
subscribe()
API方法,该方法将POST请求发送到Mailchimp的API服务器- Test out this data exchange with Postman and as a guest user 与Postman和来宾用户一起测试此数据交换
入门 (Getting started)
For this tutorial, we’ll use code located in the 1-start folder of our builderbook repo. If you don’t have time to run the app locally, I deployed this example app at: https://mailchimp.builderbook.org/subscribe
在本教程中,我们将使用位于builderbook repo的1-start文件夹中的代码 。 如果您没有时间在本地运行该应用程序, 请在以下位置部署此示例应用程序: https : //mailchimp.builderbook.org/subscribe
To run the app locally:
要在本地运行该应用程序:
- Clone the builderbook repo to your local machine with: 使用以下命令将builderbook存储库克隆到本地计算机:
git clone git@github.com:builderbook/builderbook.git
Inside the
1-start
folder, runyarn
ornpm install
to install all packages listed inpackage.json
.在
1-start
文件夹中,运行yarn
或npm install
安装package.json
列出的所有软件包。
To add Mailchimp API to our app, we will install and learn about the following packages:
要将Mailchimp API添加到我们的应用程序,我们将安装并了解以下软件包:
Let’s start by putting together the Subscribe
page. In addition to learning about the Mailchimp API, you will get familiar with Next.js, a framework for React apps.
让我们从整理“ Subscribe
页面开始。 除了学习Mailchimp API之外,您还将熟悉React应用程序框架Next.js。
A key feature of Next.js is server-side rendering for initial page load. Other features include routing, prefetching, hot code reload, code splitting, and preconfigured webpack.
Next.js的关键功能是用于初始页面加载的服务器端呈现。 其他功能包括路由,预取,热代码重载,代码拆分和预配置的Webpack。
订阅页面 (Subscribe page)
We will define a Subscribe
component as a child of ES6 class using extends.
我们将使用extends将Subscribe
组件定义为ES6类的子级 。
Instead of:
代替:
const Subscribe = React.createClass({})
We will use:
我们将使用:
class Subscribe extends React.Component {}
We will not specify ReactDOM.render()
or ReactDOM.hydrate
explicitly, since Next.js implements both internally.
我们不会明确指定ReactDOM.render()
或ReactDOM.hydrate
,因为Next.js在内部实现了两者。
A high-level structure for our Subscribe
page component is:
我们的“ Subscribe
页面组件的高级结构是:
import React from 'react';// other imports
class Subscribe extends React.Component { onSubmit = (e) => { // check if email is missing, return undefined // if email exists, call subscribeToNewsletter() API method };
render() { return ( // form with input and button ); }}
export default Subscribe;
Create a subscribe.js
file inside the pages
folder of 1-start
. Add the above code to this file. We will fill the // other imports
section as we go.
在1-start
的pages
文件夹内创建一个subscribe.js
文件。 将上面的代码添加到此文件。 我们将继续// other imports
部分。
Our form will have only two elements: (1) an input element for email addresses and (2) a button. Since our boilerplate app is integrated with Material-UI, we’ll use TextField and Button components from the Material-UI library. Add these two imports to your subscribe.js
file:
我们的表单只有两个元素:(1)电子邮件地址的输入元素和(2)按钮。 由于样板应用程序已与Material-UI集成在一起,因此我们将使用Material-UI库中的TextField和Button组件。 将这两个导入添加到您的subscribe.js
文件中:
import TextField from 'material-ui/TextField';import Button from 'material-ui/Button';
Put the TextField
and Button
components inside a <fo
rm> element:
将TextField
和Button
组件放在<fo
rm>元素内:
<form onSubmit={this.onSubmit}> <p>We will email you when a new tutorial is released:</p> <TextField type="email" label="Your email" style={styleTextField} required /> <p /> <Button variant="raised" color="primary" type="submit"> Subscribe </Button></form>
You can see that we passed some props to both TextField
and Button
components. For a complete list of props you can pass, check out the official docs for TextField props and Button props.
您可以看到我们将一些道具传递给了TextField
和Button
组件。 有关可以通过的道具的完整列表,请查看TextField道具和Button道具的官方文档。
We need to get an email address specified in TextField
. To access the value of TextField
, we add React's ref attribute to it:
我们需要获取在TextField
指定的电子邮件地址。 要访问TextField
的值,我们向其添加React的ref属性 :
inputRef={(elm) => { this.emailInput = elm;}}
We access the value with:
我们通过以下方式访问值:
this.emailInput.value
Two notes:
两个注意事项:
We did not use
ref="emailInput"
, since React documentation recommends using the contextual objectthis
. In JavaScript,this
is used to access an object in the context. If you configure Eslint properly, you would see an Eslint warning for this rule.我们没有使用
ref="emailInput"
,因为React文档建议使用上下文对象this
。 在JavaScript中,this
用于访问上下文中的对象。 如果您正确配置了Eslint,则会看到此规则的Eslint警告。Instead of
ref
, we usedinputRef
since theTextField
component is not aninput
HTML element.TextField
is a component of Material-UI and uses theinputRef
prop instead ofref
.因为
TextField
组件不是input
HTML元素,所以我们使用了inputRef
而不是ref
。TextField
是Material-UI的组件,并使用inputRef
代替ref
。
Before we define our onSubmit
function, let's run our app and take a look at our form. Your code at this point should look like: pages/subscribe.js
在定义onSubmit
函数之前,让我们运行我们的应用程序,看看表单。 此时的代码应类似于: pages/subscribe.js
import React from 'react';import Head from 'next/head';import TextField from 'material-ui/TextField';import Button from 'material-ui/Button';
import { styleTextField } from '../components/SharedStyles';import withLayout from '../lib/withLayout';
class Subscribe extends React.Component { onSubmit = (e) => { // some code };
render() { return ( <div style={{ padding: '10px 45px' }}> <Head> <title>Subscribe</title> <meta name="description" content="description for indexing bots" /> </Head> <br /> <form onSubmit={this.onSubmit}> <p>We will email you when a new tutorial is released:</p> <TextField inputRef={(elm) => { this.emailInput = elm; }} type="email" label="Your email" style={styleTextField} required /> <p /> <Button variant="raised" color="primary" type="submit"> Subscribe </Button> </form> </div> ); }}
export default withLayout(Subscribe);
A few notes:
一些注意事项:
In Next.js, you can specify page title and description using
Head
. See how we used it above.在Next.js中,您可以使用
Head
来指定页面标题和描述。 请参阅上面的用法。We added a
styleTextField
style. We keep this style incomponents/SharedStyles.js
, so that it's reusable and can be imported into any component or page.我们添加了一个
styleTextField
样式。 我们将这种样式保留在components/SharedStyles.js
,以便可重用并且可以将其导入任何组件或页面中。We wrapped the
Subscribe
component withwithLayout
. The higher-order componentwithLayout
ensures that a page gets aHeader
component and is server-side rendered on initial load.我们用
withLayout
包装了Subscribe
组件。withLayout
的高阶组件可确保页面获取Header
组件,并在初始加载时在服务器端呈现。
We access the Subscribe
page at the /subscribe
route, since Next.js creates the route for a page from the page's file name inside the pages
folder.
我们通过/subscribe
路由访问Subscribe
页面,因为Next.js从pages
文件夹内的pages
文件名创建页面的路由。
Start your app with yarn dev
and go to http://localhost:8000/subscribe
用yarn dev
启动您的应用程序,然后转到http://localhost:8000/subscribe
The form looks as expected. Try changing the values passed to different props of the TextField
and Button
components. For example, change text for the label
prop to Type your email
and change the Button variant
prop to flat
:
该窗体看起来像预期的那样。 尝试更改传递给TextField
和Button
组件的不同属性的值。 例如,将label
prop的文本更改为Type your email
,并将Button variant
prop更改为flat
:
Before we continue, click the Log in
link in the Header
. Note the loading progress bar at the top of the page. We implemented this bar with Nprogress, and we will show it while waiting for our code to send an email address to a Mailchimp list.
在继续之前,请单击“ Header
的“ Log in
链接。 请注意页面顶部的加载进度栏。 我们使用Nprogress实现了该栏,并在等待代码将电子邮件地址发送到Mailchimp列表时显示它。
Our next step is to define the onSubmit
function. The purpose of this function is to get the email address from TextField
, pass that email address to an API method subscribeToNewsletter
, and then call the method.
下一步是定义onSubmit
函数。 此功能的目的是从TextField
获取电子邮件地址,将该电子邮件地址传递给API方法subscribeToNewsletter
,然后调用该方法。
Before we call subscribeToNewsletter(email)
, let's prevent a default behavior of our <fo
rm> element and define
email:
在调用subscribeToNewsletter(email)
,让我们防止<fo
rm>元素和d efine
电子邮件的默认行为:
Prevent the default behavior of sending form data to a server with:
防止通过以下方式将表单数据发送到服务器的默认行为:
e.preventDefault();
Let’s define a local variable
email
. It has the valuethis.emailInput.value
if boththis.emailInput
andthis.emailInput.value
exist, otherwise it is null:让我们定义一个本地变量
email
。 它具有价值this.emailInput.value
如果两个this.emailInput
和this.emailInput.value
存在,否则是无效:
const email = (this.emailInput && this.emailInput.value) || null;
If
email
is null, the function should return undefined:如果
email
为null,则函数应返回undefined:
if (this.emailInput && !email) { return;}
So far we have:
到目前为止,我们有:
onSubmit = (e) => { e.preventDefault();
const email = (this.emailInput && this.emailInput.value) || null;
if (this.emailInput && !email) { return; }
// call subscribeToNewsletter(email)};
To call our API method subscribeToNewsletter(email)
, let's use the async/await
construct together with try/catch
. We cover async callbacks, Promise.then
, and async/await
in detail in our book.
要调用我们的API方法subscribeToNewsletter(email)
,让我们将async/await
构造与try/catch
一起使用。 我们在本书中详细介绍了异步回调, Promise.then
和async/await
。
To use async/await
, prepend async
to an anonymous arrow function like this:
要使用async/await
,请在async
添加一个匿名箭头函数,如下所示:
onSubmit = async (e) =>
Providing subscribeToNewsletter(email)
should return a Promise (and it does — we define this method later in this tutorial using JavaScript's fetch()
method that returns a Promise). You can prepend await
to subscribeToNewsletter(email)
:
提供subscribeToNewsletter(email)
应该返回一个Promise(并且确实如此—我们在本教程后面的部分中使用JavaScript的fetch()
方法定义了此方法,该方法返回一个Promise)。 您可以预先await
subscriptionToNewsletter subscribeToNewsletter(email)
:
await subscribeToNewsletter({ email })
You get:
你得到:
onSubmit = async (e) => { e.preventDefault();
const email = (this.emailInput && this.emailInput.value) || null;
if (this.emailInput && !email) { return; }
try { await subscribeToNewsletter({ email });
if (this.emailInput) { this.emailInput.value = ''; } } catch (err) { console.log(err); //eslint-disable-line }};
JavaScript will pause at the line with await subscribeToNewsletter({ email });
and continue only after subscribeToNewsletter({ email })
returns a response with a success or error message.
JavaScript将在行上暂停并await subscribeToNewsletter({ email });
并且仅在subscribeToNewsletter({ email })
返回带有成功或错误消息的响应后才继续。
In the case of success, let’s clear our form with:
如果成功,让我们通过以下方式清除表格:
if (this.emailInput) { this.emailInput.value = ''; }
Before we define our subscribeToNewsletter
API method, let's make a UX improvement. Use NProgress.start();
to start bar loading and use NProgress.done();
to complete bar loading:
在定义我们的subscribeToNewsletter
API方法之前,让我们进行UX改进。 使用NProgress.start();
开始加载酒吧并使用NProgress.done();
完成条形加载:
onSubmit = async (e) => { e.preventDefault();
const email = (this.emailInput && this.emailInput.value) || null;
if (this.emailInput && !email) { return; }
NProgress.start();
try { await subscribeToNewsletter({ email });
if (this.emailInput) { this.emailInput.value = ''; }
NProgress.done(); } catch (err) { console.log(err); //eslint-disable-line NProgress.done(); }};
With this change, a user who submits a form will see the progress bar.
进行此更改后,提交表单的用户将看到进度栏。
Code for your Subscribe
page should look like: pages/subscribe.js
您的“ Subscribe
页面的代码应类似于: pages/subscribe.js
import React from 'react';import Head from 'next/head';import TextField from 'material-ui/TextField';import Button from 'material-ui/Button';import NProgress from 'nprogress';
import { styleTextField } from '../components/SharedStyles';import withLayout from '../lib/withLayout';import { subscribeToNewsletter } from '../lib/api/public';
class Subscribe extends React.Component { onSubmit = async (e) => { e.preventDefault();
const email = (this.emailInput && this.emailInput.value) || null;
if (this.emailInput && !email) { return; }
NProgress.start();
try { await subscribeToNewsletter({ email });
if (this.emailInput) { this.emailInput.value = ''; }
NProgress.done(); console.log('non-error response is received'); } catch (err) { console.log(err); //eslint-disable-line NProgress.done(); } };
render() { return ( <div style={{ padding: '10px 45px' }}> <Head> <title>Subscribe</title> <meta name="description" content="description for indexing bots" /> </Head> <br /> <form onSubmit={this.onSubmit}> <p>We will email you when a new tutorial is released:</p> <TextField inputRef={(elm) => { this.emailInput = elm; }} type="email" label="Your email" style={styleTextField} required /> <p /> <Button variant="raised" color="primary" type="submit"> Subscribe </Button> </form> </div> ); }}
export default withLayout(Subscribe);
Start your app with yarn dev
and make sure your page and form look as expected. Submitting a form won't work yet, since we haven't defined the API method subscribeToNewsletter()
.
从yarn dev
启动您的应用程序,并确保您的页面和表单看起来符合预期。 由于尚未定义API方法subscribeToNewsletter()
,因此无法提交表单。
subscriptionToNewsletter API方法 (subscribeToNewsletter API method)
As you may have noticed from the import section of pages/subscribe.js
, we will define subscribeToNewsletter()
at lib/api/public.js
. We placed subscribeToNewsletter()
to the lib
folder to make it universally accessible, meaning this API method will be available on both client (browser) and server. We do so because in Next.js, page code is server-side rendered on initial load and client-side rendered on subsequent loads.
正如您可能在pages/subscribe.js
的import部分中注意到的那样,我们将在lib/api/public.js
定义lib/api/public.js
subscribeToNewsletter()
。 我们将subscribeToNewsletter()
放置在lib
文件夹中,以使其可以普遍访问,这意味着此API方法将在客户端(浏览器)和服务器上均可用。 我们这样做是因为在Next.js中,页面代码是在初始加载时在服务器端呈现的,而在后续加载时是客户端呈现的。
In our case, when a user clicks a button on the browser to call subscribeToNewsletter()
, this method will run only on the client. But imagine that you have a getPostList
API method that fetches a list of blog posts. To render a page with a list of posts on the server, you have to make getPostList
universally available.
在我们的情况下,当用户单击浏览器上的按钮以调用subscribeToNewsletter()
,此方法将仅在客户端上运行。 但是,假设您有一个getPostList
API方法可获取博客文章列表。 要在服务器上呈现包含帖子列表的页面,必须使getPostList
普遍可用。
Back to our API method subscribeToNewsletter()
. As we discussed in the introduction to this tutorial, our goal is to hook up a data exchange between client and server. In other words, our goal is to build an internal API for our app. That's why we call subscribeToNewsletter()
an API method.
回到我们的API方法subscribeToNewsletter()
。 正如我们在本教程的简介中讨论的那样,我们的目标是建立客户端与服务器之间的数据交换。 换句话说,我们的目标是为我们的应用程序构建内部API。 这就是为什么我们将subscribeToNewsletter()
称为API方法。
The purpose of subscribeToNewsletter()
is to send a request to the server at a particular route called an API endpoint and then receive a response. We discuss HTTP and request/response in detail here.
subscribeToNewsletter()
的目的是通过称为API端点的特定路由向服务器发送请求 ,然后接收响应。 我们在这里详细讨论HTTP和请求/响应。
To understand this tutorial, you should know that a request that passes data to the server and does not require any data back is sent with the POST
method. Usually, the request's body
contains data (in our case, email address).
要理解本教程,您应该知道,使用POST
方法发送了将数据传递到服务器且不需要返回任何数据的请求。 通常,请求的body
包含数据(在我们的示例中为电子邮件地址)。
In addition to sending a request, our subscribeToNewsletter()
method should wait for a response. The response does not have to contain any data — it could be a simple object with one parameter { subscribed: 1 }
or { done: 1 }
or { success: 1 }
.
除了发送请求之外,我们的subscribeToNewsletter()
方法还应等待响应。 响应不必包含任何数据-它可以是带有一个参数{ subscribed: 1 }
或{ done: 1 }
或{ success: 1 }
的简单对象。
To achieve both sending a request and receiving a response, we use the fetch()
method. In JavaScript, fetch() is a global method that is used for fetching data over a network by sending a request and receiving a response.
为了实现发送请求和接收响应的双重目的,我们使用了fetch()
方法。 在JavaScript中, fetch()是一种全局方法,用于通过发送请求和接收响应来通过网络获取数据。
We use the isomorphic-fetch
package that makes fetch()
available in our Node environment. Install this package with:
我们使用isomorphic-fetch
包,该包使fetch()
在我们的Node环境中可用。 使用以下命令安装此软件包:
yarn add isomorphic-fetch
Here’s an example of usage from the package’s README:
这是包自述文件中的用法示例:
fetch('//offline-news-api.herokuapp.com/stories') .then(function(response) { if (response.status >= 400) { throw new Error("Bad response from server"); } return response.json(); }) .then(function(stories) { console.log(stories); });
Let’s use this example to write a reusable sendRequest
method that takes path
and some other options
, passes a request object (object that has method
, credentials
and options
properties), and calls the fetch()
method. fetch()
takes path
and the request object as arguments:
让我们使用此示例编写一个可重用的sendRequest
方法, sendRequest
方法采用path
和其他一些options
,传递一个请求对象(具有method
, credentials
和options
属性的对象),然后调用fetch()
方法。 fetch()
将path
和请求对象作为参数:
async function sendRequest(path, options = {}) { const headers = { 'Content-type': 'application/json; charset=UTF-8', };
const response = await fetch( `${ROOT_URL}${path}`, Object.assign({ method: 'POST', credentials: 'include' }, { headers }, options), );
const data = await response.json();
if (data.error) { throw new Error(data.error); }
return data;}
Unlike the example from isomorphic-fetch
, we used our favorite async/await
construct instead of Promise.then
(for better code readability).
与isomorphic-fetch
的示例不同,我们使用了我们最喜欢的async/await
构造而不是Promise.then
(以提高代码的可读性)。
Object.assign() is a method that creates a new object out of three smaller objects: { method: 'POST', credentials: 'include' }
, { headers }
, and options
. The object options
is empty by default, but it could be, for example, the request's body
property. Since we need to pass an email address, our case indeed uses the body
property.
Object.assign()是一种从三个较小的对象中创建新对象的{ method: 'POST', credentials: 'include' }
, { headers }
和options
。 默认情况下,对象options
为空,但可以是例如请求的body
属性。 由于我们需要传递电子邮件地址,因此我们的案例确实使用了body
属性。
As you may have noticed from the code, we need to define ROOT_URL
. We can write conditional logic for ROOT_URL
that takes into consideration NODE_ENV
and PORT
, but for simplicity’s sake, we define it as:
正如您可能从代码中注意到的那样,我们需要定义ROOT_URL
。 我们可以为ROOT_URL
编写条件逻辑,该条件逻辑考虑了NODE_ENV
和PORT
,但是为了简单起见,我们将其定义为:
const ROOT_URL = 'http://localhost:8000';
It’s time to define our subscribeToNewsletter
method with the help of the sendRequest
method:
是时候借助sendRequest
方法来定义我们的subscribeToNewsletter
方法了:
export const subscribeToNewsletter = ({ email }) => sendRequest('/api/v1/public/subscribe', { body: JSON.stringify({ email }), });
As you can see, we pass { body: JSON.stringify({ email }), }
as an options
object to add an email address to the body of the request object.
如您所见,我们传递{ body: JSON.stringify({ email }), }
作为options
对象,以将电子邮件地址添加到请求对象的主体中。
Also we chose /api/v1/public/subscribe
as our path
, that is the API endpoint for our internal API that adds a user email address to our Mailchimp list.
另外,我们选择/api/v1/public/subscribe
作为我们的path
,这是内部API的API端点,该API将用户电子邮件地址添加到Mailchimp列表中。
Put it all together and the content of the lib/api/public.js
should be: lib/api/public.js
放在一起, lib/api/public.js
应为: lib/api/public.js
import 'isomorphic-fetch';
const ROOT_URL = 'http://localhost:8000';
async function sendRequest(path, options = {}) { const headers = { 'Content-type': 'application/json; charset=UTF-8', };
const response = await fetch( `${ROOT_URL}${path}`, Object.assign({ method: 'POST', credentials: 'include' }, { headers }, options), );
const data = await response.json();
if (data.error) { throw new Error(data.error); }
return data;}
export const subscribeToNewsletter = ({ email }) => sendRequest('/api/v1/public/subscribe', { body: JSON.stringify({ email }), });
Good job reaching this point! We defined our subscribeToNewsletter
API method that sends a request to the API endpoint /api/v1/public/subscribe
and receives a response.
做好这一点! 我们定义了subscribeToNewsletter
API方法,该方法将请求发送到API端点/api/v1/public/subscribe
并接收响应。
Start your app with yarn dev
, add an email address, and submit the form. In your browser console (Developer tools > Cons
ole), you will see an expected POST
404 error:
使用yarn dev
启动您的应用程序,添加电子邮件地址,然后提交表单。 在浏览器控制台中( Developer tools > Cons
控制台),您将看到预期的ed POST
404错误:
That error means that the request was successfully sent to the server, but the server did not find what was requested. This is expected behavior since we did not write any server code that sends a response to the client when a request is sent to corresponding API endpoint. In other words, we did not create the Express route /api/v1/public/subscribe
that handles the POST request we sent using the subscribeToNewsletter
API method.
该错误意味着请求已成功发送到服务器,但是服务器未找到请求的内容。 这是预期的行为,因为当请求发送到相应的API端点时,我们没有编写任何服务器代码来将响应发送给客户端。 换句话说,我们没有创建处理通过使用subscribeToNewsletter
API方法发送的POST请求的Express路由/api/v1/public/subscribe
。
快速路线/订阅 (Express route/subscribe)
An Express route specifies a function that gets executed when an API method sends a request from the client to the route’s API endpoint. In our case, when our API method sends a request to the API endpoint /api/v1/public/subscribe
, we want the server to handle this request with an Express route that executes some function.
Express路由指定当API方法从客户端向路由的API端点发送请求时执行的功能。 在我们的例子中,当我们的API方法将请求发送到API端点/api/v1/public/subscribe
,我们希望服务器使用执行某些功能的Express路由来处理此请求。
You can use the class express.Router()
and syntax router.METHOD()
to modularize Express routes into small groups based on user type:
您可以使用express.Router()
类和router.METHOD()
语法根据用户类型将Express路由模块化为小组:
const router = express.Router();router.METHOD('API endpoint', ...);
If you’d like to learn more, check out the official Express docs on express.Router() and router.METHOD().
如果您想了解更多信息,请查看express.Router()和router.METHOD()上的官方Express文档。
However, in this tutorial, instead of modularizing, we will use:
但是,在本教程中,我们将使用:
server.METHOD('API endpoint', ...);
And place the above code directly into our main server code at server/app.js
.
并将以上代码直接放入我们的主服务器代码中,位于server/app.js
You already have enough information to put together a basic Express route:
您已经有足够的信息来整理基本的Express路线:
- The method is POST 方法是POST
The API endpoint is
/api/v1/public/subscribe
API端点为
/api/v1/public/subscribe
From writing
onSubmit
andsubscribeToNewsletter
, you know about an anonymous arrow function通过编写
onSubmit
和subscribeToNewsletter
,您将了解匿名箭头功能From writing
onSubmit
, you know about thetry/catch
construct通过编写
onSubmit
,您可以了解try/catch
构造
Put all this knowledge together, and you get:
将所有这些知识放在一起,您将获得:
server.post('/api/v1/public/subscribe', (req, res) => { try { res.json({ subscribed: 1 }); console.log('non-error response is sent'); } catch (err) { res.json({ error: err.message || err.toString() }); }});
A couple of notes:
一些注意事项:
We wrote
error: err.message || err.toString()
to handle both situations: when the error is a type of string and when the error is an object.我们写了
error: err.message || err.toString()
error: err.message || err.toString()
处理以下两种情况:当错误是字符串类型时,以及错误是对象时。- To test out our Express route, we added the line: 为了测试我们的Express路线,我们添加了以下行:
console.log(‘non-error response is sent’);
Add the above Express route to server/app.js
after this line:
在此行之后,将上述Express路由添加到server/app.js
:
const server = express();
It’s time to test!
现在该进行测试了!
We recommend using the Postman app for testing out a request-response cycle.
我们建议您使用Postman应用程序来测试请求-响应周期。
Look at this snapshot of request properties in Postman:
查看Postman中请求属性的快照:
You need to specify at least three properties (similar to when we wrote the subscribeToNewsletter
API method):
您需要至少指定三个属性(类似于我们编写subscribeToNewsletter
API方法时的属性):
- Select POST method 选择开机自检方法
Specify the full path for the API endpoint:
http://localhost:8000/api/v1/public/subscribe
指定API端点的完整路径:
http://localhost:8000/api/v1/public/subscribe
Add a
Content-Type
header with the valueapplication/json
添加带有值
application/json
的Content-Type
标头
Make sure your app is running. Start it with yarn dev
. Now click the Send
button on Postman.
确保您的应用正在运行。 从yarn dev
开始。 现在单击邮递员上的Send
按钮。
If successful, you will see the following two outputs:
如果成功,您将看到以下两个输出:
- On Postman, you see the response has code 200 and the following body: 在Postman上,您看到响应的代码为200和以下正文:
2. Your terminal prints:
2.您的终端打印:
Good job, you just wrote a working Express route!
做得好,您刚刚写了一条有效的Express路线!
At this point, you showed that two events happen successfully in your app: a request gets sent and a response is received. However, we did not pass an email address to a function inside our Express route. To do so, we need to access req.body.email
, because this is where we saved the email address when defining the subscribeToNewsletter
API method:
至此,您证明了两个事件在您的应用程序中成功发生:发送请求并接收到响应。 但是,我们没有将电子邮件地址传递给Express路由内的函数。 为此,我们需要访问req.body.email
,因为这是我们在定义subscribeToNewsletter
API方法时保存电子邮件地址的位置:
const email = req.body.email;
With ES6 object destructuring, it becomes shorter:
通过ES6对象分解,它变得更短:
const { email } = req.body;
If the email
local variable does not exist, then let's send a response with an error and return undefined (exit with blank return
):
如果email
局部变量不存在,那么我们发送一个带有错误的响应并返回undefined(以空白return
退出):
if (!email) { res.json({ error: 'Email is required' }); return;}
Also, modify the console.log
statement to print out email
.
另外,修改console.log
语句以打印出email
。
After these modifications, you get:
完成这些修改后,您将获得:
server.post('/api/v1/public/subscribe', async (req, res) => { const { email } = req.body;
if (!email) { res.json({ error: 'Email is required' }); return; }
try { res.json({ subscribed: 1 }); console.log(email); } catch (err) { res.json({ error: err.message || err.toString() }); }});
Let’s test it out. Open Postman, and add one more property to our request: body
with value team@builderbook.org
. Make sure that you selected the raw > J
SON data format:
让我们测试一下。 打开邮差,还有一个属性添加到我们的要求: body
与价值team@builderbook.org
。 确保选择raw > J
SON数据格式:
Make sure that your app is running and then click the Send
button.
确保您的应用程序正在运行,然后单击“ Send
按钮。
Look at the response on Postman and the output of your terminal:
查看有关Postman的响应和终端的输出:
Postman will display
Loading...
but never finish邮递员将显示“
Loading...
但永远不会完成Terminal outputs an error:
TypeError: Cannot read property 'email' of undefined
终端输出错误:
TypeError: Cannot read property 'email' of undefined
Apparently, the email
variable is undefined. To read the email
property from req.body
, you need a utility that decodes the body
object of a request from Unicode to JSON format. This utility is called bodyParser
, read more about it here.
显然, email
变量未定义。 要从req.body
读取email
属性,您需要一个实用程序,该实用程序将请求的body
对象从Unicode解码为JSON格式。 该实用程序称为bodyParser
, 在此处了解更多信息 。
Install bodyParser
:
安装bodyParser
:
yarn add body-parser
Import it to server/app.js
with:
使用以下命令将其导入到server/app.js
:
import bodyParser from 'body-parser';
Mount JSON bodyParser
on the server. Add the following line right after const server = express();
and before your Express route:
在服务器上安装JSON bodyParser
。 在const server = express();
之后添加以下行 在您的Express路线之前 :
server.use(bodyParser.json());
An alternative to using the external bodyParser
package is to use internal Express middleware express.json(). To do so, remove the import code for bodyParser
and replace the above line of code with:
使用外部bodyParser
包的替代方法是使用内部Express中间件express.json() 。 为此,请删除bodyParser
的导入代码, bodyParser
以上代码行替换为:
server.use(express.json());
We are ready to test. Make sure your app is running and click the Send
button on Postman.
我们准备测试。 确保您的应用程序正在运行,然后单击Postman上的“ Send
按钮。
Take a look at the response on Postman and your terminal:
看一下Postman和您的终端上的响应:
Postman successfully outputs:
"subscribed": 1
邮递员成功输出:
"subscribed": 1
Terminal has no error this time, instead it prints:
team@builderbook.org
终端这次没有错误,而是打印:
team@builderbook.org
Great, now the request’s body
is decoded and available inside the Express route's function as req.body
.
太好了,现在请求的body
已解码,并且在Express路由的功能内可以作为req.body
。
You successfully added the first internal API to this app! Data exchange between client and server works as expected.
您已成功向该应用添加了第一个内部API! 客户端和服务器之间的数据交换按预期进行。
Inside the Express route that we wrote earlier, we want to call and wait for a subscribe
method that sends a POST request from our server to Mailchimp's. In the next and final section of this tutorial, we will discuss and write the subscribe
method.
快递航线,我们之前写的里面,我们要打电话,等待subscribe
发送从我们的服务器Mailchimp的POST请求方法。 在本教程的下一个也是最后一部分,我们将讨论并编写subscribe
方法。
方法subscription() (Method subscribe())
We wrote code for proper data exchange between our server and a user’s browser. However, to add a user’s email address to a Mailchimp list, we need to send a server to server POST request. POST request from our server to Mailchimp’s server.
我们编写了代码,以实现服务器与用户浏览器之间的正确数据交换。 但是,要将用户的电子邮件地址添加到Mailchimp列表中,我们需要将服务器发送到服务器 POST请求。 从我们的服务器到Mailchimp的服务器的 POST请求。
To send a server to server request, we will use the request
package. Install it:
要将服务器发送到服务器请求,我们将使用request
包。 安装它:
yarn add request
As with any request, we need to figure out which API endpoint and what request properties to include (headers
, body
and so on):
与任何请求一样,我们需要弄清楚要包含的API端点和请求属性( headers
, body
等):
Create a
server/mailchimp.js
file.创建一个
server/mailchimp.js
文件。Import
request
.导入
request
。Define
request.post()
(POST request) with these properties:uri
,headers
,json
,body
, and callback.使用以下属性定义
request.post()
(POST请求):uri
,headers
,json
,body
和callback。
server/mailchimp.js
:
server/mailchimp.js
:
import request from 'request';
export async function subscribe({ email }) { const data = { email_address: email, status: 'subscribed', };
await new Promise((resolve, reject) => { request.post( { uri: // to be discussed headers: { Accept: 'application/json', Authorization: // to be discussed, }, json: true, body: data, }, (err, response, body) => { if (err) { reject(err); } else { resolve(body); } }, ); });}
All properties are self-explanatory, but we should discuss uri
(or API endpoint) and Authorization
header:
所有属性都是不言自明的,但是我们应该讨论uri
(或API端点)和Authorization
标头:
1. uri
. Earlier in this chapter, we picked http://localhost:8000/api/v1/public/subscribe
as our API endpoint. We could've picked any route for our internal API. However, Mailchimp’s API is external. Thus we should check the official documentation to find the API endpoint that adds an email address to a list. Read more about the API to add members to a list. The API endpoint is:
1. uri
。 在本章的前面,我们选择了http://localhost:8000/api/v1/public/subscribe
作为我们的API端点。 我们可以为内部API选择任何路线。 但是,Mailchimp的API是外部的。 因此,我们应该检查官方文档以找到将电子邮件地址添加到列表的API端点。 阅读有关API的更多信息, 以将成员添加到列表中 。 API端点是:
https://usX.api.mailchimp.com/3.0/lists/{LIST_ID}/members
Region usX
is a subdomain. Follow these steps to find the subdomain for an API endpoint:
区域usX
是一个子域。 请按照以下步骤查找API端点的子域:
- sign up or log in to Mailchimp 注册或登录Mailchimp
go to
Account > Extras > API keys > Your
API keys转到
Account > Extras > API keys > Your
API密钥your API key may look like
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us17
您的API密钥可能类似于
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us17
That means the region is us17
and your app will send requests to the Mailchimp subdomain:
这表示该地区为us17
,您的应用程序会将请求发送到Mailchimp子域:
https://us17.api.mailchimp.com/3.0/lists/{LIST_ID}/members
Variable LIST_ID
is the List ID of a particular list in your Mailchimp account. To find List ID
, follow these steps:
变量LIST_ID
是Mailchimp帐户中特定列表的列表ID。 要查找List ID
,请按照下列步骤操作:
On your Mailchimp dashboard, go to
Lists > click the list name > Settings > List name and
defaults在Mailchimp仪表板上,转到
Lists > click the list name > Settings > List name and
默认值Find the section
List ID
找到“
List ID
”部分Get the
xxxxxxxxxx
value from this section, it's yourLIST_ID
从此部分获取
xxxxxxxxxx
值,这是您的LIST_ID
2. Authorization
header. We need to send our API_KEY
inside Authorization
header to Mailchimp's server. This tells Mailchimp's server that our app is authorized to send a request. Read more about Authorization header here (headers.Authorization
). Syntax for Authorization
header:
2. Authorization
头。 我们需要将Authorization
标头中的API_KEY
发送到Mailchimp的服务器。 这告诉Mailchimp的服务器我们的应用程序有权发送请求。 在此处阅读有关授权标头的更多信息( headers.Authorization
)。 Authorization
标头的语法:
Authorization:
- In our case: 在我们的情况下:
Authorization: Basic apikey:API_KEY
The API_KEY
must be base64 encoded. Follow this example.
API_KEY
必须是base64编码的。 请遵循此示例 。
After encoding:
编码后:
Authorization: `Basic ${Buffer.from(`apikey:${API_KEY}`).toString(‘base64’)}`
To find API_KEY
:
要找到API_KEY
:
On your Mailchimp dashboard, go to
Account > Extras > API keys > Your
API keys在Mailchimp仪表板上,转到
Account > Extras > API keys > Your
API密钥Your API key may look like
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us17
您的API密钥可能类似于
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us17
Where are we going to store listId
and API_KEY
values? You can store all environmental variable in a .env
file and manage them with the dotenv package. However, to stay focused in this tutorial, we add values directly to our server/mailchimp.js
file:
我们将在哪里存储listId
和API_KEY
值? 您可以将所有环境变量存储在.env
文件中,并使用dotenv软件包进行管理。 但是,为了专注于本教程,我们将值直接添加到我们的server/mailchimp.js
文件中:
const listId = 'xxxxxxxxxx';const API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us17';
Plug in the above code snippets:
插入以上代码片段:
import request from 'request';
export async function subscribe({ email }) { const data = { email_address: email, status: 'subscribed', };
const listId = 'xxxxxxxxxx'; const API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us17';
await new Promise((resolve, reject) => { request.post( { uri: `https://us17.api.mailchimp.com/3.0/lists/${listId}/members/`, headers: { Accept: 'application/json', Authorization: `Basic ${Buffer.from(`apikey:${API_KEY}`).toString('base64')}`, }, json: true, body: data, }, (err, response, body) => { if (err) { reject(err); } else { resolve(body); } }, ); });}
Remember to add real values for listId
and API_KEY
.
记住要为listId
和API_KEY
添加实际值。
测试中 (Testing)
It’s time to test out the entire MailChimp subscription flow.
现在该测试整个MailChimp订阅流程了。
We exported our subscribe
method from server/mailchimp.js
, but we haven't imported/added this method to the Express route at server/app.js
. To do so:
我们出口我们的subscribe
从方法server/mailchimp.js
,但我们并没有进口/添加在此方法快速通道server/app.js
。 为此:
Import to
server/app.js
with:使用以下命令导入到
server/app.js
:
import { subscribe } from ‘./mailchimp’;
Add an
async/await
construct to the Express route, so we call and wait for thesubscribe
method. Modify the following snippet of code like this:向Express路由添加
async/await
构造,因此我们调用并等待subscribe
方法。 修改如下代码片段:
server.post('/api/v1/public/subscribe', async (req, res) => { const { email } = req.body; if (!email) { res.json({ error: 'Email is required' }); return; }
try { await subscribe({ email }); res.json({ subscribed: 1 }); console.log(email); } catch (err) { res.json({ error: err.message || err.toString() }); }});
We were able to use await
for subscribe
because this method returns a Promise. Recall the definition of subscribe
— it has a line with new Promise()
.
我们能够使用await
进行subscribe
因为此方法返回了Promise。 回想定义subscribe
-它有一个符合new Promise()
Let’s add a console.log
statement to the onSubmit
function from pages/subscribe.js
. Open your pages/subscribe.js
file and add console.log
like this:
让我们从pages/subscribe.js
向onSubmit
函数添加console.log
语句。 打开pages/subscribe.js
文件,并添加console.log
如下所示:
try { await subscribeToNewsletter({ email });
if (this.emailInput) { this.emailInput.value = ''; } NProgress.done(); console.log('email was successfully added to Mailchimp list');} catch (err) { console.log(err); //eslint-disable-line NProgress.done();}
At this point, we can skip testing with Postman. Instead, let’s start our app, fill out the form, submit the form, and check if the email was added to the Mailchimp list. Also, we will see the output of our browser console.
此时,我们可以跳过对Postman的测试。 相反,让我们启动应用程序,填写表格,提交表格,然后检查电子邮件是否已添加到Mailchimp列表中。 另外,我们将看到浏览器控制台的输出。
Start your app with yarn dev
. Go to http://localhost:8000/subscribe
. Take a look at the empty list on your Mailchimp dashboard:
用yarn dev
启动您的应用程序。 转到http://localhost:8000/subscribe
。 查看Mailchimp仪表板上的空白列表:
Fill out the form and click Subscribe
. Refresh the page with the Mailchimp list:
填写表格,然后单击“ Subscribe
。 用Mailchimp列表刷新页面:
And the browser console prints:
浏览器控制台将输出:
In case you are not running the app locally, you can test on the app I deployed for this tutorial: https://mailchimp.builderbook.org/subscribe. You’ll get a test email to confirm that MailChimp API worked.
如果您不在本地运行该应用程序,则可以在我为本教程部署的应用程序上进行测试: https : //mailchimp.builderbook.org/subscribe 。 您会收到一封测试电子邮件,以确认MailChimp API是否有效。
Boom! You just learned two powerful skills: building internal and external APIs for your JavaScript web application.
繁荣! 您刚刚学习了两项强大的技能:为JavaScript Web应用程序构建内部和外部API。
When you complete this tutorial, your code should match code in the 1-end folder. This folder is located in the tutorials
directory of our builderbook repo.
完成本教程后,您的代码应与1端文件夹中的代码匹配。 该文件夹位于我们的builderbook repo的tutorials
目录中。
If you found this article useful, consider giving a star to our Github repo and checking out our book where we cover this and many other topics in detail.
如果您认为这篇文章很有用,请考虑给我们的Github回购加星号,并查看我们的书 ,其中详细介绍了此主题和许多其他主题。
If you are building a software product, check out our SaaS boilerplate and Async (team communication philosophy and tool for small teams of software engineers).
如果您要开发软件产品,请查看我们的SaaS样板和Async (面向小型软件工程师团队的团队沟通理念和工具)。
翻译自: https://www.freecodecamp.org/news/how-to-integrate-mailchimp-in-a-javascript-web-app-2a889fb43f6f/
mailchimp教程