一種不錯的 BFF Microservice GraphQL/REST API 層的開發方式
阿新 • • 發佈:2020-12-09
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140118576-1541099959.png)
雲原生(`Cloud Native`)Node JS Express Reactive 微服務模板 (`REST/GraphQL`) 這個專案提供了完整的基於 `Node JS / Typescript` 的微服務模板,包括生產部署、監控、除錯、日誌記錄、安全、CI/CD 所需的所有功能。還添加了基於響應性擴充套件的示例,以演示如何將其用於構建微服務 `API` 邊緣服務(`edge-service`)、前端的後端(`BFF`)或將其用作構建任何型別微服務的基礎。
專案地址:[nxplorerjs-microservice-starter](https://github.com/ERS-HCL/nxplorerjs-microservice-starter)
## 設計原則
- 使用同類最佳的模組來建立可用於生產的微服務框架
- 基於 [12-factor app](https://12factor.net/) 原則
- 沒有定製程式碼或包裝器,因此任何開發人員都可以修改/替換任何模組或實現
- 可作為參考的實現的設計模式
- 模組化,可替換和即插即用程式碼
- 為業務 API 和微服務平臺開發提供入門
- DevOps 準備了程式碼質量,單元和整合測試,自動部署。
## 功能/特性
- `Node JS`, `Express`, `Typescript` , 依賴注入(Dependency Injection base)
- 基於 `Backpack` (webpack) - 構建 , 開發 , 打包
- 啟用 `Swagger` - Express swagger 中介軟體 / Swagger UI 整合
- GraphQL 基於 `Apollo Server 2.0`,帶有 `JWT` 安全性、資料載入器(`data loader`)和 `REST` 資料來源示例
- 通過 `graphql-import` 支援 `GraphQL SDL`
- 開發期間 `GraphQL mock resolvers` (可選) - `graphql-tools`
- 基於 GraphQL 的客戶端包裝 API - `graphql-request`
- REST APIs - 使用 `Inversify Controller`
- 外部化配置 - `DotEnv` (設定,特定於 Env 的 API URL)
- 測試 - `Jest` , `SuperTest` , `GraphQL Tester`。 自動化單元和整合測試的基礎設施
- 程式碼覆蓋率 - `Istanbul`
- 程式碼質量 - `tslint`
- 容器支援 - `Docker` , `Kubernetes` 叢集
- 基於 `Helm Chart` 的部署支援
- `Prometheus` 整合
- API 響應日誌記錄,Express Server 日誌記錄,UUID 傳播 - `Pino`
- Reactive Extensions 支援 - `RxJS6`
- CORS, JSONObject 限制 , Helmet , CSRF - Express 安全
- 基於 IOC / 依賴注入 / Express 註釋的 API - `Inversify`
- 文件 - `TypeDocs`
- API 異常處理實用程式
- 標準 HTTP Code 以獲取更清蒸的 code
- 示例 API,模式以供參考
- `Sonar Qube` 整合
- `Hystrix` 熔斷器支援 (使用 Brakes)
- 基於 JWT 的 API 安全性 - `jsonwebtoken`, `express-jwt`
- 現在使用超級快速的 `pino` 日誌程式來滿足所有的日誌記錄需求
- 內建額外的效能時間記錄
檢視 REST API /examples/{id}
```json
{
"pid": 3984,
"hostname": "LP-507B9DA1D355",
"level": 30,
"time": 1515813665734,
"0": {
"socket": 5.656709999995655,
"lookup": 186.8375229999947,
"connect": 389.5646870000055,
"response": 594.8022639999981,
"end": 599.1270230000082
},
"v": 1
}
```
### REST APIs
- 可以使用以下 URL 下載 API 規範
```
http://localhost:3000/api-docs/Api.yaml
```
```
http://localhost:3000/api-docs/
```
- examples - API 中的按 ID 示例搜尋的基本示例 (`/examples/:id`)
- shop - 如何使用 Reactive Extensions 進行 API 編排(`FlatMap`)的示例 (`/shop/priceByOptionId/:id`)
- starwars - 如何使用 Reactive Extensions 進行 API 編排的示例(`ForkJoin`)(`/starwars/people/:id`)
- hystrix - 如何對 API 使用熔斷模式的示例 (`/hystrix`)
- scraper - 如何使用 scrape-it 從網站上抓取資料的示例 (`/scraper`)
- 使用 swagger UI 以獲得示例 API 的完整列表
- metrics - 為所有 API 添加了基於 Prometheus 的指標 (`/metrics`)
- API 部分 JSON 響應支援
```bash
curl http://localhost:3000/api/v1/starwars/people/1
```
- Response
```bash
{
name: "Luke Skywalker",
height: "172",
mass: "77",
hair_color: "blond",
skin_color: "fair",
eye_color: "blue",
birth_year: "19BBY",
gender: "male",
homeworld: {
name: "Tatooine",
rotation_period: "23",
orbital_period: "304",
diameter: "10465",
climate: "arid",
gravity: "1 standard",
terrain: "desert",
surface_water: "1",
population: "200000",
residents: [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/2/",
"http://swapi.co/api/people/4/",
"http://swapi.co/api/people/6/",
"http://swapi.co/api/people/7/",
"http://swapi.co/api/people/8/",
"http://swapi.co/api/people/9/",
"http://swapi.co/api/people/11/",
"http://swapi.co/api/people/43/",
"http://swapi.co/api/people/62/"
],
films: [
"http://swapi.co/api/films/5/",
"http://swapi.co/api/films/4/",
"http://swapi.co/api/films/6/",
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/1/"
],
created: "2014-12-09T13:50:49.641000Z",
edited: "2014-12-21T20:48:04.175778Z",
url: "http://swapi.co/api/planets/1/"
},
films: [
"http://swapi.co/api/films/2/",
"http://swapi.co/api/films/6/",
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/1/",
"http://swapi.co/api/films/7/"
],
species: [
"http://swapi.co/api/species/1/"
],
vehicles: [
"http://swapi.co/api/vehicles/14/",
"http://swapi.co/api/vehicles/30/"
],
starships: [
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/22/"
],
created: "2014-12-09T13:50:51.644000Z",
edited: "2014-12-20T21:17:56.891000Z",
url: "http://swapi.co/api/people/1/"
}
```
---
```bash
curl http://localhost:3000/api/v1/starwars/people/1?data(name,gender,homeworld(gravity,population))
```
- Response
```bash
{
"data": {
"name": "Luke Skywalker",
"gender": "male",
"homeworld": {
"gravity": "1 standard",
"population": "200000"
}
}
}
```
### GraphQL
- 已基於 [apollo framework](https://github.com/apollographql) 和參考實現添加了 `GraphQL` 支援(包括來自 `swapi.co` 的 `starwars api`)
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140149002-665777784.png)
- 從 `http://localhost:3000/playground` 訪問 graphql playground
- 從 `http://localhost:3000/graphiql` 訪問 graphiql tool
- GraphQL API 跟蹤(可配置)
- 用於快取和批處理的資料載入器(Dataloader)
- 添加了多個**啟用 Dataloader** 示例
- RxJS API 呼叫 - peopleWithPlanets(id : )
- Starwars APIs - people(id: ) , planet(id: ) , starship(id: ) - peopleList(keys: [number])
- 查詢列表(請參閱 schema 詳細資訊以獲取完整列表)
- quoteOfTheDay: String
- random: Float
- examples: [ExampleType] <-- JWT身份驗證
- example(id: Int): ExampleType
- blog(id: Int) (Paginated query)
- rollThreeDice: [Int]
- peopleWithPlanet(id: Int): PeopleWithPlanetType (Uses RxJS to combine results from 2 APIs)
- peopleDS(id: Int): PersonType (Based on REST DataSource)
- people(id: Int): PersonType (Based on data loader)
- planet(id: Int): PlanetType
- starship(id: Int): StarshipType
- peopleList(keys: [Int]): [PersonType]
- movie: MovieType
- 示例查詢執行
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140203585-370005323.png)
- Mutations
- addExample(name: String!): ExampleType
- addComment(comment: CommentInput!): Comment
- login(email: String!,password: String!): UserType
- 示例 Mutation 執行
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140411017-892284278.png)
- Subscriptions
- exampleAdded (將檢查是否通過 mutation 添加了新元素)
- commentAdded (每當通過 mutation 新增新 comment 時都會檢查)
- 示例 Subscription 執行
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140233371-1692599953.png)
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140259307-1897438208.png)
- VSCode 除錯啟動配置(添加了預配置的除錯啟動器)
- 在開發過程中添加了用於遙測的 Node 儀表板檢視
- 增加了 NodeJS 叢集模式(負載均衡 worker)
- 啟動伺服器時,它會根據 CPU 數量新增 worker
```bash
Master cluster setting up 4 workers...
Worker 2828 is online
Worker 2816 is online
Worker 13956 is online
Worker 3756 is online
up and running in development @: LP-507B9DA1D355 on port: 3000
up and running in development @: LP-507B9DA1D355 on port: 3000
up and running in development @: LP-507B9DA1D355 on port: 3000
up and running in development @: LP-507B9DA1D355 on port: 3000
```
## Graphql 客戶端 API
- 當我們構建基於 GraphQL 的伺服器時,可能需要從其他下游基於 GraphQL 的 API 伺服器獲取資料。
- 作為一個示例,`graphqlcool/graphql-request` 模組用於演示這一點,使用 `graphqlcool` 演示 graphQL api `https://api.graph.cool/simple/v1/movies`。
- API 規範
```bash
query {
movie {
releaseDate
slug
actors {
name
}
}
}
```
- API 輸出
```json
{
"data": {
"movie": {
"releaseDate": "2010-08-28T20:00:00.000Z",
"slug": "inception",
"actors": [
{
"name": "Leonardo DiCaprio"
},
{
"name": "Ellen Page"
},
{
"name": "Tom Hardy"
},
{
"name": "Joseph Gordon-Levitt"
},
{
"name": "Marion Cotillard"
}
]
}
}
}
```
## 先決條件
安裝 npm 和 nodeJS
npm version >= 3.x
node version >= 6.x
## 安裝它
```bash
npm install
```
## 設定 _外部環境_
- 編輯 **.{PROFILE}.env** 檔案 —— 其中的概要檔案(PROFILE)可以是測試(test)、開發(development)、生產(production)
| 變數 | 描述 | 預設值 |
| --------------------- | ------------------------------------------------------------------------------------------------------- | ------------- |
| PORT | 伺服器埠 | 3000 |
| LOG_LEVEL | 日誌級別 (info,debug,error) | info |
| SESSION_SECRET | 用於簽名 cookie 的字串 | |
| API_TIME_OUT | 預設API超時(以毫秒為單位) | 10000 |
| TEST_TIME_OUT | 預設測試超時(以毫秒為單位) | 10000 |
| JWT_AUTH | 啟用/禁用基於 JWT 的 API 安全 | true |
| RSA_PRIVATE_KEY_FILE | RSA 私鑰路徑示例 | |
| RSA_PUBLIC_KEY_FILE | RSA 公鑰路徑示例 | |
| TOKEN_EXPIRY_TIME | JWT 令牌到期(從 /login 生成) | 1 hour (1h) |
| STREAM_HYSTRIX | 啟用/禁用 Hystrix streaming 伺服器 (true 或 false) | false |
| CORS | 在伺服器上啟用/禁用 CORS (true 或 false)。僅在生產版本中可用 | false |
| CLUSTER_MODE | 在伺服器上啟用/禁用 Node Clustering (true 或 false) | false |
| SWAGGER_API_DOCS_ROOT | 服務您的 Swagger API 檔案,以便它們可與 Swagger UI,PostMan 等前端工具一起使用。 | /api-docs/ |
| GRAPHQL_SUBSCRIPTIONS | 啟用/禁用 GraphQL subscriptions (true 或 false) | true |
| GRAPHQL_PLAYGROUND | 啟用/禁用 GraphQL Playground (true 或 false) | true |
| GRAPHQL_TRACING | 啟用/禁用 GraphQL tracing (true 或 false) | true |
| GRAPHQL_MOCK | 啟用/禁用 GraphQL Mock,對於未實現的介面(true 或 false) | true |
| API_MOCK | 啟用/禁用 REST API Mock,對於未實現的路由(true 或 false) | true |
## 執行它
### 執行在 _開發_ 模式
```bash
npm run dev
```
### 執行在 _生產_ 模式
```bash
npm run compile
npm start
```
#### 執行在 _VS Code 除錯_ 模式
```bash
npm run compile
Press F5
```
#### 執行帶有程式碼覆蓋率的測試
#### 執行單元測試
- 單元測試與要測試的模組或類位於同一目錄中
- 所有單元測試都需要有一個副檔名 `\*.spec.ts`
```bash
npm run test
```
#### 執行整合測試
- 整合測試與要測試的模組或類位於同一目錄中
- 所有整合測試都需要有一個副檔名 \*.itest.ts
- 首先構建整合測試。這將在構建中設定整合測試環境
```bash
npm run itest:build
```
- 執行 node 伺服器並對其進行整合測試
- 這等待伺服器啟動,執行測試,然後在完成時終止所有程序
```bash
npm itest:run
```
### 嘗試一下
- 將您的瀏覽器指向 [http://localhost:3000](http://localhost:3000).
- 直接或通過 swagger 呼叫示例 REST 端點 `http://localhost:3000/swagger`
- 使用端點呼叫 Prometheus 指標 `curl http://localhost:3000/metrics`
- 訪問針對 graphQL 的瀏覽器內建 IDE `http://localhost:3000/graphiql`
- 訪問 graphQL playground app `http://localhost:3000/playground/`
- 訪問健康檢查 api `curl http://localhost:3000/healthcheck`
### 檔案結構
```
├───public * nxplorer server 的登入頁面
├───screenshots * 示例截圖
└───server * 伺服器配置和 API
| ├───api * 伺服器上定義的 REST API
| │ ├───controllers * 使用 RxJS,Inversify 的 API controller
| │ │ ├───examples * Examples controller
| │ │ ├───hystrix-demo * Hystrix demo controller
| │ │ ├───security * JWT login API controller
| │ │ ├───shop * 帶有產品,價格,庫存的示例商店 API
| │ │ └───starwars * SWAPI controller
| │ ├───interfaces * Service 介面
| │ ├───models * API 資料模型
| │ └───services * Service API 實現
| ├───common * Server 啟動和配置
| │ ├───config * Server 配置
| │ ├───constants * Inversify 和其他通用識別符號常量
| │ ├───interfaces * 公共 service 介面
| │ ├───middleware * 自定義中介軟體
| │ ├───models * 公共 API 資料模型
| │ ├───services * 公共 service 實現
| │ └───swagger * Swagger API 規範 (YAML)
| | └───env.ts * DotENV 配置
| | └───server.ts * Express Server 啟動和配置
| └───graphql * 伺服器上定義的 GraphQL API
| | ├───dataloader * GraphQL 資料載入器功能
| | ├───errors * GraphQL 錯誤處理程式
| | ├───schema * GraphQL Schema 型別
| | ├───mocks * GraphQL Mock Resolvers
| | └───resolvers * GraphQL resolvers
| | └───setupSchema.ts * GraphQL schema 配置
| └───index.ts * 主 Server 入口點
├───helm * Helm chart 部署指令碼
│ ├───charts *
│ └───templates *
└───backpack.config.js * Backpack 配置
└───package.json * npm 依賴
└───build.js * ShellJS 實用程式構建指令碼
└───deploy-k8s.sh * Kubernetes 部署指令碼
└───Dockerfile * Docker 構建檔案
└───docker-compose.yml * Docker 構建和執行檔案
└───build-docker.bat|sh * Docker 構建檔案
└───itest.config.json * Jest 整合測試配置
└───unit.config.json * Jest 單元測試配置
└───tsconfig.json * typescript 配置
└───tslint.json * tslint 配置
└───.{profile}.env * 基於配置檔案的外部環境檔案 (development 開發,test 測試, production 生產)
└───sonar-properties.json * sonarscanner|SonarQube 配置
└───jwtRS256.key|.key.pub * 伺服器使用的 JWT 私鑰和公鑰示例
```
### 日誌與 UUID
- 添加了 UUID 傳播的示例實現。這取決於在請求物件中設定的 cookie 'UUID'。[LogService](server/common/services/log.service.ts) 將把 uuid 新增到它生成的所有日誌中。
- 例如,如果 'UUID' 設定為 `xxxx-dddd-ssss-wwww-ssss`,那麼呼叫 `/shop/products` API 將生成
```json
{
"pid": 13492,
"hostname": "LP-507B9DA1D355",
"level": 30,
"time": 1515859200496,
"uuid": "xxxx-dddd-ssss-wwww-ssss",
"fullUrl": "http://localhost:3000/api/v1/shop/products",
"statusCode": 200,
"responseTime": "1.187",
"v": 1
}
```
### GraphQL Mocks
- 作為 TDD 的一部分,我們可能需要模擬 graphql 響應,直到我們能夠實現解析器為止
- 該基礎結構設定為僅為當前未實現的解析器新增模擬。因此,一旦實現可用,實際的解析器就會接手。 同樣,如果解析器執行失敗,那麼這將落在模擬響應上。 此功能只能在開發期間使用,因此已新增檢查以禁用“生產”版本中的此功能。
- 為了支援
- 設定環境變數 GRAPHQL_MOCK 為 true
- 在 [mocks/index.ts](server/graphql/mocks/index.ts) 檔案定義 mock 解析器(resolver)
- 作為示例,有查詢添加了 examplesMock, peopleMock
- 示例輸出如下
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140504434-482278881.png)
### RestAPI Mocks
- 在 `..env` 檔案中啟用 `API_MOCK=true`。注意:為了安全起見,即使 `API_MOCK` 設定為 `true`,也無法在生產模式下使用
- 針對[自動產生 mock](https://github.com/BigstickCarpet/swagger-express-middleware/blob/master/docs/middleware/mock.md),`swagger-express-middleware` 模組提供了開箱即用的支援
- 步驟
- 在 [Api.yaml 檔案](server/common/swagger/Api.yaml) 定義 API swagger 規範
- 如果 express 路由中沒有可用的實現,那麼中介軟體將為這些 api 建立模擬
- 訪問 `nXplorer` (`/swagger`) 提供的 `swagger ui`,並引用標記為 `Mock API` 和帶有字首 `/mock` 的 API。該示例有兩個主要實體 —— `cars` 和 `drivers`。您可以搜尋、執行 CRUD 操作以及上傳和下載影象。
#### 構建 Docker 映象
```bash
./build-docker.sh
```
#### k8s 部署
- 基於 Helm chart 的部署
```bash
./deploy-k8s.sh
```
- 一個示例的輸出
```bash
release "nxplorerjs-microservice" deleted
NAME: nxplorerjs-microservice
LAST DEPLOYED: Fri Sep 22 22:10:58 2017
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
nxplorerjs-microservice-starter 5 1s
==> v1/Service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nxplorerjs-microservice-starter 10.0.0.196 80:30316/TCP 1s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nxplorerjs-microservice-starter 1 1 1 0 1s
注意:
1. 通過執行以下命令獲取應用程式 URL:
export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services nxplorerjs-microservice-nxplorerjs-microservice-starter)
export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
Express 微服務被部署到 http://192.168.99.100:30316/
```
#### 使用 node 儀表板檢視(僅針對開發環境)
- 去使用 node 儀表板檢視
```bash
npm run compile
npm run dash
```
- 這將啟動帶有附加的 node 儀表板的應用程式,該儀表板提供有關記憶體,cpu 和日誌的詳細資訊
#### 安全
- 已使用[示例 JWT 私鑰和公鑰](https://gist.github.com/ygotthilf/baa58da5c3dd1f69fae9)實現了基於 JWT 的安全性
- REST API 和 GraphQL 都添加了示例實現。
- 驗證中介軟體程式碼可以在[這裡](server/common/middleware/auth-middleware.ts)檢視
#### JWT Security GraphQL
- 基於 `JWT` 的安全性的演示實現已啟用一個查詢“示例”。 下面是測試的步驟。
- 如果啟用了 `JWT` 安全性(環境變數 `JWT_AUTH` 為 `true`),我們需要使用登入突變 `API` 來獲取示例 `JWT` 令牌(當前設定為1小時到期)
- Step 1 - 使用登入 mutation(突變)來獲取有效使用者的 jwt 令牌。出於演示目的,可以提供任何電子郵件和密碼字串。該角色是可選的。如果未提供,則預設為角色 “USER”
```
mutation {
login(email: "[email protected]",
password:"admin",role:"ADMIN") {
id
role
email
jwt
}
}
```
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140528901-1157200765.png)
- Step 2 - 驗證“示例”是否無需身份驗證即可工作。 它將給出一個錯誤(注意:錯誤處理需要改進,但是這裡我們只看這個概念)
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140540575-1968663245.png)
- Step 3 - 在執行 “examples” 查詢之前,使用 `Bearer token` 設定授權頭。
```
{
"Authorization": "Bearer xxx.xxx.xxx"
}
```
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140551844-1248187659.png)
#### JWT Security REST APIs
- 如果啟用了 JWT 安全性,那麼我們需要使用 `/login` API 獲取示例 JWT 令牌(當前設定為1小時到期)
```bash
curl -X POST "http://localhost:3000/api/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"email\": \"[email protected]\", \"password\": \"pwd\", \"role\": \"admin\"}"
```
- 示例輸出。注意,JWT token 是屬性 **idToken** 的值
```json
{
"idToken":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE1MTQ4NjQ3ODMsImV4cCI6MTUxNDg2ODM4Mywic3ViIjoidGVzdEBnbWFpbC5jb20ifQ.hAEa6AL1Kxxxxxxx",
"expiresIn": "1h"
}
```
- `api/v1/examples` API,一個有效的 JWT 令牌必須在 “Authorization” header 中,在所有查詢中傳遞。注意,這只是一個示例。您也可以類似地輕鬆啟用新增端點。
- 請檢視 [examples controller](server/api/controllers/examples/controller.ts)
```typescript
@controller('/examples', authMiddleware({ role: 'admin'}))
```
- 函式 [authMiddleware](server/common/middleware/auth-middleware.ts) 負責驗證在 header 中傳遞的 JWT token。
- 它可以擴充套件為也支援基於角色的訪問,併為此提供了支援。
- 注意:作為演示示例,這裡提供了公鑰和私鑰。理想情況下,在真實場景中從外部維護這些JWKS (JSON Web Key Set)端點
- “Authorization” header 中必須使用以下語法:
Bearer xxxxxx.yyyyyyy.zzzzzz
- 使用 [swagger ui](http://localhost:3000/swagger) 測試
- 點選 “Authorize” 按鈕,設定上面提到的 Bearer token
- 現在所有 `/examples` 相關的 `api` 都可以工作了
##### RBAC 測試
- 如果啟用了 JWT 安全性,並且我們使用 `/login` API 獲取示例 `JWT token`,但其角色為 “guest” 而不是 “admin”
```bash
curl -X POST "http://localhost:3000/api/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"email\": \"[email protected]\", \"password\": \"pwd\", \"role\": \"guest\"}"
```
- 在 “Authorization” header 中設定 Bearer token,用於後續呼叫任何 `/examples api`,將導致基於角色的授權失敗
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140618406-391491549.png)
### GraphQL Directives
- 這項工作正在進行中。
- 當前添加了一個使用 `@date` 指令的示例(如 `graphql-tools` 文件中所述)
- Query (`{ today(format: "mmm-dd-yy") }`) - 這裡的格式基於@date scheme Directive,該格式接受解析器的輸出並格式化日期,然後再將其傳送給客戶端。
- Query ( `{ examplesWithAuth { id name } }` - 這是對 [JWT GraphQL APIs](#jwt-security-graphql) 部分中提到的示例查詢的一種變體。此處的區別在於,我們使用 `@auth` 指令根據角色來處理身份驗證,而不是對解析程式中的實現進行硬編碼。這是更清蒸的方法,並且與解析器分離。
- 查詢 schema `examplesWithAuth: [ExampleType] @auth(requires: ADMIN)` 使用 `@auth` 指令,該指令將攔截具有適當角色的經過身份驗證的使用者的呼叫檢查。(注意:您需要在之前執行 `login` mutation,然後使用 Authorization token 設定 HTTP header)
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140639842-1580551103.png)
#### CSRF Security
- 在**生產模式**中啟用了 CSRF 安全性
- 所有 POST API 都需要讀取瀏覽器中設定的 cookie “XSRF-TOKEN”,然後使用以下任一 `key` 將其傳遞到響應頭中
- req.headers['csrf-token'] - CSRF-Token HTTP 請求頭。
- req.headers['xsrf-token'] - XSRF-Token HTTP 請求頭。
- req.headers['x-csrf-token'] - X-CSRF-Token HTTP 請求頭。
- req.headers['x-xsrf-token'] - X-XSRF-Token HTTP 請求頭。
#### Compression
- 預設情況下,壓縮是在伺服器上啟用的,並且基於[壓縮模組](https://www.npmjs.com/package/compression)
- 配置詳細資訊位於 [compression.ts 檔案](server/common/config/compression.ts)中
- 如果需要在不壓縮的情況下獲取響應,請在請求頭中傳遞 `x-no-compression` 鍵
#### Hystrix 熔斷器支援
- 熔斷器支援已新增到專案中,並使用 hystrix 相容模組 [brakes](https://github.com/awolden/brakes)
- Hystrix 預設禁用流支援。
- 通過在 [.env](.env) 檔案中將 STREAM_HYSTRIX 屬性設定為 “STREAM_HYSTRIX=true” 來啟用它
- 為了方便起見,提供了 Hystrix 伺服器的 Docker 版本,並在 [docker-compose.yml](docker-compose.yml) 檔案中進行了設定
##### 在 Docker 上執行的步驟
```bash
npm run compile
docker-compose build
docker-compose up
```
- 在 Docker 上設定 3000 和 8080 的埠轉發
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140711294-728020782.png)
- Access the Hystrix dashboard at localhost:8080/hystrix and set the stream location to `localhost:3001/hystrix.stream`
- 訪問 `localhost:8080/hystrix` 上的 Hystrix 儀表板,並將流位置設定為 `localhost:3001/hystrix.stream`
- 在 `/api/v1/hystrix` 下執行示例,並在儀表板上檢視 hystrix stream 結果
![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140723824-1704399582.png)
### 與 SonarQube 整合(保證持續的程式碼質量)
假設您已經安裝了 SonarQube 5.5.6 (LTS)
- 使用 [Sonar Typescript 外掛](https://github.com/Pablissimo/SonarTsPlugin#installation)和 Generic Test Coverage 外掛設定 SonarQube
- 全域性安裝 sonar-scanner (`npm install --global sonar-scanner`)
- 更新屬性 `sonar.host.url` 的 [`sonar-project.properties`](sonar-project.properties) 檔案以指向您的 SonarQube 伺服器。預設情況下,這假設 SonarQube 伺服器使用預設埠在本地執行
- 執行單元測試
```bash
npm run test
```
- 測試結果以 sonar 相容格式收集在結果資料夾中
- 將結果推送到 SonarQube
```bash
npm run sonar-scanner
```
- 如果使用 SonarQube 6.x。它支援[通用測試資料](https://docs.sonarqube.org/display/SONAR/Generic+Test+Data)
修改 [package.json](package.json) 以設定適當的 sonarQube 版本
```json
jestSonar": {
"reportPath": "reports",
"reportFile": "test-reporter.xml",
"indent": 4,
"sonar56x": true
}
```
注意:對於 Sonar 6.x,將 sonar56x 設定為 “false”,這將生成使用 sonar 6 schema 的測試報告。
### 負載測試
- [loadtest](https://www.npmjs.com/package/loadtest) 是用於負載測試的出色工具
- 使用步驟
- 將其安裝為全域性 npm 模組
```bash
npm install -g loadtest
```
- 啟動 nxplorerjs-microservice
```bash
npm run start
```
- 針對生產版本執行負載測試。以下是一個示例
```bash
loadtest http://localhost:3000/api/v1/examples/1 -t 20 -c 20
```
## 互相交流
[一起雲原生](https://micro-ssr.hacker-linner.com/)