前后端交互—开发一个完整的服务器

代码下载

初始化

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

npm init -y

运行如下的命令,安装 express、cors:

npm i express cors

在项目根目录中新建 app.js 作为整个项目的入口文件,配置 cors 跨域中间件、解析表单数据的中间件,并启动服务器:

// 导入 express 模块
const express = require('express');
// 创建 express 的服务器实例
const app = express();

// 配置跨域中间件
const cors = require('cors');
app.use(cors());

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

// 指定端口号并启动web服务器
app.listen('80', function() {
    console.log('server running at http://127.0.0.1:80');
});

因为在处理函数中,需要多次调用 res.send() 向客户端响应 处理失败 的结果,为了简化代码, 可以手动封装一个 res.cc() 函数。在 app.js 中所有路由之前,声明一个全局中间件,为 res 对象挂载一个 函数 :

// 在 路由 之前自定义中间件,处理错误的响应数据
app.use(function(req, res, next) {
    // status 默认值1,表示失败,0表示成功
    // err 可能是错误对象,也可能是错误描述字符串
    res.cc = function(err, status = 1) {
        res.send({
            status: status,
            message: err instanceof Error ? err.message : err
        });
    };

    next();
});
  1. 在项目根目录中,新建 router 文件夹,用来存放所有的 路由 模块,路由模块中,只存放客户端的请求与处理函数之间的映射关系
  2. 在项目根目录中,新建 routerHandler 文件夹,用来存放所有的 路由处理函数模块 路由处理函数模块中,专门负责存放每个路由对应的处理函数
  3. 在项目根目录中,新建 db 文件夹,用来存放所有的 数据库 模块
  4. 在项目根目录中,新建 middleware 文件夹,用来存放所有的 自定义中间件 模块

登录注册

新建 users 表,在 my_db_01 数据库中,新建 ev_users 表如下:
请添加图片描述

安装并配置 mysql 模块,需要安装并配置 mysql 这个第三方模块,来连接和操作 MySQL 数据库,运行如下命令,安装 mysql 模块:

npm i mysql

在项目根目录中 db 文件夹新建 index.js 文件,在此自定义模块中创建数据库的连接对象:

const mysql = require('mysql');
const db = mysql.createPool({
    host: 'localhost',
    port: '3306',
    user: 'root',
    password: 'admin123',
    database: 'my_db_01'
});

module.exports = db;

初始化用户路由模块

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

const express = require('express') // 创建路由对象
const router = express.Router()
// 注册新用户
router.post('/signin', (req, res) => {
  res.send('signin OK')
})
// 登录
router.post('/login', (req, res) => {
  res.send('login OK')
})
// 将路由对象共享出去 module.exports = router

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

// 配置路由
const user = require('./router/user');
app.use('/api', user);

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

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

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

/**
* 在这里定义和用户相关的路由处理函数,供 /router/user.js 模块进行调用 */

// 注册用户的处理函数 
exports.signin = (req, res) => {
  res.send('signin OK')
}

// 登录的处理函数
exports.login = (req, res) => {
  res.send('login OK')
}

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

const express = require('express');
const router = express.Router();
const handler = require('../routerHandler/user');

// 注册
router.post('/signin', handler.signin);

// 登录
router.post('/login', handler.login);

module.exports = router;

joi 表单数据验证

表单验证的原则:前端验证为辅,后端验证为主,后端 永远不要相信 前端提交过来的 任何内容。

在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且, 后端做为数据合法性验证的最后 一个关口 ,在拦截非法数据方面,起到了至关重要的作用。

单纯的使用 if…else… 的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此, 推荐使用 第三方数据验证模块 ,来降低出错率、提高验证的效率与可维护性, 让后端程序员把更多的精力放在核心业务逻辑的处理上。

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

npm install joi

在 /middleware/expressJoi.js 中,使用 joi 对象,定义并向外共享表单数据验证中间件:

const joi = require('joi');

const expressJoi = function (schemas, options = { strict: false }) {
  // 自定义校验选项
  // strict 自定义属性,默认不开启严格模式,会过滤掉那些未定义的参数项
  //        如果用户指定了 strict 的值为 true,则开启严格模式,此时不会过滤掉那些未定义的参数项
  if (!options.strict) {
    // allowUnknown 允许提交未定义的参数项
    // stripUnknown 过滤掉那些未定义的参数项
    options = { allowUnknown: true, stripUnknown: true, ...options }
  }

  // 从 options 配置对象中,删除自定义的 strict 属性
  delete options.strict

  // TODO: 用户指定了什么 schema,就应该校验什么样的数据
  return function (req, res, next) {
    ['body', 'query', 'params'].forEach(key => {
      // 如果当前循环的这一项 schema 没有提供,则不执行对应的校验
      if (!schemas[key]) return

      // 执行校验
      const schema = joi.object(schemas[key])
      const { error, value } = schema.validate(req[key], options)

      if (error) {
        console.log('---------------');
        // 校验失败
        throw error
      } else {
        // 校验成功,把校验的结果重新赋值到 req 对应的 key 上
        req[key] = value
      }
    })

    // 校验通过
    next()
  }
}

module.exports = expressJoi

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

// @hapi/joi 包,为表单中携带的每个数据项,定义验证规则
const joi = require('joi');


// * string() 值必须是字符串
// * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 * min(length) 最小长度
// * max(length) 最大长度
// * required() 值是必填项,不能为 undefined
// * pattern(正则表达式) 值必须符合正则表达式的规则
const username = joi.string().alphanum().min(2).max(20).required();
const password = joi.string().required().pattern(/^[\S]{6,16}$/);

exports.schema = {
    user: {
        body: {
            username,
            password
        }
    }
}

修改 /router/user.js 中的代码如下:

// 导入验证表单数据的中间件
const expressJoi = require('../middleware/expressJoi');
// 导入需要的验证规则对象
const { schema } = require('../schema/user');

// 为 注册新用户 配置表单验证中间件
// 在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证
//      数据验证通过后,会把这次请求流转给后面的路由处理函数
//      数据验证失败后,终止后续代码的执行,并抛出一个全局的 Error 错误,进入全局错误级别中间件中进行 处理
router.post('/signin', expressJoi(schema.user), handler.signin);

// 登录
router.post('/login', expressJoi(schema.user), handler.login);

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

// 配置 错误处理 中间件
const joi = require('joi');
app.use(function(err, req, res, next) {
    // console.log(err);
    if (err instanceof joi.ValidationError) {
        // 处理数据校验错误
    } else {
        // 处理其他未知错误
    }
    return res.cc(err);
});

加密处理

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

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

运行如下命令,安装 bcryptjs:

npm i bcryptjs

调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理:

const bcrypt = require('bcryptjs');
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
userinfo.password = bcrypt.hashSync(userinfo.password, 10);

调用 bcrypt.compareSync(用户提交的密码, 数据库中的密码) 方法比较密码是 否一致,返回值是布尔值(true 一致、false 不一致):

// 拿着用户输入的密码,和数据库中存储的密码进行对比
const compareResult = bcrypt.compareSync(userinfo.password, results[0].password);

// 如果对比的结果等于 false, 则证明用户输入的密码错误 
if (!compareResult) {
    console.log('密码错误!');
}

注册

  1. 检测表单数据是否合法
  2. 检测用户名是否被占用
  3. 对密码进行加密处理
  4. 插入新用户

新建 /middleware/constValue.js 常量数据模块,并初始化代码如下:

// sql 语句
// 查找用户名
const selectUN = `select * from users where username = ?`;
// 插入用户
const insertUser = `insert into users set ?`;

module.exports = {
    selectUN,
    insertUser
}

修改 /routerHandler/user.js 中的代码,实现最终完整的登录逻辑,如下:

const bcrypt = require('bcryptjs');
const constValue = require('../middleware/constValue');
const signin = (req, res) => {
    const info = req.body;

    // 数据校验交给 expressJoi 中间件处理
    // if (info.username && info.password) {
        db.query(constValue.selectUN, info.username, function(err, result) {
            if (err) {
                res.cc(err);
            } else if (result.length > 0) {
                res.cc('用户名被占用,请更换其他用户名!');
            } else {
                // 调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理
                info.password = bcrypt.hashSync(info.password, 10);
                db.query(constValue.insertUser, {username: info.username, password: info.password}, function(err, result) {
                    if (err) {
                        res.cc(err);
                    } else if (result.affectedRows === 1) {
                        res.cc('注册成功!', 0);
                    } else {
                        res.cc('注册失败,请稍后重试!');
                    }
                });
            }
        });
    // } else {
    //     res.cc('用户名或密码不能为空!');
    // }
};

登录

  1. 检测表单数据是否合法
  2. 根据用户名查询用户的数据
  3. 判断用户输入的密码是否正确
  4. 生成 JWT 的 Token 字符串

在 /middleware/constValue.js 定义并导出查询用户数据的 SQL 语句以及 JWT 秘钥、加密算法、无权限表达式:

// 查找用户信息
const selectInfo = `select id, username, nickname, email, user_pic from users where id = ?`;
// JWT 秘钥
const jwtSecretKey = 'JWTSecretKeyabcABC123!@#JWTSecretKey';
// JWT 加密算法
const jwtAlgorithms = 'HS256';
// JWT 无需权限的接口 表达式
const jwtUnlessPath = /^\/api\//;

运行如下的命令,安装生成 Token 字符串的包、解析 Token 的中间件的包:

npm i jsonwebtoke express-jwt

核心注意点: 在生成 Token 字符串的时候,一定要剔除 密码 和 头像 的值 通过 ES6 的高级语法,快速剔除 密码 和 头像 的值,将用户信息对象加密成 Token 字符串:

// 通过 ES6 的高级语法,快速剔除用户敏感信息后的数据作为 token 的加密数据
const user = { ...result[0], password: '', user_pic: '' }
// token 有效期为 10 天
const token = jwt.sign(user, constValue.jwtSecretKey, { expiresIn: '10d' });

在 app.js 中注册路由之前,配置解析 Token 的中间件:

// 配置 解析 Token 的中间件
var { expressjwt } = require('express-jwt');
const constValue = require('./middleware/constValue');
app.use(expressjwt({
    secret: constValue.jwtSecretKey, 
    algorithms: [constValue.jwtAlgorithms]
}).unless({
    path: [constValue.jwtUnlessPath]
}));

在 app.js 中的 错误级别中间件里面,捕获并处理 Token 认证失败后的错误:

app.use(function(err, req, res, next) {
    // console.log(err);
    if (err instanceof joi.ValidationError) {
        // 处理数据校验错误
    } else if (err.name === 'UnauthorizedError') {
        // 处理身份认证失败的错误
        return res.cc('身份验证失败!');
    } else {
        // 处理其他未知错误
    }
    return res.cc(err);
});

修改 /routerHandler/user.js 中的代码,实现最终完整的登录逻辑,如下:

const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
const { use } = require('../router/user');
const login = (req, res) => {
    const info = req.body;
    // 查询 username 是否存在
    db.query(constValue.selectUN, info.username, function(err, result) {
        if (err) {
            res.cc(err);
        } else if (result.length === 1) {
            // 判断密码是否正确
            if (bcrypt.compareSync(info.password, result[0].password)) {
                // 剔除用户敏感信息后的数据作为 token 的加密数据
                const user = { ...result[0], password: '', user_pic: '' }
                console.log(user);
                const token = jwt.sign(user, constValue.jwtSecretKey, { expiresIn: '10d' });
                res.send({
                    status: 0,
                    token: 'Bearer ' + token,
                    message: '登录成功!'
                });
            } else {
                res.cc('密码错误!');
            }
        } else {
            res.cc('登录失败!');
        }
    });
};

个人中心

获取用户的基本信息

  1. 初始化 路由 模块
  2. 初始化 路由处理函数 模块
  3. 获取用户的基本信息

创建 /router/userinfo.js 路由模块,并初始化如下的代码结构:

const express = require('express');
const router = express.Router();
const handler = require('../routerHandler/userinfo');

// 获取用户信息
router.get('/userinfo', handler.userinfo);

module.exports = router;

在 app.js 中导入并使用个人中心的路由模块:

const userinfo = require('./router/userinfo');
app.use('/my', userinfo);

在 /middleware/constValue.js 定义并导出查询用户信息的 SQL 语句:

// 查找用户信息
const selectInfo = `select id, username, nickname, email, user_pic from users where id = ?`;

创建 /routerHandler/userinfo.js 文件,实现最终完整的逻辑,如下:

const db = require('../db/index');
const constValue = require('../middleware/constValue');

const userinfo = function(req, res) {
    console.log(req.auth);
    db.query(constValue.selectInfo, req.auth.id, function(err, result) {
        if (err) {
            res.cc(err);
        } else if (result.length === 1) {
            res.send({
                status: 0,
                data: result[0],
                message: '用户信息获取成功!'
            });
        } else {
            res.cc('用户信息获取失败!');
        }
    });
};

module.exports = {
    userinfo
};

更新用户的基本信息

  1. 定义路由和处理函数
  2. 验证表单数据
  3. 实现更新用户基本信息的功能

具体实现见代码……

重置密码

  1. 定义路由和处理函数
  2. 验证表单数据
  3. 实现重置密码的功能

在 /schema/user.js 验证规则模块中,定义 newPassword 的验证规则并使用 exports 向外共享如下的验证规则对象:

const password = joi.string().required().pattern(/^[\S]{6,16}$/);
// 1. joi.ref('oldPassword') 表示 newPassword 的值必须和 oldPassword 的值保持一致
// 2. joi.not(joi.ref('oldPwd')) 表示 newPwd 的值不能等于 oldPwd 的值
// 3. .concat() 用于合并 joi.not(joi.ref('oldPwd')) 和 password 这两条验证规则
const newPassword = joi.not(joi.ref('oldPassword')).concat(password);

exports.schema = {
    updatePassword: {
        body: {
            oldPassword: password,
            newPassword
        }
    }
}

其他具体实现见代码……

更新用户头像

  1. 定义路由和处理函数
  2. 验证表单数据
  3. 实现更新用户头像的功能

在 /schema/user.js 验证规则模块中,定义 user_pic 的验证规则并使用 exports 向外共享如下的验证规则对象:

// dataUri() 指的是如下格式的字符串数据:
// data:image/png;base64,VE9PTUFOWVNFQ1JFVFM=
const user_pic = joi.string().dataUri().required();

exports.schema = {
    updateAvatar: {
        body: {
            user_pic
        }
    }
}

在 /router/userinfo.js 模块中,导入需要的验证规则对象, 修改 更新用户头像 的路由:

const expressJoi = require('../middleware/expressJoi');
const { schema } = require('../schema/user');
// 更换头像
router.post('/updateAvatar', expressJoi(schema.updateAvatar), handler.updateAvatar);

在 /middleware/constValue.js 定义并导出 更新用户头像 的 SQL 语句:

// 修改头像
const updateAvatar = `update users set user_pic = ? where id = ?`;

处理 /routerHandler/userinfo.js 中的代码,实现最终完整的登录逻辑,如下:

// 更换头像
const updateAvatar = (req, res) => {
    console.log('------------');
    console.log(req.body);
    db.query(constValue.updateAvatar, [req.body.user_pic, req.auth.id], (err, result) => {
        if (err) {
            res.cc(err);
        } else if (result.affectedRows === 1) {
            res.cc('更换头像成功!', 0);
        } else {
            res.cc('更换头像失败!');
        }
    })
}

文章分类管理

具体实现见代码……

文章管理

新建 articles 表如下:
请添加图片描述

发布新文章

  1. 初始化路由模块
  2. 初始化路由处理函数模块
  3. 使用 multer 解析表单数据
  4. 验证表单数据
  5. 实现发布文章的功能

创建 /routerHandler/article.js 路由处理函数模块,并初始化如下的代码结构:

// 发布新文章的处理函数 
exports.addArticle = (req, res) => {
  res.send('ok')
}

创建 /router/article.js 路由模块,并初始化如下的代码结构:

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

// 导入文章的路由处理函数模块
const handler = require('../routerHandler/article');

// 发布新文章
routor.post('/addArticle', handler.addArticle);

// 向外共享路由对象 
module.exports = router

在 app.js 中导入并使用文章的路由模块:

const article = require('./router/article');
app.use('/my', article);
使用 multer 解析表单数据

注意: 使用 express.urlencoded() 中间件无法解析 multipart/form-data 格式的请求体 数据。

推荐使用 multer 来解析 multipart/form-data 格式的表单数据。运行如下的终端命令,在项目中安装 multer :

npm i multer

在 /router/article.js 模块中导入并配置 multer :

// 导入解析 formdata 格式表单数据的包 
const multer = require('multer') 
// 导入处理路径的核心模块
const path = require('path')
// 创建 multer 的实例对象,通过 dest 属性指定文件的存放路径
const upload = multer({ dest: path.join(__dirname, '../uploads') })

// 发布新文章的路由
// upload.single() 是一个局部生效的中间件,用来解析 FormData 格式的表单数据
// 将文件类型的数据,解析并挂载到 req.file 属性中
// 将文本类型的数据,解析并挂载到 req.body 属性中
routor.post('/addArticle', uploads.single('cover_img'), handler.addArticle);
实现发布文章的功能

通过 express-joi 自动验证 req.body 中的文本数据;通过 if 判断手动验证 req.file 中的 文件数据。

在 /schema/user.js 验证规则模块中,定义数据验证规则并使用 exports 向外共享如下的 验证规则对象:

const id = joi.number().integer().min(1).required();
const title = joi.string().required();
const content = joi.string().required().allow('');
const state = joi.string().valid('已发布', '草稿').required();

exports.schema = {
    addArticle: {
        body: {
            title,
            content,
            cate_id: id,
            state
        }
    }
}

在 /router/article.js 模块中,导入需要的验证规则对象,并在路由中使用。

在 /middleware/constValue.js 定义并导出 发布文章 的 SQL 语句:

// 插入文章
const insertArticle = `insert into articles set ?`;

修改 /routerHandler/article.js 中的代码,实现最终完整的登录逻辑,如下:

const addArticle = (req, res) => {
    console.log(req.body);
    console.log(req.file);
    if (req.file && req.file.fieldname === 'cover_img') {
        const article = {
            ...req.body,
            // 文章封面在服务器端的存放路径
            cover_img: `/uploads/${req.file.filename}`,
            pub_date: new Date(),
            author_id: req.auth.id
        };
        db.query(constValue.insertArticle, article, (err, result) => {
            if (err) {
                res.cc(err);
            } else if (result.affectedRows === 1) {
                res.cc('文章添加成功!', 0);
            } else {
                res.cc('文章添加失败!');
            }
        });
    } else {
        res.cc('cover_img 是必选参数!');
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

时序预测 | Python实现LSTM电力需求预测

时序预测 | Python实现LSTM电力需求预测 目录 时序预测 | Python实现LSTM电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行业预测进行比较。使用…

uniGUI之上传文件UniFileUploadButton

TUniFileUploadButton主要属性: Filter: 文件类型过滤,有图片image/* audio/* video/*三种过滤 MaxAllowedSize: 设置文件最大上传尺寸; Message:标题以及消息文本,可翻译成中文 TUniFileUploadButton控件 支持多…

redis:四、双写一致性的原理和解决方案(延时双删、分布式锁、异步通知MQ/canal)、面试回答模板

双写一致性 场景导入 如果现在有个数据要更新,是先删除缓存,还是先操作数据库呢?当多个线程同时进行访问数据的操作,又是什么情况呢? 以先删除缓存,再操作数据库为例 多个线程运行的正常的流程应该如下…

Android动画(二)——补间动画

目录 介绍 Xml文件定义View动画 补充 alpha_animation.xml(透明度) rotate_animation.xml(旋转) scale_animation.xml(伸缩) translate_animation.xml (平移) group_animation.…

〖大前端 - 基础入门三大核心之JS篇(55)〗- 内置对象

说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司…

结构体基础全家桶(2)结构体指针

目录 指向结构体类型数据的指针: 指向结构体变量的指针: 创建: 应用: 注意事项: 指向结构体数组的指针 创建: 应用: 注意: 用结构体变量和指向结构体的指针做函数的参数 …

【Linux】文件系统、文件系统结构、虚拟文件系统

一、文件系统概述 1. 什么是文件系统?2. 文件系统(文件管理系统的方法)的种类有哪些?3. 什么是分区?4. 什么是文件系统目录结构?5. 什么虚拟文件系统Virtual File System ?6. 虚拟文件系统有什…

selenium 与 chromedriver安装

本文章向大家介绍selenium 安装与 chromedriver安装,主要包括selenium 安装与 chromedriver安装使用实例、应用技巧、基本知识点总结和需要注意事项供大家参考。 一、安装selenium 1、Selenium简介 Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开…

IDEA配置一个新项目

git clone xxxxx 下载项目主分支 git checkout xxx 切换到需要开发的分支上 配置maven仓库 在File下的Settings中设置maven仓库 配置maven仓库的文件夹 配置好maven后,项目中会出现一个红色的pom.xml文件,右击文件,点击…,pom…

RabbitMq基本使用

目录 SpringAMQP1.准备Demo工程2.快速入门1.1.消息发送1.2.消息接收1.3.测试 3.WorkQueues模型3.1.消息发送3.2.消息接收3.3.测试3.4.能者多劳3.5.总结 SpringAMQP 将来我们开发业务功能的时候,肯定不会在控制台收发消息,而是应该基于编程的方式。由于R…

ArrayList与LinkLIst

ArrayList 在Java中,ArrayList是java.util包中的一个类,它实现了List接口,是一个动态数组,可以根据需要自动增长或缩小。下面是ArrayList的一些基本特性以及其底层原理的简要讲解: ArrayList基本特性: 动…

科大讯飞(深圳)测开面试真题

一面(测试组长面) 1、上家公司项目以及团队的规模是怎么样的? 2、你负责的项目整体的流程是怎么样的? 3、自动化实施过程中,是如何和业务测试进行沟通的? 4、在上家公司你已经是专职做自动化了&#xf…

会声会影怎么使用? 会声会影2024快速掌握入门技巧

一听说视频剪辑我们就不由得联想到电影、电视等一些高端的视频剪辑技术,大家都觉得视频剪辑是一个非常复杂而且需要很昂贵的设备才可以完成的技术活,这对很多“门外汉”来说都可望而不可及。实际上,使用会声会影剪辑视频不仅是很多人都可以操…

《面向机器学习的数据标注规程》摘录

说明:本文使用的标准是2019年的团体标准,最新的国家标准已在2023年发布。 3 术语和定义 3.2 标签 label 标识数据的特征、类别和属性等。 3.4 数据标注员 data labeler 对待标注数据进行整理、纠错、标记和批注等操作的工作人员。 【批注】按照定义…

【话题】低代码123

目录 一、什么是低代码 二、低代码的优缺点 三、你认为低代码会替代传统编程吗? 四、有哪些低代码工具和框架 4.1 国外的平台 4.2 国内的平台 五、未来的软件研发 低代码,听着就过瘾的一个词。而且不是无代码,这说明,低代码…

GoogLeNet(pytorch)

亮点与创新: 1. 引入Inception基础结构 2. 引入PW维度变换卷积,启迪后续参数量的优化 3. 丢弃全连接层,使用平均池化层(大大减少模型参数) 4. 添加两个辅助分类器帮助训练(避免梯度消失,用于…

方舟无限ARX-5臂的奇异验证

事情起因是,某技术人员号称这款机械臂无奇异点,博主当场一个【黑人问号脸】。 既然是串联臂,大概很难做到无奇异点~ 为了反驳,博主建模简单分析了下,偏置参数随便写了个,具体验证程序见文末。 clear,clc,…

共同编辑文档功能实现(websocket)

目录 前言 websocket封装 wangeditor下载 共同编辑文档代码实现 HTML样式部分 JS部分 css部分 前言 功能:实现文档共同编辑功能,可以实时接收到其他人的信息 思路:先调用接口获取相应的数据进行渲染,然后通过webSocket建…

C#基础知识 - 基本语法篇

C#基础知识-基本语法篇 第2节 C#基本语法2.1 C#程序结构2.2 C# 结构解析2.3 命名空间及标识符、关键字2.3.1 别名的使用2.3.2 标识符2.3.3 C#关键字 更多C#基础知识详解请查看:C#基础知识 - 从入门到放弃 第2节 C#基本语法 2.1 C#程序结构 “Hello, World”程序历…

棋牌的电脑计时计费管理系统教程,棋牌灯控管理软件操作教程

一、前言 有的棋牌室在计时的时候,需要使用灯控管理,在开始计时的时候打开灯,在结账后关闭灯,也有的不需要用灯控,只用来计时。 下面以 佳易王棋牌计时计费管理系统软件为例说明: 软件试用版下载或技术支…
最新文章