複習 - node.js(介面案例)

来源:https://www.cnblogs.com/heymar/archive/2022/04/26/16196418.html
-Advertisement-
Play Games

其實複習一次的作用真實太大了,真的,自從上次ajax開始其實就開始i有點懵懵懂懂的感覺,一直拖想到了node在去回顧一遍,這一次回去複習,ajax已經很熟練了,node之前搞不懂那些原理也順清楚了好多,其實這次複習沒有什麼需要說的知識點,因為要說的前面都說過了,我來說一下這個做的一個大項目吧,這個項 ...


其實複習一次的作用真實太大了,真的,自從上次ajax開始其實就開始i有點懵懵懂懂的感覺,一直拖想到了node在去回顧一遍,這一次回去複習,ajax已經很熟練了,node之前搞不懂那些原理也順清楚了好多,其實這次複習沒有什麼需要說的知識點,因為要說的前面都說過了,我來說一下這個做的一個大項目吧,這個項目真的,應該是我不熟練的願意那邊,就是用express寫介面,用postman來測試,三個模塊,三個資料庫,基本上都在我的代碼裡面了,寫的很詳細步驟,用到的技術,基本上是用node的express模塊,去寫介面,然後中途中到了一些中間件,比如規定語義規則的joi,比如給密碼解碼加密的bcryptjs,我做了一天才做下來這一個案例

一個項目初試化,首先要創立一個單獨的項目文件夾,然後終端npm init直接安裝package.json,api.js介面文件,路由模塊創立一個文件夾夾,路由函數又要分為一個模塊,再把資料庫創立好,基本就可以開始完成功能需求了,用後端node完成增刪改查

項目文件分類:

 

 

 

1

介面文件

// 1.初始化
// 1.1創建項目
const express = require('express')
const app = express()
// 1.2配置跨域
const cors = require('cors')
app.use(cors())
// 1.3配置解析表單中間件
// 錯誤點:記住要有參數
app.use(express.urlencoded({extended : false}))

// 2.3因為後面處理函數用到了很多res.send所以封裝為一個全局中間件,給res綁定一個函數,那後面的中間件路由都可以用到這個函數了
app.use((req, res, next) => {
    res.cc = function(err, status = 1) {
        res.send({
            status,
            msg : err instanceof Error ? err.message : err
        })
    }
    next()
})

// 2.4.6配置解析token的中間件
const expressJWT = require('express-jwt')
const secretKey = require('./secretKey')
app.use(expressJWT({secret : secretKey.secretKey, algorithms : ['HS256']}).unless({path : [/^\/api\//]}))

// 1.4.4導入路由模塊
const routerUser = require('./router/user')
const Joi = require('joi')
const { expressjwt } = require('express-jwt')
const { path } = require('express/lib/application')
app.use('/api', routerUser)
// 3.1.1個人中心路由導入
const infoRouter = require('./router/userinfo')
app.use('/my', infoRouter)

// 4.1.2文章管理導入
const article = require('./router/acticle')
app.use('/my/article', article)

// 5.1.2發佈文章路由導入
const cates = require('./router/cate')
app.use('/my/article', cates)

// 2.2.3定義規則joi的錯誤級別中間件
app.use((err, req, res, next) => {
    if (err instanceof Joi.ValidationError) return res.send(err.message)
    // 2.4.7增加jwt錯誤中間件
    if (err.name == 'UnauthorizedError') return res.cc('身份認證失敗')
    return res.send('其他錯誤')
})


app.listen(80, () => {
    console.log('http://127.0.0.1');
})

2.

寫好介面文件該去給路由創建模塊

// 1.4初始化路由相關文件夾 不光要給路由分裝一個模塊 裡面的處理函數也要有一個模塊
const express = require('express')
const { append } = require('express/lib/response')
const router = express.Router()
// 1.4.2導入路由處理函數
const routerHandler = require('../router_handler/user')

// 註冊
// 2.2.2導入joi驗證輸入進來的是否合法
const expressJOI = require('@escook/express-joi')
const {schema_user_info} = require('../schema/user')
router.post('/reguser',expressJOI(schema_user_info), routerHandler.getReguser)

// 2.4登錄
// 2.4.1添加語法規則,可以直接用註冊的
router.post('/login',expressJOI(schema_user_info), routerHandler.getLogin)

module.exports = router

3.

處理函數模塊

// 1.4.1初始化路由處理函數模塊

//註冊
const db = require('../mysql')
//導入密碼加密解密包
const bcrypt = require('bcryptjs')
function getReguser(req, res) {
    // res.send('這裡是註冊模塊')
    // 2.2.4檢測用戶名是否被占用 導入mysql
    let selectUser = 'select * from users where username = ?'
    db.query(selectUser, req.body.username, (err, results) => {
        // 2.3.1用到我們前面定義的全局中間件優化res.send
        if (err) return res.cc(err)
        if (results.length == 1) return res.cc('用戶名已被占用')
        // 2.2.5如果過了前兩關的驗證 基本可以驗證成功了 就先對密碼進行加密處理安裝bcryptjs
        req.body.password = bcrypt.hashSync(req.body.password, 10)
        // console.log(req.body.password);
        // 2.2.6插入新用戶
        let insertUser = 'insert into users set ?'
        // -------------註意一下這裡插入資料庫的參數怎麼寫的
        db.query(insertUser, [{username : req.body.username, password : req.body.password}], (err, results) => {
            if (err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('註冊失敗')
            res.cc('註冊成功',0)
        })
    })
}

//登錄
// 2.4.4.1導入jwt
const jwt = require('jsonwebtoken')
const secretKey = require('../secretKey')
const { result } = require('@hapi/joi/lib/base')
function getLogin(req, res) {
    // res.send('這裡是登錄模塊')
    // 2.4.2根據用戶名查詢用戶的數據
    let selectLoginuser = 'select * from users where username = ?'
    db.query(selectLoginuser, req.body.username, (err, results) => {
        if (err) res.cc(err)
        if (results.length !== 1) res.cc('未找到該用戶')
        // 2.4.3有數據就去判斷密碼是否正確
        // console.log(results);
        if (!bcrypt.compareSync(req.body.password, results[0].password)) return res.cc('密碼錯誤')
        // 2.4.4用戶名有,密碼 也對上了說明登陸成功,開始jwt身份認證
        // 先剔除密碼和頭像的值
        let user = {...results[0], password : '', user_pic: '',algorithms : ['HS256']}
        let userToken = jwt.sign(user, secretKey.secretKey, {expiresIn : '1h'})
        // 2.4.5向客戶端發送token
        res.send({
            status : 0, 
            msg : '登錄成功',
            token : 'Bearer ' + userToken
        })
    })
}

// 獲取用戶基本信息
function getInfo(req,res) {
    // res.send('個人中心')
    // 3.1.2獲取用戶基本信息
    let selectInfo = 'select id,username,nickname,email,user_pic from users where id = ?'
    db.query(selectInfo, req.user.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.length !== 1) return res.cc('獲取信息失敗')
        res.send({
            status : 0,
            msg : '獲取信息成功',
            data : results[0]
        })
    })
}

// 3.2更新用戶信息
function updateInfo(req, res) {
    // res.send('更新用戶信息')
    // 3.2.4更新用戶功能
    let updateInfo = 'update users set ? where id = ?'
    db.query(updateInfo, [req.body, req.user.id], (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('更新信息失敗')
        res.cc('更新信息成功', 0)
    })
}

// 3.3重置密碼
function updatePwd(req, res) {
    // res.send('重置密碼')
    // 3.3.2查詢用戶是否存在
    let selectExist = 'select * from users where id = ?'
    db.query(selectExist, req.user.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.length !== 1) return res.cc('用戶不存在')
        // 3.3.3前面那一步雖然無所謂但這一步必須的 判斷輸入的舊密碼是否正確
        if(!bcrypt.compareSync(req.body.oldPwd, results[0].password)) return res.cc('輸入密碼錯誤')
        // 3.3.4對新密碼加密後更新到資料庫
        let password = bcrypt.hashSync(req.body.newPwd)
        let updatePwd = 'update users set password =? where id =?'
        db.query(updatePwd, [password,req.user.id], (err, results) => {
            if (err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('修改密碼失敗')
            res.cc('更新密碼成功', 0)
        })
    })
}


// 3.4更換頭像
function updateAvatar(req, res) {
    // res.send('更換頭像')
    // 3.4.3
    let updateAvatar = 'update users set user_pic = ? where id = ?'
    db.query(updateAvatar, [req.body.avatar, req.user.id], (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('更新頭像失敗')
        res.cc('更新頭像成功', 0)
    })
}
module.exports = {
    getLogin,
    getReguser,
    getInfo,
    updateInfo,
    updatePwd,
    updateAvatar
}

4.

這個時候就可以去api入口文件測試一下了,然後在資料庫寫好我們的數據表,創立一個js文件鏈接資料庫

// 2.登錄註冊
// 2.1建好資料庫後配置資料庫
const { result } = require('@hapi/joi/lib/base')
const mysql = require('mysql')
const db = mysql.createPool({
    host : '127.0.0.1',
    user : 'root',
    password : 'admin123',
    database : 'mydb'
})

// 測試
/* db.query('select 1' , (err, results) => {
    if(err) return console.log(err.message);
    return console.log(results);
}) */

module.exports = db

5.

沒記錯的話,在登錄介面應該使用jwt認證機制生成token吧

const express = require('express')
const router = express.Router()
const routerHandler = require('../router_handler/user')
// 3.個人中心
// 3.1獲取用戶基本信息
router.get('/userinfo', routerHandler.getInfo)


// 3.2.1更新用戶信息
// 3.2.3添加驗證規則
const expressJoi = require('@escook/express-joi')
const {schema_update_info, schema_update_avatar} = require('../schema/user')
router.post('/userinfo',expressJoi(schema_update_info),routerHandler.updateInfo)

// 3.3.1重置密碼
const {schema_update_pwd} = require('../schema/user')
router.post('/updatepwd',expressJoi(schema_update_pwd), routerHandler.updatePwd)

// 3.4.1更換頭像
const {schema_updatee_avatar} = require('../schema/user')
router.post('/update/avatar',expressJoi(schema_updatee_avatar), routerHandler.updateAvatar)

module.exports = router

token的驗證規則

module.exports = {
    secretKey : 'sdfafsfds'
}

上面就是整個user部分的介面了包括登錄註冊,添加刪除修改賬號或者密碼等,下麵是我們的user的驗證規則

// 2.2註冊
// 2.2.1對錶單數據驗證,這裡就不if else了直接用上joi來驗證 joi要下最新版而且直接導入joi
const { number } = require('joi')
const joi = require('joi')

const username = joi.string().alphanum().min(1).max(10).required()
// 錯誤點:正則{}裡面的量詞之間不能以空格隔開
const password = joi.string().pattern(/^[\S]{6,12}$/).required()

// 3.2.2更新用戶信息規則
const id = joi.number().integer().min(1).required()
const nickname = joi.string().required()
const email = joi.string().email().required()

// 3.4.2更換頭像規則
const avatar = joi.string().dataUri().required()
module.exports.schema_user_info = {
    body : {
        username,
        password
    }
}

module.exports.schema_update_info = {
    body : {
        id,
        nickname,
        email
    }
}

module.exports.schema_update_pwd = {
    // 3.3.2重置密碼規則
    body : {
        oldPwd : password,
        // --------------錯誤點這裡就算是變數也要添加引號
        newPwd : joi.not(joi.ref('oldPwd')).concat(password)
    }
}

module.exports.schema_updatee_avatar = {
    body : {
        avatar
    }
}

6.

這個部分是對文章的名字和別名的增刪改查的操作了,這裡面的難點在於要去理解那個怎麼來判斷是否重名哪裡

// 4.1.1文章分類列表函數
const { result } = require('@hapi/joi/lib/base')
const db = require('../mysql')
function getArticleList(req, res) {
    // res.send('文章分類列表')
    // 4.1.3獲取文章數據
    let selectArticleList = 'select * from article where is_delete = 0'
    db.query(selectArticleList, (err, results) => {
        if (err) return res.cc(err)
        res.send({
            status : 0,
            msg : '獲取文章分類列表成功',
            data : results
        })
    })
}

// 4.2.1新增文章分類
function addCates(req,res) {
    // 4.2.3名字與別名是否重名
    let selectDuplicate = 'select * from article where name = ? or alias = ?'
    db.query(selectDuplicate, [req.body.name, req.body.alias], (err, results) => {
        if(err) return res.cc(err)
        if(results.length == 2) return res.cc('文章名字和別名已被占用')
        if(results.length == 1 && results[0].name == req.body.name && results[0].alias == req.body.alias) return res.cc('文章名字和別名已被占用')
        if(results.length == 1 && results[0].name == req.body.name) return res.cc('文章名字被占用')
        if(results.length == 1 && results[0].alias == req.body.alias) return res.cc('文章別名被占用')
        // 4.2.4實現文章分類新增
        let addArt = 'insert into article set ?'
        db.query(addArt, req.body, (err, results) => {
            if(err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('新增文章失敗')
            res.cc('新增文章分類成功', 0)
        })
    })
}

// 4.3.1根據idshanchuwenzhan
function deleteCate(req, res) {
    // 4.3.3實現刪除功能
    let deleteId = 'update article set is_delete = 1 where id = ?'
    db.query(deleteId, req.params.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('刪除文章失敗')
        res.cc('刪除文章分類成功', 0)
    })
}

// 4.4.1根據id獲取文章分類
function requireArt(req,res) {
    // 4.4.2
    let selectArt = 'select * from article where id = ?'
    db.query(selectArt, req.params.id, (err, results) => {
        if (err) return res.cc(err)
        if (results.length !== 1 || results[0].is_delete == 1) return res.cc('沒有該文章')
        res.send({
            status : 0,
            msg : '獲取文章分類數據成功',
            data : results[0]
        })
    })
}

// 4.5.1根據id更新文章
function updateArt(req, res) {
    // 4.5.2查看是否重名
    // -----------------這裡需要先將自己這一項排除出來
    let selectIdDuplicate = 'select * from article where id != ? and (name = ? or alias = ?)'
    db.query(selectIdDuplicate, [req.body.id, req.body.name, req.body.alias], (err, results) => {
        if (err) return res.cc(err)
        if (results.length == 2) return res.cc('文章名稱和別名已被占用')
        if (results.length == 1 && results[0].name == req.body.name && results[0].alias == req.body.alias) return res.cc('文章名稱和別名已被占用')
        if (results.length == 1 && results[0].name == req.body.name) return res.cc('文章名稱已被占用')
        if (results.length == 1 && results[0].alias == req.body.alias) return res.cc('別名已被占用')
        let updateIdArt = 'update article set ? where id = ?'
        db.query(updateIdArt, [req.body, req.body.id] , (err,results) => {
            if (err) return res.cc(err)
            if (results.affectedRows !== 1) return res.cc('文章更新失敗')
            res.cc('更新分類信息成功', 0)
        })
    })
}
module.exports = {
    getArticleList,
    addCates,
    deleteCate,
    requireArt,
    updateArt
}

這是路由模塊,上面是路由的處理函數

// 4.文章類別管理
const express = require('express')
const router = express.Router()
const routerhanlder = require('../router_handler/article')
// 4.1獲取文章分類列表
router.get('/cates',routerhanlder.getArticleList)

// 4.2新增文章分類
const expressJoi = require('@escook/express-joi')
const {add_article_list, update_id_cate} =require('../schema/article')
router.post('/addcates',expressJoi(add_article_list),routerhanlder.addCates)

// 4.3根據id刪除文章
const {delete_id_cate} = require('../schema/article')
router.get('/deletecate/:id',expressJoi(delete_id_cate),routerhanlder.deleteCate)

// 4.4根據id獲取文章分類數據
router.get('/cates/:id',expressJoi(delete_id_cate), routerhanlder.requireArt)

// 4.5根據id更新文章分類數據
const {update_id_Art} = require('../schema/article')
router.post('/updatecate',expressJoi(update_id_Art) ,routerhanlder.updateArt)

module.exports = router

然後我們的規則

const joi = require('joi')

// 4.2.2新增文章規則
const name = joi.string().required()
const alias = joi.string().alphanum().required()

// 4.3.2根據id刪除文章 註意這個id是動態的而且是get請求,所以不再是body數據
const id = joi.number().integer().min(1).required()
module.exports.add_article_list = {
    body : {
        name,
        alias
    }
}

module.exports.delete_id_cate = {
    params : {
        id
    }
}

module.exports.update_id_Art = {
    body : {
        id : id,
        name : name,
        alias : alias
    }
}

7.

最後是我們的添加文章這個功能,就是往每一個剛纔創建好的文章裡面,添加新文章,直接看邏輯的實現吧

// 5.1.1發佈文章函數
const path = require('path')
const db = require('../mysql')
function addCate(req, res) {
    // 5.1.5因為上傳表單無法用joi所以要單獨規定
    if (!req.file || req.file.fieldname !== 'cover_img') return res.cc('請上傳圖片')
    // 5.1.6事先發佈文章功能
    const cateObj = {
        ...req.body,
        cover_img : path.join('/uploads', req.file.filename),
        pub_date : new Date(),
        author_id : req.user.id
    }
    let addInsert = 'insert into articles set ?'
    db.query(addInsert,  cateObj, (err, results) => {
        if (err) return res.cc(err)
        if (results.affectedRows !== 1) return res.cc('發佈文章失敗')
        res.cc('發佈文章成功', 0)
    })
    
}


module.exports = {
    addCate
}

 

 

 

 

// 1.4初始化路由相關文件夾 不光要給路由分裝一個模塊 裡面的處理函數也要有一個模塊 const express = require('express') const { append } = require('express/lib/response') const router = express.Router() // 1.4.2導入路由處理函數 const routerHandler = require('../router_handler/user')
// 註冊 // 2.2.2導入joi驗證輸入進來的是否合法 const expressJOI = require('@escook/express-joi') const {schema_user_info} = require('../schema/user') router.post('/reguser',expressJOI(schema_user_info), routerHandler.getReguser)
// 2.4登錄 // 2.4.1添加語法規則,可以直接用註冊的 router.post('/login',expressJOI(schema_user_info), routerHandler.getLogin)
module.exports = router



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 緩存穿透 問題描述 緩存穿透是指查詢一個一定不存在的數據,由於緩存是不命中時需要從資料庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到資料庫去查詢,進而給資料庫帶來壓力。 解決方案 緩存空值,即對於不存在的數據,在緩存中放置一個空對象(註意,設置過期時間) 2 緩存擊穿 問 ...
  • 導讀: 眾所周知,信息時代下的數據就是能源,就是生產力。但是面對海量、紛繁的數據,特別是在金融領域,如何充分地利用數據是核心問題。本次分享主要想和大家一起探討下,在金融風控場景下,如何通過數據對齊模型和業務目標,哪些數據、方法可以應用於風控模型,通過哪些指標可以正確地評估模型效果,以及最終如何用數據 ...
  • 本文介紹如何使用 SQL WHERE 子句指定搜索條件,過濾返回的數據。還介紹如何檢驗相等、不相等、大於、小於、值的範圍以及 NULL 值等。 一、使用 WHERE 子句 資料庫表一般包含大量的數據,很少需要檢索表中的所有行。通常只會根據特定操作或報告的需要提取表數據的子集。只檢索所需數據需要指定搜 ...
  • 為了更好地幫助開發者,官方文檔特意整理出“接入智慧生活App”專題。跟緊小編的步伐,趕緊來看看本次文檔更新內容~ ...
  • 你或許不知道它在什麼時候橫空出世,當你意識到的時候,它已如空氣環繞在你身邊。 截至2021年6月,我國即時通信用戶規模達到9.83億,以97%的網民使用占比,在用戶量級之巔一覽眾山小。今天就來聊聊這個“如空氣般存在”的能力——IM(Instant Messaging)即時消息。(數據來自中國互聯網路 ...
  • 微信小程式集成vant,大概的過程先通過npm 安裝vant包->微信小程式設置npm環境變數->將npm中的vant包導成miniprogram_npm 開發環境 macOS ,微信小程式模版【支持騰訊雲】 安裝vant包 cd miniprogram # 通過 npm 安裝 npm i @van ...
  • 4月25日,“共建新技術,開拓新領域”OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)技術日在深圳順利召開。OpenHarmony 攜手各共建單位、生態伙伴分享技術創新、生態共建、教育發展等方面的最新進展和實踐成果。 ...
  • 想必大部分做頂部導航欄(position: fixed;)的都遇見過導航欄遮住鏈接鏈接對象部分內容這種情況吧,如下圖所示,我的頂部導航欄的高度為9vh,video元素是“本店快遞流程”(錨鏈接)跳轉的元素 當我點擊該鏈接時,video元素被遮去了9vh的高度,這是為什麼呢? 我查看了一下源代碼(vi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...