只需cv,實戰中學習next | 整合後端服務

語言: CN / TW / HK

theme: cyanosis highlight: atom-one-dark


王志遠,微醫前端技術部

前言

要模擬真實專案中 SSR 的應用,真正的後端服務和資料庫無可避免,我們就基於 express+mongodb 啟動一個後端服務吧!

本文分享兩種專案實現方案:express-generator 腳手架 、 原生 express 整合;

準備啟動指令碼

在 package.json 的script中新增如下指令碼

"server": "nodemon server"

nodemon 是一個 node 專案啟動器,具有失敗重啟功能,如果沒有安裝nodemon,可以執行如下命令進行安裝

npm i nodemon -g

後面的啟動專案都是指執行如下命令

npm run server

搭建專案框架

express-generator 腳手架(不推薦,只是記錄下)

安裝

npm i express-generator -g

建立專案

express-generator api

原生 express 整合(推薦)

既然是學習專案,還是多熟悉下吧,我們手寫一個 koa 伺服器並連線資料庫實現增刪改查邏輯。實現目錄如下

  • 實現可訪問的 express 服務
  • 實現跨域處理
  • 實現 post 請求的請求體解析
  • 實現連線 mongodb 資料庫
  • 實現增刪改查介面

原生 express 整合實現

實現可訪問的 express 服務

安裝依賴

yarn add [email protected]

實現內容

新建server/index.js,實現如下內容

js let express = require("express"); let app = express(); app.get("/hello", async (req, res) => { res.send("hello Express"); }); app.listen(4000, () => { console.log("伺服器在 4000 埠啟動!"); });

檢視效果

實現跨域處理

上面的 get 請求在瀏覽器可以正常請求,但如果在專案中,就會出現如下問題,這就是我們常說的跨域,在這不過多介紹,一句話:非同源的資料請求,瀏覽器會對伺服器的響應進行攔截並報錯。

image-20220501201055266

要想實現 post 請求模擬,我們先處理下跨域問題

安裝依賴

yarn add [email protected]

實現內容

新建server/index.js,實現如下內容

```js let express = require("express"); let app = express(); let cors = require("cors");

app.use( cors({ origin: ['http://localhost:3000'], credentials: true, allowedHeaders: "Content-Type,Authorization", methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS" }) );

app.get("/hello", async (req, res) => { res.send("hello Express"); }); app.listen(4000, () => { console.log("伺服器在 4000 埠啟動!"); }); ```

這時就能正常請求了

實現 post 請求的請求體解析

body-parser可以將 post 請求引數處理成物件並掛載在req.body

安裝依賴

yarn add [email protected]

接入規則

js let bodyParser = require("body-parser"); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json());

實現內容

server/index.js改為如下內容

js let express = require("express"); let bodyParser = require("body-parser"); let app = express(); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.get("/hello", async (req, res) => { res.send("hello Express"); }); app.post("/api/register", async (req, res) => { let user = req.body; res.send({ code: 0, data: user }); }); app.listen(4000, () => { console.log("伺服器在 4000 埠啟動!"); });

檢視效果

前端提交表單,後端將req.body直接返回

2022-05-02 18.06.06

實現連線 mongodb 資料庫

要連線,首先你得有 mongo 資料庫,推薦文章安裝 mongo 資料庫;下文預設已經存在 mongodb 資料庫了,並且已經建立了next-sty名稱的資料庫,我們來進行專案中的連線操作。

安裝依賴

yarn add [email protected] [email protected]

接入規則

先將資料庫的地址資訊和用於會話加鹽的金鑰放在配置檔案中,建立server/config.js檔案,寫入如下內容

js module.exports = { secret: "wzyan", dbUrl: "mongodb://127.0.0.1/next-db", };

然後在專案中進行接入

js let session = require("express-session"); var MongoStore = require("connect-mongo"); app.use( session({ secret: config.secret, resave: false, saveUninitialized: true, store: MongoStore.create({ mongoUrl: config.dbUrl, mongoOptions: { useNewUrlParser: true, useUnifiedTopology: true, }, }), }) );

實現內容

server/index.js內容修改為如下

js let express = require("express"); let bodyParser = require("body-parser"); let cors = require("cors"); let session = require("express-session"); let config = require("./config"); var MongoStore = require("connect-mongo"); let app = express(); app.use( cors({ origin: ["http://localhost:3000"], credentials: true, allowedHeaders: "Content-Type,Authorization", methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS", }) ); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use( session({ secret: config.secret, resave: false, saveUninitialized: true, store: MongoStore.create({ mongoUrl: config.dbUrl, mongoOptions: { useNewUrlParser: true, useUnifiedTopology: true, }, }), }) ); app.listen(4000, () => { console.log("伺服器在 4000 埠啟動!"); });

檢視效果

這個效果我們在實現了增刪改查介面後驗證

實現使用者增刪改查介面

有了資料庫,又完成了連線,我們就可以開始實現業務介面啦!很簡單的增刪改查介面

安裝依賴

yarn add [email protected]

接入規則

mogonse 操作資料庫的方式是通過 Model 的概念實現的,我們來實現下使用者的介面,自然需要建立使用者的模型。我們建立server/db.js檔案,實現如下內容,匯出使用者模型

```js const mongoose = require("mongoose"); const Schema = mongoose.Schema; const ObjectId = Schema.Types.ObjectId; let config = require("./config"); const conn = mongoose.createConnection(config.dbUrl, { useNewUrlParser: true, useUnifiedTopology: true, }); const UserModel = conn.model( "User", new Schema( { username: { type: String }, password: { type: String }, }, { timestamps: { createdAt: "created", updatedAt: "updated" } } ) );

module.exports = { UserModel, };

```

然後在服務端引入,實現介面即可

server/index.js

```js let express = require("express"); let bodyParser = require("body-parser"); let cors = require("cors"); let Models = require("./db"); let session = require("express-session"); let config = require("./config"); var MongoStore = require("connect-mongo"); let app = express(); app.use( cors({ origin: ["http://localhost:3000"], credentials: true, allowedHeaders: "Content-Type,Authorization", methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS", }) ); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use( session({ secret: config.secret, resave: false, saveUninitialized: true, store: MongoStore.create({ mongoUrl: config.dbUrl, mongoOptions: { useNewUrlParser: true, useUnifiedTopology: true, }, }), }) ); app.get("/api/users", async (req, res) => { let users = await Models.UserModel.find(); res.send({ code: 0, data: users }); }); app.get("/api/users/:id", async (req, res) => { let user = await Models.UserModel.findById(req.params.id); res.send({ code: 0, data: user }); }); app.post("/api/register", async (req, res) => { let user = req.body; user = await Models.UserModel.create(user); res.send({ code: 0, data: user }); }); app.listen(4000, () => { console.log("伺服器在 4000 埠啟動!"); });

```

在前臺專案中接入介面

add.tsx中修改為如下內容

```tsx import UserLayout from "./index"; import router from "next/router"; import { Form, Input, Button, Icon, message } from "antd"; import axios from "../../utils/axios"; function UserAdd(props) { const { getFieldDecorator } = props.form; async function handleSubmit(event) { event.preventDefault(); let values = props.form.getFieldsValue(); let response = await axios.post("/api/register", values); if (response.data.code === 0) { console.log(response);

  router.push("/user/list");
} else {
  message.error("新增使用者失敗");
}

} return (

{getFieldDecorator("username", { rules: [{ required: true, message: "Please input your username!" }], })( } placeholder="Username" /> )} {getFieldDecorator("password", { rules: [{ required: true, message: "Please input your Password!" }], })( } type="password" placeholder="Password" /> )}
); } export default Form.create({ name: "UserAdd" })(UserAdd); ```

list.tsx中修改為如下內容

```tsx import UserLayout from "./index"; import { List } from "antd"; import Link from "next/link"; import axios from "../../utils/axios";

function UseList(...params) { let props = params[0]; console.log("4.UseList.render", params); return ( 使用者列表\

} footer={\
共計多少{props?.list?.length}個使用者\
} bordered dataSource={props.list} renderItem={(item: any) => ( /user/detail/${item._id}} href={{ pathname: /user/detail, query: { id: item._id } }} > {item.username} )} /> ); } // 獲取此元件的初始化物件 此函式的返回值將成為此元件的屬性物件 UseList.getInitialProps = async (ctx) => { console.log("2.UseList.getInitialProps ctx", ctx); // let list = [ // { _id: 1, username: "zhangsan", password: "1" }, // { _id: 2, username: "lisi", password: "2" }, // ]; let response = await axios({ url: "/api/users", method: "GET" }); return { list: response.data.data }; // return { list }; };

export default UseList;

```

檢視效果

2022-05-02 21.28.06

至此,我們也就完成了【後端服務的實現】啦!