Honey Butter Garlic Chicken Bites with Cheesy Alfredo Twisted Pasta

<|begin▁of▁sentence|># 1. 前言

在之前的文章中,我们介绍了`axios`的拦截器,并使用`TS`对拦截器进行了简单实现。了解了拦截器之后,本章我们来为`axios`实现添加拦截器的功能,即实现`axios.interceptors.request`和`axios.interceptors.response`对象以及这两个对象上的`use`和`eject`方法。

# 2. 需求分析

通过官方文档我们知道:`axios`对象上有一个`interceptors`对象属性,该属性又有两个属性:`request`和`response`,它们都是拦截器管理器,并且这两个属性都有`use`和`eject`方法。`use`方法用于添加拦截器,它接收两个参数,第一个参数是`Promise`成功态要执行的函数,第二个参数是`Promise`失败态要执行的函数,并且都会返回一个`id`用于删除该拦截器;`eject`方法用于删除拦截器,它接收一个参数,即拦截器的`id`。

“`javascript
// 添加一个请求拦截器
axios.interceptors.request.use(
function (config) {
// 在发送请求之前做些什么
return config;
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error);
}
);

// 添加一个响应拦截器
axios.interceptors.response.use(
function (response) {
// 对响应数据做点什么
return response;
},
function (error) {
// 对响应错误做点什么
return Promise.reject(error);
}
);
“`

另外,如果同时添加多个拦截器,那么拦截器的执行顺序是:对于请求拦截器,后添加的拦截器先执行;对于响应拦截器,先添加的拦截器先执行。

“`javascript
// 添加两个请求拦截器
axios.interceptors.request.use(
function (config) {
console.log(“请求拦截器1”);
return config;
},
function (error) {
return Promise.reject(error);
}
);
axios.interceptors.request.use(
function (config) {
console.log(“请求拦截器2”);
return config;
},
function (error) {
return Promise.reject(error);
}
);

// 添加两个响应拦截器
axios.interceptors.response.use(
function (response) {
console.log(“响应拦截器1”);
return response;
},
function (error) {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
console.log(“响应拦截器2”);
return response;
},
function (error) {
return Promise.reject(error);
}
);
“`

当发送一个请求时,控制台会依次打印:

“`javascript
请求拦截器2;
请求拦截器1;
响应拦截器1;
响应拦截器2;
“`

# 3. 实现思路

根据需求分析,我们可以知道:

– 我们需要在`axios`对象上添加`interceptors`对象属性,该属性又有`request`和`response`两个属性,它们都是拦截器管理器;
– 拦截器管理器是一个类,我们可以实现一个`InterceptorManager`类,该类上有`use`和`eject`方法;
– `use`方法接收两个参数,分别是`Promise`成功和失败的回调函数,并且返回一个`id`用于删除该拦截器;
– `eject`方法接收一个参数,即拦截器的`id`,用于删除该拦截器;
– 另外,在`InterceptorManager`类内部,我们还需要维护一个数组,用来存储添加的拦截器;

OK,思路已经清晰了,接下来我们就按照思路一步步实现。

# 4. 代码实现

## 4.1 定义拦截器管理器类

首先,我们创建一个`interceptorManager.ts`文件,在该文件中实现`InterceptorManager`类。

“`typescript
// src/core/interceptorManager.ts

import { ResolvedFn, RejectedFn } from “../types”;

interface Interceptor {
resolved: ResolvedFn;
rejected?: RejectedFn;
}

export default class InterceptorManager {
private interceptors: Array | null>;

constructor() {
this.interceptors = [];
}

use(resolved: ResolvedFn, rejected?: RejectedFn): number {
this.interceptors.push({
resolved,
rejected,
});
return this.interceptors.length – 1;
}

eject(id: number): void {
if (this.interceptors[id]) {
this.interceptors[id] = null;
}
}
}
“`

我们定义了一个`InterceptorManager`泛型类,内部维护了一个私有属性`interceptors`,它是一个数组,用来存储拦截器。该类还对外提供了两个方法`use`和`eject`:

– `use`方法接收两个参数,`resolved`和`rejected`,其实就是`Promise`的成功和失败的回调函数,我们把这两个函数组成一个对象`Interceptor`并`push`到`interceptors`中,并且返回它在`interceptors`中的索引`id`。
– `eject`方法接收一个参数`id`,用于删除`interceptors`中对应`id`的拦截器,注意我们并不是把它从数组中删除,而是置为`null`,这样我们后面在真正执行拦截器的时候,可以通过判断是否为`null`来确定是否需要执行,这样做不会影响到`interceptors`里后续其他拦截器的执行。

另外,我们还定义了`Interceptor`接口,它描述了单个拦截器的类型。

## 4.2 在 Axios 类中创建 interceptors 对象

我们给`Axios`类添加`interceptors`属性,该属性又有两个属性:`request`和`response`,它们的类型都是`InterceptorManager`。

“`typescript
// src/core/Axios.ts

import { InterceptorManager } from “./InterceptorManager”;

export default class Axios {
interceptors: {
request: InterceptorManager;
response: InterceptorManager;
};

constructor() {
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
};
}
}
“`

## 4.3 修改 Axios 类 request 方法

在之前的文章中,我们实现了`Axios`类的`request`方法,并且在该方法内部通过`Promise`链式调用的方式实现了发送请求。现在,我们要在发送请求的`Promise`链中添加上请求拦截器和响应拦截器的执行。

我们首先需要把`Axios`类中`interceptors`对象中`request`和`response`属性上添加的拦截器拿出来,并且按照一定的顺序把它们插入到`Promise`链中。

“`typescript
// src/core/Axios.ts

import { InterceptorManager } from “./InterceptorManager”;

interface PromiseChain {
resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise);
rejected?: RejectedFn;
}

export default class Axios {
interceptors: {
request: InterceptorManager;
response: InterceptorManager;
};

constructor() {
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
};
}

request(url: any, config?: any): AxiosPromise {
if (typeof url === “string”) {
if (!config) {
config = {};
}
config.url = url;
} else {
config = url;
}

const chain: PromiseChain[] = [
{
resolved: dispatchRequest,
rejected: undefined,
},
];

this.interceptors.request.forEach((interceptor) => {
chain.unshift(interceptor);
});

this.interceptors.response.forEach((interceptor) => {
chain.push(interceptor);
});

let promise = Promise.resolve(config);

while (chain.length) {
const { resolved, rejected } = chain.shift()!;
promise = promise.then(resolved, rejected);
}

return promise;
}
}
“`

首先,我们定义了一个`PromiseChain`接口,它描述了`Promise`链中每个节点的类型。然后,我们创建了一个数组`chain`,并把`dispatchRequest`函数(该函数用于发送请求)放在数组的第一个位置。

接下来,我们遍历`interceptors.request`拦截器,并使用`unshift`方法把每个拦截器插入到`chain`数组的前面,这样就能保证后添加的请求拦截器先执行。

然后,我们遍历`interceptors.response`拦截器,并使用`push`方法把每个拦截器插入到`chain`数组的后面,这样就能保证先添加的响应拦截器先执行。

最后,我们初始化一个`Promise`,这个`Promise`会`resolve`我们传入的`config`,然后通过`while`循环依次取出`chain`数组中的节点,并将其拼接到`Promise`链上。

注意:我们上面在实现`InterceptorManager`类的时候,内部`interceptors`数组存储的拦截器有可能是`null`,所以我们在遍历的时候需要把为`null`的拦截器过滤掉。因此,我们需要给`InterceptorManager`类添加一个`forEach`方法,用于遍历所有非`null`的拦截器。

“`typescript
// src/core/interceptorManager.ts

import { ResolvedFn, RejectedFn } from “../types”;

interface Interceptor {
resolved: ResolvedFn;
rejected?: RejectedFn;
}

export default class InterceptorManager {
private interceptors: Array | null>;

constructor() {
this.interceptors = [];
}

use(resolved: ResolvedFn, rejected?: RejectedFn): number {
this.interceptors.push({
resolved,
rejected,
});
return this.interceptors.length – 1;
}

forEach(fn: (interceptor: Interceptor) => void): void {
this.interceptors.forEach((interceptor) => {
if (interceptor !== null) {
fn(interceptor);
}
});
}

eject(id: number): void {
if (this.interceptors[id]) {
this.interceptors[id] = null;
}
}
}
“`

我们给`InterceptorManager`类添加了`forEach`方法,它接收一个函数作为参数,该方法内部会遍历所有非`null`的拦截器,并以该拦截器作为参数调用传入的函数。

# 5. 编写 demo

“`javascript
// examples/addInterceptors/index.html





addInterceptors demo

addInterceptors demo

请求拦截器:后添加的先执行;响应拦截器:先添加的先执行




“`

“`javascript
// examples/addInterceptors/app.ts

import axios from “../../src/index”;

// 添加两个请求拦截器
axios.interceptors.request.use(
(config) => {
config.headers.test += “requestInterceptor1—“;
return config;
},
(error) => {
return Promise.reject(error);
}
);
axios.interceptors.request.use(
(config) => {
config.headers.test += “requestInterceptor2—“;
return config;
},
(error) => {
return Promise.reject(error);
}
);

// 添加两个响应拦截器
axios.interceptors.response.use(
(res) => {
res.data += “responseInterceptor1—“;
return res;
},
(error) => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(res) => {
res.data += “responseInterceptor2—“;
return res;
},
(error) => {
return Promise.reject(error);
}
);

document.getElementById(“send”)?.addEventListener(“click”, function () {
axios({
url: “/api/addInterceptors”,
method: “get”,
headers: {
test: “”,
},
}).then((res) => {
console.log(res.data);
});
});
“`

该`demo`中,我们添加了两个请求拦截器和两个响应拦截器,并且在请求拦截器中给请求配置对象的`headers`属性添加了字符串,在响应拦截器中给响应数据的`data`属性添加了字符串。当我们点击发送请求按钮时,我们会在控制台看到打印出的响应数据。

另外,我们在服务端也编写了对应的接口:

“`javascript
// examples/server.js

const router = express.Router();

router.get(“/api/addInterceptors”, function (req, res) {
res.json({
msg: “hello world”,
});
});
“`

# 6. 运行 demo 并查看结果

我们按照之前的方法运行项目,然后打开浏览器,点击发送请求按钮,我们会在控制台看到如下打印:

![](~@/axios/13/01.png)

从图中可以看到,我们发送的请求配置对象的`headers`属性中已经添加了两个请求拦截器添加的字符串,并且是后添加的请求拦截器先执行;响应数据的`data`属性中也已经添加了两个响应拦截器添加的字符串,并且是先添加的响应拦截器先执行,结果符合我们的预期。

OK,拦截器添加功能就已经实现完毕了。

Print
clock clock iconcutlery cutlery iconflag flag iconfolder folder iconinstagram instagram iconpinterest pinterest iconfacebook facebook iconprint print iconsquares squares iconheart heart iconheart solid heart solid icon

Honey Butter Garlic Chicken Bites with Cheesy Alfredo Twisted Pasta


5 Stars 4 Stars 3 Stars 2 Stars 1 Star

No reviews

  • Author: Chef Billy

Description

Tender, juicy chicken bites glazed in a sweet and savory honey butter garlic sauce, served over twisted pasta coated in a rich, cheesy Alfredo sauce.


Ingredients

Scale

For the Crust:

  • 1 lb boneless, skinless chicken breast, cut into bite-sized pieces
  • 8 oz twisted pasta (such as fusilli or rotini)
  • 4 tbsp butter, divided
  • 4 cloves garlic, minced
  • 3 tbsp honey
  • 1 cup heavy cream
  • 1/2 cup grated Parmesan cheese
  • 1/4 cup shredded mozzarella cheese
  • Salt and black pepper to taste
  • 1 tbsp olive oil
  • Fresh parsley, chopped (for garnish)

Instructions

1. Prepare the Crust:

  1. Cook the twisted pasta according to package instructions until al dente. Drain and set aside.
  2. Season chicken bites with salt and pepper. Heat olive oil in a large skillet over medium-high heat. Add chicken and cook until golden and cooked through, about 6-8 minutes. Remove chicken and set aside.
  3. In the same skillet, melt 2 tbsp butter over medium heat. Add garlic and sauté until fragrant, about 1 minute. Stir in honey and remaining 2 tbsp butter until combined. Return chicken to the skillet and toss to coat in the honey butter garlic sauce. Remove from heat.
  4. In a separate saucepan, heat heavy cream over medium heat until it begins to simmer. Reduce heat to low, stir in Parmesan and mozzarella until melted and smooth. Season with salt and pepper. Toss the cooked pasta in the Alfredo sauce until well coated.
  5. Serve the honey butter garlic chicken bites over the cheesy Alfredo twisted pasta. Garnish with fresh parsley.

Notes

You can customize the seasonings to taste.

Share it :

Leave a Comment

Recipe rating 5 Stars 4 Stars 3 Stars 2 Stars 1 Star

Billy Robert

Pro Chef & Blogger

HI! I’m Billy. Cookbook author,
Father of 1 and cookie fan.
On my food blog you’ll find easy
and delicious recipes.

Billy Robert

From Novice to Chef
Discover all our recipes and articles to level up your cooking skills !
Welcome to my kitchen, where time around the table is more important than what is on it. Join me in making easy, delicious recipes your whole family will love.
Copyright © 2024 Billyrecipes, All rights reserved. Powered by Billyrecipes.com