掘金 后端 ( ) • 2024-04-26 17:59

前言

本文是基于小满zs的nest教学视频的个人学习笔记,大家感兴趣可以去看看原文 https://xiaoman.blog.csdn.net/article/details/126212760

我们通过axios库发送http请求,首先安装axios

npm i axios -S

然后定义一个控制器 Controller

class Controller {
	constructor(){
	
	}
	getList(){
	
	}
}

现在我希望Controller的getList()方法可以获取后端API返回的list,同时通过装饰器@GET去发送http请求,getList()方法中不包含任何与网络请求有关的代码。

那么我们要做的第一步,将后端API的URL作为参数传入装饰器函数中,再由装饰器函数发起axios请求,按照正常人想法,既然装饰器本质是一个函数,那我能不能直接将URL作为参数,传入装饰器函数中呢?

const GET:MethodDecorator = (target, key, scriptor, URL:string)=>{
	cosnole.log(URL)
}

class Controller{
	constructor(){}
	
    @GET("https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10")
    getList() {

    }
}

image.png 可以发现代码报错,MethodDecorator函数类型的参数是固定的,不能随便添加。 因为TS严格约束数据类型,因此通过|来添加参数的方法并不可取,至少这种投机取巧的方式用的多了,项目便很难维护。所以现在,是高阶函数出场的时候了。

既然我们要将URL作为参数传入装饰器函数,但是装饰器函数不能接收新的参数。我们不妨在外面再套一层函数,最外面的一层函数接收URL,然后返回装饰器函数。由于装饰器函数引用了外层函数的URL,形成了一个闭包!我们的问题完美解决了

const GET = (URL: string):MethodDecorator=>{
	return (target, key, scriptor)=>{
		console.log(URL)
	}
}

class Controller{
	constructor(){}
	
    @GET("https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10")
    getList() {

    }
}

image.png

这种高阶函数就叫做装饰器工厂,装饰器的本质就是一个高阶函数。

接下来我们就可以继续编写装饰器函数中的逻辑:发送HTTP Get请求,然后将结果返回给装饰的getList()函数

这里我们复习一下方法装饰器函数的三个默认参数

  • 原型对象
  • 方法名称
  • 属性描述符
    • 可写:writable
    • 可枚举:enumerable
    • 可配置:configurable
    • ?value:对应的函数

所以我们可以通过descriptor属性描述符的value属性,获取到装饰的函数

import axios from "axios";

const GET = (url: string): MethodDecorator => {
    return (target, key, descriptor) => {
        //获取到装饰的函数
        const fn = descriptor.value as Function;
        //自定义参数status,用来传递状态码
        let status = 0
        //发送get请求
        axios.get(url).then(res => {
            //如果get请求发送成功,将返回的结果和其他自定义参数传递给getList函数
            status = 200
            fn(res, status)
        }).catch(err => {
            status = 500
            fn(err, status)
        })
    }
}

然后我们使用@Get装饰getList方法,然后再getList函数里接收返回的参数。这句话是什么意思呢?我来解释给你听: 我们再axios发起Get请求后,调用了fn()函数,并往里面传递了一系列参数

        axios.get(url).then(res => {
            //如果get请求发送成功,将返回的结果和其他自定义参数传递给getList函数
            status = 200
            fn(res, status)
        }).catch(err => {
            status = 500
            fn(err, status)
        })

而fn()函数是我们从方法装饰器的属性描述符中获取的

        //获取到装饰的函数
        const fn = descriptor.value as Function;

结合起来就是,fn()函数接收到的参数,会传递给原方法,即被getList()函数接收

class Controller {
    constructor() { }

    @GET("https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10")
    //注意这里,我定义了两个参数,这两个参数就是从@GET装饰器传来的
    getList(res: any, status: number) {
        console.log(res.data)
        console.log(status)
    }
}

这样通过装饰器实现了,将发送HTTP Get请求的逻辑全部集中到@GET返回的装饰器函数,将Get请求返回的结果处理逻辑,全部集中到了getlist()方法中。

总结

我们通过定义装饰器工厂函数(高阶函数),解决了向装饰器传递自定义参数的问题 通过装饰器,成功将发送Get请求逻辑和处理Get请求逻辑分开