Node.js控制器Controller使用教程
Controller 控制器
控制器負(fù)責(zé)處理傳入的請(qǐng)求并向客戶返回響應(yīng)。
一個(gè)控制器的目的是接收應(yīng)用程序的特定請(qǐng)求。路由機(jī)制控制哪個(gè)控制器接收哪些請(qǐng)求。通常,每個(gè)控制器有一個(gè)以上的路由,不同的路由可以執(zhí)行不同的動(dòng)作。
為了創(chuàng)建一個(gè)基本的控制器,我們使用類和裝飾器。裝飾器將類與所需的元數(shù)據(jù)聯(lián)系起來,并使 Nest 能夠創(chuàng)建一個(gè)路由圖(將請(qǐng)求綁定到相應(yīng)的控制器)。
為了快速創(chuàng)建一個(gè)內(nèi)置驗(yàn)證的 CRUD 控制器,你可以使用 CLI 的 CRUD 生成器:nest g resource [name]
路由
在下面的例子中,我們將使用@Controller()
裝飾器,這是定義一個(gè)基本控制器所需要的。我們將指定一個(gè)可選的路由路徑前綴為cats
。在@Controller()
裝飾器中使用路徑前綴,可以讓我們輕松地將一組相關(guān)的路由分組,并盡量減少重復(fù)的代碼。例如,我們可以選擇將一組管理與客戶實(shí)體互動(dòng)的路由歸入路由/customers
。在這種情況下,我們可以在@Controller()
裝飾器中指定路徑前綴customers
,這樣我們就不必為文件中的每個(gè)路由重復(fù)這部分的路徑。
import { Controller, Get } from "@nestjs/common"; @Controller("cats") export class CatsController { @Get() findAll(): string { return "This action returns all cats"; } }
要使用 CLI 創(chuàng)建一個(gè)控制器,只需執(zhí)行$ nest g controller cats
命令。
findAll()
方法之前的@Get()
HTTP 請(qǐng)求方法裝飾器,告訴 Nest 為 HTTP 請(qǐng)求的特定端點(diǎn)創(chuàng)建一個(gè)處理程序。端點(diǎn)與 HTTP 請(qǐng)求方法(本例中為 GET)和路由路徑相對(duì)應(yīng)。
什么是路由路徑?處理程序的路徑是通過連接控制器聲明@Controller('cats')
中的cats
,以及方法裝飾器@Get()
中指定的任何路徑來確定的。由于我們已經(jīng)為每個(gè)路由(cats)聲明了一個(gè)前綴,并且沒有在裝飾器@Get()
中添加任何路徑信息,Nest 就會(huì)將GET /cats
請(qǐng)求映射到findAll()的Get方法
上來。
如前所述,路徑包括@Controller()
路徑前綴和請(qǐng)求方法findAll()
裝飾器@Get()
中配置的路徑。例如,@Controller('customers')
的路徑前綴與裝飾器@Get('profile')
相結(jié)合,將產(chǎn)生一個(gè)GET /customers/profile
這樣的路由映射請(qǐng)求。
在我們上面的例子中,當(dāng)向該端點(diǎn)發(fā)出 GET 請(qǐng)求時(shí),Nest 將該請(qǐng)求路由到我們用戶定義的 findAll()方法。注意,我們?cè)谶@里選擇的方法名稱是完全任意的。顯然,我們必須聲明一個(gè)方法來綁定路由,但 Nest 并不重視所選擇的方法名稱的任何意義。
這個(gè)方法將返回一個(gè) 200 狀態(tài)代碼和相關(guān)的響應(yīng),在這種情況下,它只是一個(gè)字符串。為什么會(huì)發(fā)生這種情況?為了解釋,我們首先要介紹一個(gè)概念,即 Nest 采用了兩種不同的選項(xiàng)來操作響應(yīng)。
選項(xiàng) | 描述 |
---|---|
Standard (recommended) | 使用這種內(nèi)置方法,當(dāng)請(qǐng)求處理程序返回一個(gè) JavaScript 對(duì)象或數(shù)組時(shí),它將自動(dòng)被序列化為 JSON。然而,當(dāng)它返回一個(gè) JavaScript 原始類型(例如,字符串、數(shù)字、布爾值)時(shí),Nest 將只發(fā)送值,而不嘗試對(duì)其進(jìn)行序列化。這使得響應(yīng)處理變得簡(jiǎn)單:只需返回值,Nest 就會(huì)處理其余的事情。此外,響應(yīng)的狀態(tài)代碼默認(rèn)總是 200,除了使用 201 的 POST 請(qǐng)求。我們可以通過在處理程序級(jí)別添加@HttpCode(…)裝飾器來輕松改變這一行為(見狀態(tài)代碼)。 |
Library-specific | 我們可以使用庫特定的(例如 Express)響應(yīng)對(duì)象,它可以使用方法處理簽名中的@Res()裝飾器注入(例如 findAll(@Res() response))。通過這種方法,你有能力使用該對(duì)象所暴露的本地響應(yīng)處理方法。例如,在 Express 中,你可以使用 response.status(200).send()這樣的代碼來構(gòu)造響應(yīng)。 |
警告:
NEST 會(huì)檢測(cè)處理程序是否使用@Res()或@Next(),表明你選擇了庫的特定選項(xiàng)。如果同時(shí)使用這兩種方法,標(biāo)準(zhǔn)方法將自動(dòng)禁用于該單一路由,并不再按預(yù)期工作。要同時(shí)使用這兩種方法(例如,通過注入響應(yīng)對(duì)象只設(shè)置 cookie/頭文件,但仍將其余部分留給框架),你必須在@Res({ passthrough: true })裝飾器中將 passthrough 選項(xiàng)設(shè)置為 true。
請(qǐng)求對(duì)象
處理程序經(jīng)常需要訪問客戶端的請(qǐng)求細(xì)節(jié)。Nest 提供了對(duì)底層平臺(tái)(默認(rèn)為 Express)的請(qǐng)求對(duì)象的訪問。我們可以通過在處理程序的簽名中添加@Req()裝飾器來指示 Nest 注入請(qǐng)求對(duì)象來訪問它。
import { Controller, Get, Req } from "@nestjs/common"; import { Request } from "express"; @Controller("cats") export class CatsController { @Get() findAll(@Req() request: Request): string { return "This action returns all cats"; } }
為了利用表達(dá)式類型的優(yōu)勢(shì)(如上面的 request: Request 參數(shù)的例子),請(qǐng)安裝@types/express 包。
請(qǐng)求對(duì)象代表了 HTTP 請(qǐng)求,并有請(qǐng)求查詢字符串、參數(shù)、HTTP 頭和正文的屬性(在此閱讀更多內(nèi)容)。在大多數(shù)情況下,沒有必要手動(dòng)抓取這些屬性。我們可以使用專門的裝飾器來代替,比如@Body()
或@Query()
,這些裝飾器開箱即用。下面是一個(gè)所提供的裝飾器的列表,以及它們所代表的普通平臺(tái)特定對(duì)象。
裝飾器 | 特定對(duì)象 |
---|---|
@Request(), @Req() | req |
@Response(), @Res()* | res |
@Next() | next |
@Session() | req.session |
@Param(key?: string) | req.params / req.params[key] |
@Body(key?: string) | req.body / req.body[key] |
@Query(key?: string) | req.query / req.query[key] |
@Headers(name?: string) | req.headers / req.headers[name] |
@Ip() | req.ip |
@HostParam() | req.hosts |
為了與跨底層 HTTP 平臺(tái)(例如Express
和Fastify
)的類型兼容,Nest 提供了@Res()
和@Response()
裝飾器。@Res()
是 @Response()
的簡(jiǎn)單別名。兩者都直接暴露了底層的本地平臺(tái)響應(yīng)對(duì)象接口。當(dāng)使用它們時(shí),你也應(yīng)該導(dǎo)入底層庫的類型(例如,@types/express
)以充分利用。請(qǐng)注意,當(dāng)你在方法處理程序中注入@Res()
或@Response()
時(shí),你將 Nest 放入該處理程序的庫特定模式中,并且你將負(fù)責(zé)管理響應(yīng)。當(dāng)這樣做時(shí),你必須通過調(diào)用響應(yīng)對(duì)象(如 res.json(...)
或 res.send(...)
)來發(fā)出某種響應(yīng),否則 HTTP 服務(wù)器會(huì)掛起。
如何定義自己的裝飾器,請(qǐng)學(xué)習(xí)這個(gè)章節(jié)
資源
早些時(shí)候,我們定義了一個(gè)方法來獲取cats
的資源(GET 路由)。我們通常也想提供一個(gè)創(chuàng)建新記錄的方法。為此,讓我們創(chuàng)建一個(gè) POST 方法。
import { Controller, Get, Post } from "@nestjs/common"; @Controller("cats") export class CatsController { @Post() create(): string { return "This action adds a new cat"; } @Get() findAll(): string { return "This action returns all cats"; } }
就是這么簡(jiǎn)單。Nest 為所有的標(biāo)準(zhǔn) HTTP 方法提供裝飾器。 @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), 和 @Head()
。此外,@All()
定義了一個(gè)可以處理所有這些方法的端點(diǎn)。
路由通配符
基于路由的模式也被支持。例如,*
被用作通配符,將匹配任何字符的組合。
@Get('ab*cd') findAll() { return 'This route uses a wildcard'; }
ab*cd
路由路徑將匹配 abcd、ab_cd、abecd,等等。字符"?“、”+“、”*“和”()"可以在路徑中使用,它們是對(duì)應(yīng)于正則表達(dá)式的子集。連字符(-)和點(diǎn)(.)可以通過基于字符串的路徑進(jìn)行字面解釋。
狀態(tài)代碼
如前所述,響應(yīng)狀態(tài)代碼默認(rèn)總是 200,除了 POST 請(qǐng)求是 201。我們可以通過在處理程序級(jí)別添加@HttpCode(...)
裝飾器來輕松改變這一行為。
@Post() @HttpCode(204) create() { return 'This action adds a new cat'; }
從@nestjs/common
包導(dǎo)入HttpCode
通常,你的狀態(tài)代碼不是靜態(tài)的,而是取決于各種因素。在這種情況下,你可以使用一個(gè)庫特定的響應(yīng)(使用@Res()注入)對(duì)象(或者,在出現(xiàn)錯(cuò)誤時(shí),拋出一個(gè)異常)。
頭信息
要指定一個(gè)自定義的響應(yīng)頭,你可以使用@Header()
裝飾器或一個(gè)庫特定的響應(yīng)對(duì)象(并直接調(diào)用res.header()
)。
@Post() @Header('Cache-Control', 'none') create() { return 'This action adds a new cat'; }
從@nestjs/common
包導(dǎo)入Header
重定向
要將一個(gè)響應(yīng)重定向到一個(gè)特定的 URL,你可以使用@Redirect()
裝飾器或一個(gè)庫特定的響應(yīng)對(duì)象(并直接調(diào)用res.redirect()
)。
@Redirect()
需要兩個(gè)參數(shù),url
和statusCode
,都是可選的。如果省略的話,statusCode
的默認(rèn)值是302
。
@Get() @Redirect('https://nestjs.com', 301)
有時(shí)你可能想動(dòng)態(tài)地確定 HTTP 狀態(tài)代碼或重定向 URL。通過從路由處理方法中返回一個(gè)對(duì)象來做到這一點(diǎn),類似如下。
{ "url": string, "statusCode": number }
返回的值將覆蓋傳遞給@Redirect()裝飾器的任何參數(shù)。比如說。
@Get('docs') @Redirect('https://docs.nestjs.com', 302) getDocs(@Query('version') version) { if (version && version === '5') { return { url: 'https://docs.nestjs.com/v5/' }; } }
路由參數(shù)
當(dāng)你需要接受動(dòng)態(tài)數(shù)據(jù)作為請(qǐng)求的一部分時(shí),帶有靜態(tài)路徑的路由將無法工作(例如,GET /cats/1 以獲得 id 為 1 的貓)。為了定義帶參數(shù)的路由,我們可以在路由的路徑中添加路由參數(shù)令牌,以捕獲請(qǐng)求 URL 中該位置的動(dòng)態(tài)值。下面@Get()裝飾器例子中的路由參數(shù)令牌展示了這種用法。以這種方式聲明的路由參數(shù)可以使用@Param()裝飾器進(jìn)行訪問,它應(yīng)該被添加到方法簽名中。
@Get(':id') findOne(@Param() params): string { console.log(params.id); return `This action returns a #${params.id} cat`; }
@Param()
被用來裝飾一個(gè)方法參數(shù)(上面例子中的params
),并使路由參數(shù)作為該方法主體中被裝飾的方法參數(shù)的屬性可用。正如上面的代碼所見,我們可以通過引用params.id
來訪問id參數(shù)
。你也可以向裝飾器傳遞一個(gè)特定的參數(shù)標(biāo)記,然后在方法主體中直接引用路由參數(shù)的名稱。
從@nestjs/common
包導(dǎo)入Param
@Get(':id') findOne(@Param('id') id: string): string { return `This action returns a #${id} cat`; }
子域路由
@Controller
裝飾器可以接受一個(gè)host
選項(xiàng),要求傳入的請(qǐng)求的 HTTP 主機(jī)與某些特定的值相匹配。
@Controller({ host: "admin.example.com" }) export class AdminController { @Get() index(): string { return "Admin page"; } }
由于 Fastify 缺乏對(duì)嵌套路由器的支持,當(dāng)使用子域路由時(shí),應(yīng)該使用(默認(rèn))Express 適配器來代替
與路由路徑類似,hosts
選項(xiàng)可以使用令牌來捕獲主機(jī)名稱中該位置的動(dòng)態(tài)值。下面@Controller()
裝飾器例子中的主機(jī)參數(shù)令牌展示了這種用法。以這種方式聲明的主機(jī)參數(shù)可以使用@HostParam()
裝飾器進(jìn)行訪問,它應(yīng)該被添加到方法簽名中。
@Controller({ host: ":account.example.com" }) export class AccountController { @Get() getInfo(@HostParam("account") account: string) { return account; } }
范疇
對(duì)于來自不同編程語言背景的人來說,要知道在 Nest 中,幾乎所有的東西都是在傳入的請(qǐng)求中共享的,這可能是意想不到的。我們有一個(gè)到數(shù)據(jù)庫的連接池,有全局狀態(tài)的單體服務(wù),等等。請(qǐng)記住,Node.js 并不遵循請(qǐng)求/響應(yīng)的多線程無狀態(tài)模型,其中每個(gè)請(qǐng)求都由一個(gè)單獨(dú)的線程來處理。因此,使用單體實(shí)例對(duì)我們的應(yīng)用程序是完全安全的。
但是,在極端情況下,基于請(qǐng)求的控制器生存周祁可能是所需的行為(However, there are edge-cases when request-based lifetime of the controller may be the desired behavior),例如 GraphQL 應(yīng)用程序中的每個(gè)請(qǐng)求緩存,請(qǐng)求跟蹤或多租戶。在這里了解如何控制作用域。
異步性
我們熱愛現(xiàn)代 JavaScript,我們知道數(shù)據(jù)提取大多是異步的。這就是為什么 Nest 支持并能很好地使用異步函數(shù)。
學(xué)習(xí)async / await
請(qǐng)點(diǎn)擊這里
每個(gè)異步函數(shù)都必須返回一個(gè) Promise。這意味著你可以返回一個(gè)延遲值,Nest 將能夠自己解決。讓我們來看看這個(gè)例子。
@Get() async findAll(): Promise<any[]> { return []; }
上述代碼是完全有效的。此外,Nest 路由處理程序通過能夠返回 RxJS 可觀察流而變得更加強(qiáng)大。Nest 將自動(dòng)訂閱下面的源并獲取最后一個(gè)發(fā)出的值(一旦流完成)–( Nest will automatically subscribe to the source underneath and take the last emitted value (once the stream is completed))
@Get() findAll(): Observable<any[]> { return of([]); }
上述兩種方法都有效,你可以使用任何符合你要求的方法。
請(qǐng)求的有效載荷
我們之前的 POST 路由處理器的例子沒有接受任何客戶端參數(shù)。讓我們通過在這里添加@Body()
裝飾器來解決這個(gè)問題。
但首先(如果你使用 TypeScript),我們需要確定 DTO(數(shù)據(jù)傳輸對(duì)象)模式。DTO 是一個(gè)定義了數(shù)據(jù)如何在網(wǎng)絡(luò)上發(fā)送的對(duì)象。我們可以通過使用 TypeScript 接口,或通過簡(jiǎn)單的類來確定 DTO 模式。有趣的是,我們?cè)谶@里推薦使用類。為什么呢?類是 JavaScript ES6 標(biāo)準(zhǔn)的一部分,因此它們?cè)诰幾g后的 JavaScript 中被保留為真實(shí)的實(shí)體。另一方面,由于 TypeScript 接口在轉(zhuǎn)譯過程中被移除,Nest 不能在運(yùn)行時(shí)引用它們。這一點(diǎn)很重要,因?yàn)橄窆艿肋@樣的功能在運(yùn)行時(shí)可以訪問變量的元類型,從而實(shí)現(xiàn)更多的可能性。
我們來創(chuàng)建CreateCatDto
類。
export class CreateCatDto { name: string; age: number; breed: string; }
它只有三個(gè)基本屬性。此后我們可以在CatsController
中使用新創(chuàng)建的DTO
。
我們的 Validation Pipe 可以過濾掉那些不應(yīng)該被方法處理程序接收的屬性。在這種情況下,我們可以將可接受的屬性列入白名單,任何不包括在白名單中的屬性都會(huì)自動(dòng)從結(jié)果對(duì)象中剝離。在CreateCatDto
的例子中,我們的白名單是名稱、年齡和品種屬性。在這里了解更多。
處理錯(cuò)誤
這里有一個(gè)關(guān)于處理錯(cuò)誤的單獨(dú)章節(jié)(即,與異常一起工作)。
完整例子
下面是一個(gè)例子,利用幾個(gè)可用的裝飾器來創(chuàng)建一個(gè)基本的控制器。這個(gè)控制器暴露了一些方法來訪問和操作內(nèi)部數(shù)據(jù)。
import { Controller, Get, Query, Post, Body, Put, Param, Delete, } from "@nestjs/common"; import { CreateCatDto, UpdateCatDto, ListAllEntities } from "./dto"; @Controller("cats") export class CatsController { @Post() create(@Body() createCatDto: CreateCatDto) { return "This action adds a new cat"; } @Get() findAll(@Query() query: ListAllEntities) { return `This action returns all cats (limit: ${query.limit} items)`; } @Get(":id") findOne(@Param("id") id: string) { return `This action returns a #${id} cat`; } @Put(":id") update(@Param("id") id: string, @Body() updateCatDto: UpdateCatDto) { return `This action updates a #${id} cat`; } @Delete(":id") remove(@Param("id") id: string) { return `This action removes a #${id} cat`; } }
Nest CLI 提供了一個(gè)自動(dòng)生成器(示意圖),自動(dòng)生成所有的模板代碼,以幫助我們避免做這些事情,并使開發(fā)人員的體驗(yàn)更加簡(jiǎn)單。在這里閱讀更多關(guān)于這個(gè)功能的信息。
開始運(yùn)行
隨著上述控制器的完全定義,Nest 仍然不知道CatsController
的存在,因此不會(huì)創(chuàng)建這個(gè)類的實(shí)例。
控制器總是屬于一個(gè)模塊,這就是為什么我們?cè)?code>@Module()裝飾器中包含控制器數(shù)組。由于我們還沒有定義任何其他模塊,除了根AppModule
,我們將用它來介紹CatsController
。
import { Module } from "@nestjs/common"; import { CatsController } from "./cats/cats.controller"; @Module({ controllers: [CatsController], }) export class AppModule {}
我們使用@Module()
裝飾器將元數(shù)據(jù)附加到模塊類,Nest 現(xiàn)在可以很容易地反映出哪些控制器必須被安裝。
庫的特定方法
到目前為止,我們已經(jīng)討論了操作響應(yīng)的 Nest 標(biāo)準(zhǔn)方式。操作響應(yīng)的第二種方式是使用庫特定的響應(yīng)對(duì)象。為了注入一個(gè)特定的響應(yīng)對(duì)象,我們需要使用 @Res()
裝飾器。為了顯示差異,讓我們把CatsController
重寫成以下內(nèi)容。
import { Controller, Get, Post, Res, HttpStatus } from "@nestjs/common"; import { Response } from "express"; @Controller("cats") export class CatsController { @Post() create(@Res() res: Response) { res.status(HttpStatus.CREATED).send(); } @Get() findAll(@Res() res: Response) { res.status(HttpStatus.OK).json([]); } }
雖然這種方法是可行的,而且事實(shí)上通過提供對(duì)響應(yīng)對(duì)象的完全控制,在某些方面確實(shí)有更大的靈活性(頭文件的操作、庫的特定功能等等),但是應(yīng)該謹(jǐn)慎使用。一般來說,這種方法不那么明確,而且確實(shí)有一些缺點(diǎn)。主要的缺點(diǎn)是,你的代碼變得依賴于平臺(tái)(因?yàn)榈讓訋煸陧憫?yīng)對(duì)象上可能有不同的 API),而且更難測(cè)試(你必須模擬響應(yīng)對(duì)象,等等)。
另外,在上面的例子中,你失去了與依賴 Nest 標(biāo)準(zhǔn)響應(yīng)處理的 Nest 功能的兼容性,如攔截器和@HttpCode() / @Header() 裝飾器。為了解決這個(gè)問題,你可以將 passthrough 選項(xiàng)設(shè)置為 true,如下所示。
@Get() findAll(@Res({ passthrough: true }) res: Response) { res.status(HttpStatus.OK); return []; }
現(xiàn)在,你可以與本地響應(yīng)對(duì)象進(jìn)行交互(例如,根據(jù)某些條件設(shè)置 cookies 或頭信息),但把其余的事情留給框架。
到此這篇關(guān)于Node.js控制器Controller使用教程的文章就介紹到這了,更多相關(guān)Node.js Controller內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解nodejs微信公眾號(hào)開發(fā)——6.自定義菜單
這篇文章主要介紹了詳解nodejs微信公眾號(hào)開發(fā)——6.自定義菜單,自定義菜單能夠幫助公眾號(hào)豐富界面,讓用戶更好更快地理解公眾號(hào)的功能。2017-04-04WebSocket+node.js創(chuàng)建即時(shí)通信的Web聊天服務(wù)器
這篇文章主要為大家詳細(xì)介紹了WebSocket+node.js創(chuàng)建即時(shí)通信的Web聊天服務(wù)器的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-08-08nodejs中簡(jiǎn)單實(shí)現(xiàn)Javascript Promise機(jī)制的實(shí)例
這篇文章主要介紹了nodejs中簡(jiǎn)單實(shí)現(xiàn)Javascript Promise機(jī)制的實(shí)例,本文在nodejs中簡(jiǎn)單實(shí)現(xiàn)一個(gè)promise/A 規(guī)范,需要的朋友可以參考下2014-12-12nodejs使用http模塊發(fā)送get與post請(qǐng)求的方法示例
這篇文章主要介紹了nodejs使用http模塊發(fā)送get與post請(qǐng)求的方法,結(jié)合實(shí)例形式分析了nodejs基于http模塊實(shí)現(xiàn)發(fā)送get與post請(qǐng)求具體操作技巧,需要的朋友可以參考下2018-01-01Node 使用express-http-proxy 做api網(wǎng)關(guān)的實(shí)現(xiàn)
這篇文章主要介紹了Node 使用express-http-proxy 做api網(wǎng)關(guān)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Node.js操作mysql數(shù)據(jù)庫增刪改查
這篇文章主要介紹使用Node.js操作mysql數(shù)據(jù)庫增刪改查的相關(guān)資料,需要的朋友可以參考下2016-03-03nodejs個(gè)人博客開發(fā)第六步 數(shù)據(jù)分頁
這篇文章主要為大家詳細(xì)介紹了nodejs個(gè)人博客開發(fā)的數(shù)據(jù)分頁,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04node文件資源管理器讀取視頻信息從零實(shí)現(xiàn)
這篇文章主要為大家介紹了node文件資源管理器讀取視頻信息從零實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12