利用node.js搭配express框架写后端接口(一)

news2025/2/28 10:08:39

Node.js 凭借其高效的非阻塞 I/O 操作、事件驱动架构以及轻量级的特点,成为了开发高性能服务器应用的热门选择。Express 框架作为 Node.js 上最流行的 Web 应用框架之一,以其简洁的 API 和丰富的中间件生态系统,极大地简化了 Web 后端开发流程。本文将引导你如何使用 Node.js 和 Express 框架构建一个简单的后端接口。

目录

一、初始化项目

1.创建项目

2.配置cors跨域

3.配置解析表单数据的中间件

4.初始化路由相关的文件夹

 5.初始化用户路由模块

6.抽离用户路由模块中的处理函数

二、新建数据库表

1.新建数据库

2.新建表

三、安装并配置MySQL模块

1.安装MySQL模块

2.连接数据库

四、登录、注册

1.注册

1.1检测表单数据是否合法

1.2检测用户名是否被占用

1.3对密码进行加密处理

1.4插入新用户

1.5优化res.send()代码

1.6优化表单数据验证

2.登录

 2.1检测登录表单的数据是否合法

2.2根据用户名查询用户的数据

2.3判断用户输入的密码是否正确

2.4生成jwt的token字符串

2.5配置解析token的中间件


一、初始化项目

1.创建项目

新建一个文件夹作为项目的根目录,并在项目根目录中运行如下命令,初始化包的管理配置文件:

npm i express@4.17.1

然后在项目中新建app.js作为整个项目的入口文件,并初始化如下代码:

//导入express
const express = require('express')
//创建服务器对象
const app = express() 
//启动服务器
app.listen(3007, () => {
    console.log('api server htttp://127.0')
})

2.配置cors跨域

在文件夹的终端运行如下的命令,安装cors中间件:

npm i cors@2.8.5

然后再app.js中导入并配置cors中间件:

//导入并配置cors中间件
const cors = require('cors')
app.use(cors())

3.配置解析表单数据的中间件

通过使用如下代码,配置解析application/x-www-form-urlencoded格式的表单数据的中间件:

//配置解析表单数据的中间件--注意:只能解析application/x-www-from-urlencoded格式的表单数据的中间件
app.use(express.urlencoded({extended:false}))

4.初始化路由相关的文件夹

(1)在项目的根目录中,新建router文件夹,用来存放所有的路由模块。路由模块中,只存放客户端的请求与处理函数之间的映射关系。

 (2)在项目的根目录中,新建router_handler文件夹,用来存放所有的路由处理函数模块,这里专门负责存放每个路由对应的处理函数。

 5.初始化用户路由模块

在router文件夹中,新建user.js文件,作为用户的路由模块,代码如下:

const express = require('express')
//创建路由对象
const router = express.Router()

//注册新用户
router.post('/register', (req, res) => { 
    res.send('注册成功')
})

//登录
router.post('/login', (req, res) => { 
    res.send('登录成功')
})

//导出路由对象
module.exports = router

在app.js中导入并使用该用户路由模块

//导入并注册用户路由模块
const userRouter = require('./routes/user')
app.use('/api', userRouter)

6.抽离用户路由模块中的处理函数

为了保证路由模块的纯粹性,所有的路由处理函数,必须抽离到对应的路由处理函数模块中。

在router_handler/user.js文件中,使用exports对象,分别向外共享如下两个路由处理函数:

//注册用户的处理函数
exports.regUser = (req, res) => {
    res.send('注册成功')
}
//登录的处理函数
exports.login = (req, res) => {
    res.send('登录成功')
}

将router/user.js文件中的代码修改如下结构

const express = require('express')
//创建路由对象
const router = express.Router()

//打入用户路由处理函数模块
const userHandler = require('./router_handler/user')

//注册新用户---原
// router.post('/register', (req, res) => { 
//     res.send('注册成功')
// })
//注册新用户--新
router.post('/register',userHandler.regUser )

//登录----原
// router.post('/login', (req, res) => { 
//     res.send('登录成功')
// })
//登录---新
router.post('/login',userHandler.login)

//导出路由对象
module.exports = router

二、新建数据库表

在创建库表之前确保已经安装了数据库。

1.新建数据库

2.新建表

在新建的test_mysql数据库中,新建ev_users表 。点击新建查询,输入一下SQL语句

use test_mysql;
CREATE TABLE ev_users
(
    id INT NOT NULL AUTO_INCREMENT,
    username VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    nickname VARCHAR(255),
    email VARCHAR(255),
    user_pic TEXT,
    PRIMARY KEY (id)
)

然后点击旁边的运行,下方如果出现OK,就表示创建成功

 然后在表那边点击刷新就可以看见新创建的表了

三、安装并配置MySQL模块

在api接口项目中,需要安装并配置MySQL这个第三方模块,俩连接和操作MySQL数据库

1.安装MySQL模块

在根目录的终端中运行如下命令:

npm i mysql@2.18.1

2.连接数据库

在项目中的根目录下新建db文件夹,并在其下方新建index.js文件,在此文件中创建数据库的连接对象

//导入MySQL模块
const mysql = require('mysql')
//创建数据库连接对象
const db = mysql.createPool({
    host: '127.0.0.1',       // 数据库主机名
    user: '你的数据库用户名',            // 数据库用户名
    password: '你的数据库密码', // 数据库密码
    database: 'test_mysql' // 数据库名
})
// 导出连接池
module.exports = db;

四、登录、注册

1.注册

1.1检测表单数据是否合法

在router_handle/user.js这个文件中判断用户名和密码是否为空,具体代码如下:

//注册用户的处理函数
exports.regUser = (req, res) => {
    //获取客户端提交到服务器的用户信息
    const userInfo = req.body
    // console.log(userInfo, '这个信息是:')
    //对表单中的数据,进行合法的校验
    if (!userInfo.username || !userInfo.password) {
        return res.send({
            code: 404,
            message: 'fail',
            detail:'用户名或密码不合法'
        })
    }
}

1.2检测用户名是否被占用

(1)导入数据库操作模块

在router_handle/user.js这个文件中导入数据库模块

//导入数据库
const db = require('../db/index')

(2)定义SQL语句

//定义SQL语句,查询用户名是否被占用
    const sqlStr='select * from ev_users where username=?'

(3)执行SQL语句并根据结果判断用户名是否被占用

db.query(sqlStr, userInfo.username, (err, results) => {
        //执行SQL语句失败
        if (err) {
            return res.send({
                code:500,
                message: 'fail',
                detail:err.message
            })
        }
        //判断用户名是否被占用
        if (results.length > 0) {
            return res.send({
                code:409,
                message: 'fail',
                detail:'用户名被占用,请更换其他用户名'
            })
        }
    })

1.3对密码进行加密处理

为了保证密码的安全性,不建议在数据库以明文的形式保存用户密码,推荐对密码进行加密存储。

在当前所做项目中,使用bcryptjs对用户密码进行加密,优点在于:

  • 加密之后的密码,无法被逆向破解;
  • 同一明文密码多次加密,得到的加密结果各不相同,保证了安全性。

(1)安装bcryptjs

在项目终端运行如下命令:

npm i bcryptjs@2.4.3

 (2)导入bcryptjs

在router_handle/user.js这个文件中导入bcryptjs

//导入bcryptjs
const bcrypt=require('bcryptjs')

(3)使用bcryptjs

在注册用户的处理函数中,确认用户名可用之后,调用bcrypt.hashSync(明文密码,随机盐的长度)方法,对用户的密码进行加密处理:

//调用bcryptjs-hashSync()对密码进行加密
        console.log(userInfo,'加密前:')
        userInfo.password = bcrypt.hashSync(userInfo.password, 10)
        console.log(userInfo,'加密后:')

1.4插入新用户

(1)定义插入用户的SQL语句

 //定义插入新用户的SQL语句
        const sql='insert into ev_users set ?'

(2)调用db.query()执行SQL语句,插入新用户

//调用db.query()执行SQL语句
        db.query(sql, { username: userInfo.username, password: userInfo.password }, (err, results) => {
            //判断SQL语句是否执行成功
            if (err) {
                return res.send({
                    code:500,
                message: 'fail',
                detail:err.message
                })
            }
            //判断影响函数是否为1
            if (results.affectedRows != 1) {
                return res.send({
                    code: 500,
                    message: 'fail',
                    detail:'注册用户失败,请稍后再试'
                })
            } else {
                return res.send({
                    code: 200,
                    message: 'success',
                    detail:'注册成功'
                })
            }
        })

1.5优化res.send()代码

在以上的处理函数中我们可以发现,需要多次调用到res.send()向客户端响应处理失败的结果,为了简化以上代码,这里重新封装了一个res.trans()函数。

在app.js中,所有路由之前,声明一个全局中间件,为res对象挂载一个res.trans()函数

//封装res.trans
app.use((req, res, next) => {
    //code默认值为500,表示失败的情况
    //err可能是一个错误对象,也可能是一个错误的描述字符串
    res.trans = (err,code=500) => {
        res.send({
            code,
            message: 'fail',
            detail:err instanceof Error?err.detail:err
        })
    }
    next()
})

在router_handle/user.js这个文件中有关注册这个处理函数进行重新修改

//注册用户的处理函数
exports.regUser = (req, res) => {
    //获取客户端提交到服务器的用户信息
    const userInfo = req.body
    // console.log(userInfo, '这个信息是:')
    
    //对表单中的数据,进行合法的校验
    if (!userInfo.username || !userInfo.password) {
        // return res.send({
        //     code: 400,
        //     message: 'fail',
        //     detail: '用户名或密码不合法'
        // })
        return res.trans('用户名或密码不合法',400)
    }
    // } else {
    //     return res.send({
    //         code: 200,
    //         message: 'success',
    //         detail:''
    //     })
    // }
    //定义SQL语句,查询用户名是否被占用
    const sqlStr='select * from ev_users where username=?'
    db.query(sqlStr, userInfo.username, (err, results) => {
        //执行SQL语句失败
        if (err) {
            //原--未封装
            // return res.send({
            //     code:500,
            //     message: 'fail',
            //     detail:err.message
            // })
            //现--分装后
            return res.trans(err)
        }
        //判断用户名是否被占用
        if (results.length > 0) {
            //原--未封装
            // return res.send({
            //     code:409,
            //     message: 'fail',
            //     detail:'用户名被占用,请更换其他用户名'
            // })
            //现--分装后
            return res.trans('用户名被占用,请更换其他用户名',409)
        }
        //调用bcryptjs-hashSync()对密码进行加密
        // console.log(userInfo,'加密前:')
        userInfo.password = bcrypt.hashSync(userInfo.password, 10)
        // console.log(userInfo, '加密后:')
        //定义插入新用户的SQL语句
        const sql = 'insert into ev_users set ?'
        //调用db.query()执行SQL语句
        db.query(sql, { username: userInfo.username, password: userInfo.password }, (err, results) => {
            //判断SQL语句是否执行成功
            if (err) {
                // return res.send({
                //     code:500,
                // message: 'fail',
                // detail:err.message
                // })
                 //现--分装后
                return res.trans(err)
            }
            //判断影响函数是否为1
            if (results.affectedRows != 1) {
                // return res.send({
                //     code: 500,
                //     message: 'fail',
                //     detail:'注册用户失败,请稍后再试'
                // })
                 //现--分装后
            return res.trans('注册用户失败,请稍后再试')
            } else {
                return res.send({
                    code: 200,
                    message: 'success',
                    detail:'注册成功'
                })
            }
        })
    })
}

1.6优化表单数据验证

表单验证的原则:前端验证为辅,后端验证为主,后端不能相信前端提交过来的任何内容。在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且,后端做为数据合法性验证的最后一个关口,在拦截非法数据方面,起到了至关重要的作用。
单纯的使用 if...e1se...的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此,推荐使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性,让后端程序员把更多的精力放在核心业务逻辑的处理上。

(1)安装joi包,为表单中携带的每个数据项,定义验证规则

npm i joi

(2)安装@escook/express-joi中间件,来实现自动对表单数据进行验证的功能

npm i @escook/express-joi

(3)新建/schema/user.js用户信息验证规则模块,并初始化代码

//导入joi
const joi = require('joi')
/**
 * string 值必须是字符串
 * alphanum 值只能是包含a-zA-Z0-9的字符串
 * min(length)最小长度
 * max(length)最大长度
 * required() 值是必填项,不能为undefined
 * pattern(正则表达式) 值必须符合正则表达式的规则
 */
//定义用户名和密码的验证
const username = joi.string().alphanum().min(1).max(10).required()
const password = joi.string().pattern(/^[\S]{6,12}$/).required()

//定义验证注册和登录表单数据的规则对象
exports.reg_login_schema = {
    body: {
        username,
        password
    }
}

(4)修改router/user.js中的代码

const express = require('express')
//创建路由对象
const router = express.Router()

//打入用户路由处理函数模块
const userHandler = require('../router_handler/user')

//导入验证数据的中间件
const expressJoi = require('@escook/express-joi')
//导入需要的验证规则对象
const {reg_login_schema} = require('../schema/user')


//注册新用户--新
//1.在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证
//2.数据验证通过后,会把这次请求流转给后面的路由处理函数
//3.数据验证失败后,终止后端代码的执行,并抛出一个全局的error错误,进入全局错误级别中间件中进行处理
router.post('/register',expressJoi(reg_login_schema),userHandler.regUser )


//登录---新
router.post('/login',userHandler.login)

//导出路由对象
module.exports = router

(5)在app.js的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端

//导入joi
const joi = require('joi')
//中间部分的省略

//定义错误级别的中间件
app.use((err, req, res) => {
    //验证失败导致的错误
    if (err instanceof joi.ValidationError) {
        return res.trans(err)
    }
    //未知的错误
    res.trans(err)
        
})

(6)在router_handler/user.js文件中,注册用户处理函数这一块注释掉下面的代码

// if (!userInfo.username || !userInfo.password) {
    //     return res.trans('用户名或密码不合法',400)
    // }

2.登录

2.1检测登录表单的数据是否合法

将router/user.js中登录的路由代码修改如下

//登录---新
router.post('/login',expressJoi(reg_login_schema),userHandler.login)

2.2根据用户名查询用户的数据

(1)接收表单数据

//获取客户端提交到服务器的用户信息
    const userInfo = req.body

(2)定义SQL语句

//定义SQL语句,查询用户名是否被占用
    const sqlStr='select * from ev_users where username=?'

(3)执行SQL语句,查询用户的数据

db.query(sqlStr, userInfo.username, (res, results) => {
        //执行SQL语句失败
        if (err) {
            return res.trans(err)
        }
        //执行SQL语句成功,但是查询到数据条数不等于1
        if (results.length != 1) {
            return res.trans('登录失败')
        }
    })

2.3判断用户输入的密码是否正确

核心:调用bcrypt.compareSync(用户提交的密码,数据库中的密码)方法比较密码是否一致。

 db.query(sqlStr, userInfo.username, (err, results) => {
        //执行SQL语句失败
        if (err) {
            return res.trans(err)
        }
        //执行SQL语句成功,但是查询到数据条数不等于1
        if (results.length != 1) {
            return res.trans('登录失败',405)
        }
        //用户输入的密码和数据库中存储的密码进行对比
        const compareResult = bcrypt.compareSync(userInfo.password, results[0].password)
        console.log('这是:',compareResult,results[0].password,userInfo.password)
        //如果对比的结果等于false,则证明用户输入的密码错误
        if (!compareResult) {
            return res.trans('登录失败',400)
        } 
        res.send('ok')
    })

2.4生成jwt的token字符串

注意:在生成token字符串的时候,一定要提出密码和头像的值

(1)通过es6的高级语法,快速剔除密码和头像的值,该代码书写于上述对比输入密码语句之后

//在服务器端生成token字符串
 const user = { ...results[0] ,password:'',user_pic:''}
 console.log(user,'这是:')

(2)安装生成token字符串的包

npm i jsonwebtoken

(3)在router_handler/user.js文件的头部区域,导入jsonwebtoken包

//导入生成的token包
const jwt=require('jsonwebtoken')

(4)创建config.js文件,并向外共享加密和还原token的jwtSecretKey字符串

module.exports = {
    //加密和解密的token密钥
    jwtSecretKey: 'test',
    //token有效期
    expiresIn:'10h'
}

(5)将用户信息对象加密成token字符串

//导入密钥配置文件
const config=require('../config.js')

//对用户的信息进行加密,生成token字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresIn })
console.log('这是:', tokenStr)

(6)将生成的token字符串响应给客户端

 //调用res.send()将token响应给客户端
 res.send({
      code: 200,
      message: 'success',
      token: 'Bearer '+tokenStr,
      detail:'登
})

2.5配置解析token的中间件

(1)安装解析token的中间件

npm i express-jwt@5.3.3

(2)在app.js中注册路由之前,配置解析token的中间件

//一定要在路由之前配置解析token的中间件
const { expressJwt } = require('express-jwt')
const config = require('./config')
app.use(expressJwt({ secret: config.jwtSecretKey}).unless({path:[/^\/api/]}))

(3)在app.js中的错误级别中间件中,铺货并处理token认证失败后的错误

//定义错误级别的中间件---暂不使用
app.use((err, req, res,next) => {
    //验证失败导致的错误
    // if (err instanceof joi.ValidationError) return res.trans(err)
    if(err.name==='UnauthorizedError') return res.trans('身份认证失败',401)
    // //未知的错误
    res.trans(err)    
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2307333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS中shell脚本对多台机器执行下载安装

1.建立免密ssh连接 详情见这篇: CentOS建立ssh免密连接(含流程剖析)-CSDN博客 2.脚本编写 我这里只是简单写了个demo进行演示,如果服务器很多可以先暂存成文件再逐行读取host进行连接并执行命令 用node1去ssh连接node2和node…

深入剖析:自定义实现C语言中的atoi函数

在C语言的标准库中, atoi 函数是一个非常实用的工具,它能够将字符串形式的数字转换为对应的整数。然而,当我们深入探究其实现原理时,会发现其中蕴含着许多有趣的编程技巧和细节。本文将详细讲解如何自定义实现一个类似 atoi 功能的…

Flutter 学习之旅 之 flutter 在 Android 端读取相册图片显示

Flutter 学习之旅 之 flutter 在 Android 端读取相册图片显示 目录 Flutter 学习之旅 之 flutter 在 Android 端读取相册图片显示 一、简单介绍 二、简单介绍 image_picker 三、安装 image_picker 四、简单案例实现 五、关键代码 代码说明: 一、简单介绍 Fl…

数据结构秘籍(一)线性数据结构

1.数组 数组(Array)是一种很常见的数据结构。它由相同类型的元素(element)组成,并且是使用一块连续的内存来存储。 我们直接可以利用元素的索引(index)计算出该元素对应的存储地址。 数组的特…

Linux(centos)系统安装部署MySQL8.0数据库(GLIBC版本)

前言 MySQL 是一款开源的关系型数据库管理系统(RDBMS),主要用于‌结构化数据的存储、管理和检索‌。 一、检查环境 安装前检查服务器glibc版本,下载对应版本包 rpm -qa | grep glibc mysql安装包及依赖包已整理好&#xff0c…

Redis缓存一致性难题:如何让数据库和缓存不“打架”?

标题:Redis缓存一致性难题:如何让数据库和缓存不“打架”?(附程序员脱发指南) 导言:当数据库和缓存成了“异地恋” 想象一下:你刚在美团下单了一份麻辣小龙虾,付款后刷新页面&#…

【R包】pathlinkR转录组数据分析和可视化利器

介绍 通常情况下,基因表达研究如微阵列和RNA-Seq会产生数百到数千个差异表达基因(deg)。理解如此庞大的数据集的生物学意义变得非常困难,尤其是在分析多个条件和比较的情况下。该软件包利用途径富集和蛋白-蛋白相互作用网络&…

1.68M 免安装多格式图片批量转 webp 无广告软件推荐

软件介绍 今天要给大家分享一款超实用的图片处理工具,它能实现多格式图片向 webp 格式的转换,无论是 jpg、png、tif、gif 还是 webp 格式自身的图片,都能批量且借助多线程技术进行转换。 直接打开就能用,体积小巧,仅 …

《Qt窗口动画实战:Qt实现呼吸灯效果》

Qt窗口动画实战:Qt实现呼吸灯效果 在嵌入式设备或桌面应用中,呼吸灯效果是一种常见且优雅的UI动画,常用于指示系统状态或吸引用户注意。本文将介绍如何使用Qt动画框架实现平滑的呼吸灯效果。 一、实现原理 利用Qt自带的动画框架来实现&…

详解Tomcat下载安装以及IDEA配置Tomcat(2023最新)

目录 步骤一:首先确认自己是否已经安装JDK步骤二:下载安装Tomcat步骤三:Tomcat配置环境变量步骤四:验证Tomcat配置是否成功步骤五:为IDEA配置Tomcat 步骤一:首先确认自己是否已经安装JDK jdk各版本通用安…

AI如何通过大数据分析提升制造效率和决策智能化

人工智能(AI)与大数据技术的融合,不仅重新定义了生产流程,更让企业实现了从“经验驱动”到“数据智能驱动”的跨越式升级。 从“模糊经验”到“精准洞察”​​ 传统制造业依赖人工经验制定生产计划,但面对复杂多变的市…

kafka-关于ISR-概述

一. 什么是ISR ? Kafka 中通常每个分区都有多个副本,其中一个副本被选举为 Leader,其他副本为 Follower。ISR 是指与 Leader 副本保持同步的 Follower 副本集合。ISR 机制的核心是确保数据在多个副本之间的一致性和可靠性,同时在 …

使用 Polars 进行人工智能医疗数据分析(ICU数据基本测试篇)

引言 在医疗领域,数据就是生命的密码,每一个数据点都可能蕴含着拯救生命的关键信息。特别是在 ICU 这样的重症监护场景中,医生需要实时、准确地了解患者的病情变化,以便做出及时有效的治疗决策。而随着医疗技术的飞速发展&#x…

超过DeepSeek、o3,Claude发布全球首个混合推理模型,并将完成新一轮35亿美元融资...

Anthropic于2025年2月25日发布全球首个“混合推理”AI模型Claude 3.7 Sonnet,并在融资层面取得重大进展,计划完成35亿美元的新一轮融资,估值将达615亿美元。以下是核心信息整理: 技术突破:双思维模型与代码能力 1. 混合…

OmniParser v2本地部署(2)部署omnitool(包含自动化控制工具)

1 配置omniparserserver 1.1 配置conda环境、下载依赖和权重 我建议按照OmniParser v2本地部署(1)部署OmniParser_v2模型先设置一次,其中所创造的conda环境,和这一步相似 1.2 启动omniparserserver 进入OmniParser/omnitool/o…

音频进阶学习十六——LTI系统的差分方程与频域分析一(频率响应)

文章目录 前言一、差分方程的有理式1.差分方程的有理分式2.因果系统和ROC3.稳定性与ROC 二、频率响应1.定义2.幅频响应3.相频响应4.群延迟 总结 前言 本篇文章会先复习Z变换的有理分式,这是之前文章中提过的内容,这里会将差分方程和有理分式进行结合来看…

JavaWeb-ServletContext应用域接口

文章目录 ServletContext接口简介获取一个ServletContext对象ServletContext接口中的相关方法获取应用域配置参数关于应用域参数的配置要求getContextPath获取项目路径getRealPath获取真实路径log系列方法添加相关日志增删查应用域属性 ServletContext接口简介 ServletContext…

SQL命令详解之操作数据表

​​​​​ 操作数据表 操作数据表是数据库管理系统中用于存储、管理和操作数据的核心结构。数据表通常由行和列组成,每一列代表一种数据类型(例如,整数、字符、日期等),而每一行代表一条记录(即数据项&a…

Javaweb后端数据库多表关系一对多,外键,一对一

多表关系 一对多 多的表里,要有一表里的主键 外键 多的表上,添加外键 一对一 多对多 案例

【洛谷贪心算法题】P2240部分背包问题

【解题思路】 贪心策略选择 对于部分背包问题,关键在于如何选择物品放入背包以达到最大价值。由于物品可以分割,遍历排序后的物品数组,根据物品重量和背包剩余容量的关系,决定是将整个物品放入背包还是分割物品放入背包&#xff…