IndexedDB 的官网

https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API

这个大概是官网吧,原始是英文的,现在陆续是出中文版。有空的话还是多看看官网。

简介

IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 可以使用索引实现对数据的高性能搜索。

简单的说就是 —— 能装!indexedDB 是前端的一种“事务型对象数据库”,可以存放很多很多的对象(当然也可以是其他类型),功能很强大,可以用作数据(对象)在前端的缓存容器。或者其他用途。

使用也是很简单的,网上可以找到很多教程,官网也推荐了几个封装类库。

只是我比较懒,得看别人的类库(好吧,我看不懂),而是想按照自己的想法封装一个自己用着习惯的类库。

最近在使用 Vue3,所以想针对 Vue3 做一套 indexedDB 的类库,实现客户端缓存数据的功能。

其实这里介绍的应该算是第二版了,第一版在项目里面试用一段时间后,发现了几个问题,所以想在新版本里面一起解决。

indexedDB 的操作思路

一开始看 indexedDB 的时候各种懵逼,只会看大神写的文章,然后照猫画虎,先不管原理,把代码copy过来能运行起来,能读写数据即可。

现在用了一段时间,有了一点理解,整理如下:

  • 获取 indexedDB 的对象
  • open (打开/建立)数据库。
  • 如果没有数据库,或者版本升级:
    • 调用 onupgradeneeded(建立/修改数据库),然后调用 onsuccess。
  • 如果已有数据库,且版本不变,那么直接调用 onsuccess。
  • 在 onsuccess 里得到连接对象后:
    • 开启事务。

      • 得到对象仓库。

        • 执行各种操作:添加、修改、删除、获取等。
        • 用索引和游标实现查询。
      • 得到结果

思路明确之后,我们就好封装了。

做一个 help,封装初始化的代码

前端数据库和后端数据库对比一下,就会发现一个很明显的区别,后端数据库是先配置好数据库,建立需要的表,然后添加初始数据,最后才开始运行项目。

在项目里面不用考虑数据库是否已经建立好了,直接用就行。

但是前端数据库就不行了,必须先考虑数据库有没有建立好,初始数据有没有添加进去,然后才可以开始常规的操作。

所以第一步就是要封装一下初始化数据库的部分。

我们先建立一个 help.js 文件,在里面写一个 ES6 的 class。


/**
* indexedDB 的 help,基础功能的封装
* * 打开数据库,建立对象仓库,获取连接对象,实现增删改查
* * info 的结构:
* * * dbFlag: '' // 数据库标识,区别不同的数据库
* * * dbConfig: { // 连接数据库
* * * * dbName: '数据库名称',
* * * * ver: '数据库版本',
* * * },
* * * stores: {
* * * * storeName: { // 对象仓库名称
* * * * * id: 'id', // 主键名称
* * * * * index: { // 可以不设置索引
* * * * * * name: ture, // key:索引名称;value:是否可以重复
* * * * * }
* * * * }
* * * },
* * * init: (help) => {} // 完全准备好之后的回调函数
*/
export default class IndexedDBHelp {
constructor (info) {
this.myIndexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB
if (!this.myIndexedDB) {
console.log('您的浏览器不支持IndexedDB')
}
// 数据库名称和版本号
this._info = {
dbName: info.dbConfig.dbName,
ver: info.dbConfig.ver
}
// 记录连接数据库的对象, IDBDatabase 类型,因为open是异步操作,所以不能立即获得。
this._db = null // 记录仓库状态。new:新库或者版本升级后;old:有对象仓库了。
this._storeState = 'pending' /**
* 注册回调事件。
* * 如果组件读写 indexedDB 的时还没有准备好的话,
* * 可以来注册一个事件,等准备好了之后回调。
*/
this._regCallback = [] // 打开数据库,异步操作,大概需要几毫秒的时间。
this.dbRequest = this.myIndexedDB.open(this._info.dbName, this._info.ver) // 第一次,或者版本升级时执行,根据配置信息建立表
this.dbRequest.onupgradeneeded = (event) => {
this._storeState = 'new'
const db = event.target.result
// console.log('【2】新建或者升级数据库 onupgradeneeded --- ', db) for (const key in info.stores) {
const store = info.stores[key]
if (db.objectStoreNames.contains(key)) {
// 已经有仓库,验证一下是否需要删除原来的仓库
if (store.isClear) {
// 删除原对象仓库,没有保存数据
db.deleteObjectStore(key)
// 建立新对象仓库
const objectStore = db.createObjectStore(key, { keyPath: store.id })
// 建立索引
for (const key2 in store.index) {
const unique = store.index[key2]
objectStore.createIndex(key2, key2, { unique: unique })
}
}
} else {
// 没有对象仓库,建立
const objectStore = db.createObjectStore(key, { keyPath: store.id }) /* 自动创建主键 autoIncrement: true */
// 建立索引
for (const key2 in store.index) {
const unique = store.index[key2]
objectStore.createIndex(key2, key2, { unique: unique })
}
}
}
} // 数据库打开成功,记录连接对象
this.dbRequest.onsuccess = (event) => {
this._db = event.target.result // dbRequest.result
// console.log('【1】成功打开数据库 onsuccess --- ', this._db)
// 修改状态
if (this._storeState === 'pending') {
this._storeState = 'old'
}
// 调用初始化的回调
if (typeof info.init === 'function') {
info.init(this)
}
// 调用组件注册的回调
this._regCallback.forEach(fn => {
if (typeof fn === 'function') {
fn()
}
})
} // 处理出错信息
this.dbRequest.onerror = (event) => {
// 出错
console.log('打开数据库出错:', event.target.error)
}
}
// 挂载其他操作,后面介绍。。。
}

这里要做几个主要的事情:

  • 判断浏览器是否支持 indexedDB
  • 打开数据库
  • 设置对象仓库
  • 保存连接对象,备用

另外使用 jsDoc 进行参数说明,有的时候是可以出现提示,就算不出提示,也是可以有说明的作用,避免过几天自己都想不起来怎么用参数了。

挂载事务

拿到数据库的连接对象之后,我们可以(必须)开启一个事务,然后才能执行其他操作。

所以我们需要先把事务封装一下,那么为啥要单独封装事务呢?

因为这样可以实现打开一个事务,然后传递事务实例,从而实现连续操作的目的,虽然这种的情况不是太多,但是感觉还是应该支持一下这种功能。

begin-tran.js

/**
* 开启一个读写的事务
* @param {*} help indexedDB 的 help
* @param {Array} storeName 字符串的数组,对象仓库的名称
* @param {string} type readwrite:读写事务;readonly:只读事务;versionchange:允许执行任何操作,包括删除和创建对象存储和索引。
* @returns 读写事务
*/
const beginTran = (help, storeName, type = 'readwrite') => {
return new Promise((resolve, reject) => {
const _tran = () => {
const tranRequest = help._db.transaction(storeName, type)
tranRequest.onerror = (event) => {
console.log(type + ' 事务出错:', event.target.error)
reject(`${type} 事务出错:${event.target.error}`) }
resolve(tranRequest)
tranRequest.oncomplete = (event) => {
// console.log('beginReadonly 事务完毕:', window.performance.now())
}
} if (help._db) {
_tran() // 执行事务
} else {
// 注册一个回调事件
help._regCallback.push(() => _tran())
}
})
}
export default beginTran
  • 支持多个对象仓库

    storeName 是字符串数组,所以可以针对多个对象仓库同时开启事务,然后通过 tranRequest.objectStore(storeName) 来获取具体的对象仓库。

  • 挂载到 help

    因为是写在单独的js文件里面,所以还需要在 help 里面引入这个js文件,然后挂到 help 上面,以便实现 help.xxx 的调用形式,这样拿到help即可,不用 import

    各种函数了。

import _beginTran from './begin-tran.js' // 事务
... help 的其他代码
// 读写的事务
beginWrite (storeName) {
return _beginTran(this, storeName, 'readwrite')
} // 只读的事务
beginReadonly (storeName) {
return _beginTran(this, storeName, 'readonly')
}

是不是有一种“循环调用”的感觉?js 就是可以这么放飞自我。然后需要我们写代码的时候就要万分小心,因为不小心的话很容易写出来死循环。

挂载增删改查

事务准备好了,我们就可以进行下一步操作。

先设计一个添加对象的 js文件:

addModel.js

import _vueToObject from './_toObject.js'

/**
* 添加对象
* @param { IndexedDBHelp } help 访问数据库的实例
* @param { string } storeName 仓库名称(表名)
* @param { Object } model 对象
* @param { IDBTransaction } tranRequest 如果使用事务的话,需要传递开启事务时创建的连接对象
* @returns 新对象的ID
*/
export default function addModel (help, storeName, model, tranRequest = null) {
// 取对象的原型,便于保存 reactive
const _model = _vueToObject(model)
// 定义一个 Promise 的实例
return new Promise((resolve, reject) => {
// 定义个函数,便于调用
const _add = (__tran) => {
__tran
.objectStore(storeName) // 获取store
.add(_model) // 添加对象
.onsuccess = (event) => { // 成功后的回调
resolve(event.target.result) // 返回对象的ID
}
}
if (tranRequest === null) {
help.beginWrite([storeName]).then((tran) => {
// 自己开一个事务
_add(tran)
})
} else {
// 使用传递过来的事务
_add(tranRequest)
}
})
}

首先使用 Promise 封装默认的回调模式,然后可以传递进来一个事务进来,这样可以实现打开事务连续添加的功能。

如果不传递事务的话,内部会自己开启一个事务,这样添加单个对象的时候也会很方便。

然后在 help 里面引入这个 js文件,再设置一个函数:

import _addModel from './model-add.js' // 添加一个对象

  /**
* 添加一个对象
* @param {string} storeName 仓库名称
* @param {object} model 要添加的对象
* @param {*} tranRequest 事务,可以为null
* @returns
*/
addModel (storeName, model, tranRequest = null) {
return _addModel(this, storeName, model, tranRequest = null)
}

这样就可以挂载上来了。把代码分在多个 js文件里面,便于维护和扩展。

修改、删除和获取的代码也类似,就不一一列举了。

使用方式

看了上面的代码可能会感觉很晕,这么复杂?不是说很简单吗?

对呀,把复杂封装进去了,剩下的就是简单的调用了。那么如何使用呢?

准备创建数据库的信息

我们先定义一个对象,存放需要的各种信息

const dbInfo = {
dbFlag: 'project-meta-db', // 数据库标识,区分不同的数据库。如果项目里只有一个,那么不需要加这个标识
dbConfig: {
dbName: 'nf-project-meta', // 数据库名称
ver: 2
},
stores: { // 数据库里的表(对象仓库)
moduleMeta: { // 模块的meta {按钮,列表,分页,查询,表单若干}
id: 'moduleId',
index: {},
isClear: false
},
menuMeta: { // 菜单用的meta
id: 'id',
index: {},
isClear: false
},
serviceMeta: { // 后端API的meta,在线演示用。
id: 'moduleId',
index: {},
isClear: false
}
},
init: (help) => {
// 数据库建立好了
console.log('inti事件触发:indexedDB 建立完成 ---- help:', help)
}
}
  • dbFlag

    一个项目里面可能需要同时使用多个 indexedDB 的数据库,那么就需要一个标识区分一下,dbFlag 就是区分标识。

  • stores

    对象仓库的说明,在 onupgradeneeded 事件里面依据这个信息创建对象仓库。

  • init

    indexedDB 都准备好之后的回调函数。

直接使用

import IndexedDB from '../../../packages/nf-ws-indexeddb/help.js'

// 建立实例
const help = new IndexedDB(dbInfo) // 添加对象的测试
const add = () => {
// 定义一个对象
const model = {
id: new Date().valueOf(),
name: 'test'
}
// 添加
help.addModel('menuMeta', model).then((res) => {
console.log('添加成功!', res) // 返回对象ID
})
}
  • 定义一个数据库描述信息
  • 生成 help 的实例
  • 使用 help.addModel 添加对象

做个“外壳”套个娃

检查一下代码,发现有几个小问题:

  • 每次使用都需要实例化一个help吗?是不是有点浪费?
  • 对象仓库名还需要写字符串,万一写错了怎么办?
  • help.xxxModel(xxx,xxx,xxx) 是不是有点麻烦?

所以我们需要在套一个外壳,让使用更方便。


import IndexedDB from './help.js' /**
* 把 indexedDB 的help 做成插件的形式
*/
export default {
_indexedDBFlag: Symbol('nf-indexedDB-help'),
_help: {}, // 访问数据库的实例
_store: {}, // 存放对象,实现 foo.addModel(obj)的功能 createHelp (info) {
let indexedDBFlag = this._indexedDBFlag
if (typeof info.dbFlag === 'string') {
indexedDBFlag = Symbol.for(info.dbFlag)
} else if (typeof info.dbFlag === 'symbol') {
indexedDBFlag = info.dbFlag
}
// 连接数据库,获得实例。
const help = new IndexedDB(info)
// 存入静态对象,以便于支持保存多个不同的实例。
this._help[indexedDBFlag] = help // help
this._store[indexedDBFlag] = {} // 仓库变对象 // 把仓库变成对象的形式,避免写字符串的仓库名称
for (const key in info.stores) {
this._store[indexedDBFlag][key] = {
put: (obj) => {
let _id = obj
if (typeof obj === 'object') {
_id = obj[info.stores[key].id]
}
return help.updateModel(key, obj, _id)
},
del: (obj) => {
let _id = obj
if (typeof obj === 'object') {
_id = obj[info.stores[key].id]
}
return help.deleteModel(key, _id)
},
add: (obj) => help.addModel(key, obj),
get: (id = null) => help.getModel(key, id)
}
}
}, // 获取静态对象里的数据库实例
useDBHelp (_dbFlag) {
let flag = this._indexedDBFlag
if (typeof _dbFlag === 'string') {
flag = Symbol.for(_dbFlag)
} else if (typeof _dbFlag === 'symbol') {
flag = _dbFlag
}
return this._help[flag]
},
useStore (_dbFlag) {
let flag = this._indexedDBFlag
if (typeof _dbFlag === 'string') {
flag = Symbol.for(_dbFlag)
} else if (typeof _dbFlag === 'symbol') {
flag = _dbFlag
}
return this._store[flag]
}
}

首先,这是一个静态对象,可以存放 help 的实例,可以实现全局访问的效果。

以前是 使用 provide / inject 保存的,但是发现有点不太方便,也不是十分必要,所以改成了静态对象的方式。

然后根据建表的信息,创建仓库的对象,把字符串的仓库名称变成对象的形式,这样就方便多了。

为啥是 “useDBHelp”呢,因为要和 webSQL的 help 加以区分。

使用的时候就变成了这样:


// 把仓库当做“对象”
const { menuMeta } = dbInstall.useStore(dbInfo.dbFlag) // 添加对象
const add = () => {
const t1 = window.performance.now()
console.log('\n -- 准备添加对象 --:', t1)
const model = {
id: new Date().valueOf(),
name: 'test-。'
}
menuMeta.add(model).then((res) => {
const t2 = window.performance.now()
console.log('添加成功!', res, '用时:', t2 - t1, '\n')
})
}

这样的话,就方便多了。对象仓库名.xxx(oo) 就可以,代码简洁了很多。

进一步套娃

上面是把对象仓库看做了“对象”,然后实现增删改查,那么能不能让object 本身实现增删改查呢?

既然封装到这一步了,我们可以再前进一下,使用 js的原型 实现 object 的增删改查。

// 给 model 加上增删改查的函数
for (const key in info.stores) {
this._store[indexedDBFlag][key] = {
createModel: (model) => {
function MyModel (_model) {
for (const key in _model) {
this[key] = _model[key]
}
}
MyModel.prototype.add = function (tran = null) {
return help.addModel(key, this, tran)
}
MyModel.prototype.save = function (tran = null) {
const _id = this[info.stores[key].id]
return help.setModel(key, this, _id, tran)
}
MyModel.prototype.load = function (tran = null) {
return new Promise((resolve, reject) => {
// 套个娃
const _id = this[info.stores[key].id]
help.getModel(key, _id, tran).then((res) => {
Object.assign(this, res)
resolve(res)
})
})
}
MyModel.prototype.del = function (tran = null) {
const _id = this[info.stores[key].id]
return help.delModel(key, _id, tran)
}
const re = new MyModel(model)
return reactive(re)
}
}
}

首先给对象仓库加一个 “createModel”函数,用于把 object 和对象仓库挂钩,然后用原型挂上增删改查的函数,最后 new 一个实例返回。

使用方式:


// 对象仓库,创建一个实例,reactive 形式
const testModel = menuMeta.createModel({
id: 12345,
name: '对象自己save'
}) // 对象直接保存
const mSave = () => {
testModel.name = '对象自己save' + window.performance.now()
testModel.save().then((res) => {
// 保存完成
})
}

因为加上了 reactive,所以可以自带响应性。

这样是不是很像“充血实体类”了?

id 值建议不要修改,虽然可以改,但是总感觉改了的话比较别扭。

统一“出口”

虽然用 help 带上了几个常规操作,但是出口还是不够统一,像 Vue 那样,就一个出口是不是很方便呢?所以我们也要统一一下:

storage.js

// 引入各种函数,便于做成npm包
// indexedDB 部分
import dbHelp from './nf-ws-indexeddb/help.js'
import dbInstall from './nf-ws-indexeddb/install.js' // indexedDB 部分
const dbCreateHelp = (info) => dbInstall.createHelp(info)
const useDBHelp = (_dbFlag) => dbInstall.useDBHelp(_dbFlag)
const useStores = (_dbFlag) => dbInstall.useStores(_dbFlag) export {
// indexedDB 部分
dbHelp, // indexedDB 的 help
dbCreateHelp, // 创建 help 实例,初始化设置
useDBHelp, // 组件里获取 help 的实例
useStores // 组件里获取对象仓库,方便实现增删改查
}

这样也便于我们打包发布到npm。

在 vue 里面使用

基本工作都作好了,就剩最后一个问题了,在 Vue3 里面如何使用呢?

我们可以仿造一下 vuex 的使用方式,先建立一个 js文件,实现统一设置。

store-project/db.js

// 引入 indexedDB 的 help
import { dbCreateHelp } from '../../packages/storage.js' // 引入数据库数据
const db = {
dbName: 'nf-project-meta',
ver: 5
} /**
* 设置
*/
export default function setup (callback) {
const install = dbCreateHelp({
// dbFlag: 'project-meta-db',
dbConfig: db,
stores: { // 数据库里的表
moduleMeta: { // 模块的meta {按钮,列表,分页,查询,表单若干}
id: 'moduleId',
index: {},
isClear: false
},
menuMeta: { // 菜单用的meta
id: 'id',
index: {},
isClear: false
},
serviceMeta: { // 后端API的meta,在线演示用。
id: 'moduleId',
index: {},
isClear: false
},
testIndex: { // 测试索引和查询。
id: 'moduleId',
index: {
kind: false,
type: false
},
isClear: false
}
},
// 加入初始数据
init (help) {
if (typeof callback === 'function') {
callback(help)
}
}
})
return install
}

然后在 main.js 里面调用,因为这是最早执行代码的地方,可以第一时间建立数据库。


// 引入 indexedDB 的help
import dbHelp from './store-project/db.js' dbHelp((help) => {
// indexedDB 准备好了
console.log('main里面获取 indexedDB 的help', help)
})

同时可以把 help 的实例存入静态对象里面。

其实一开始是使用 provide 注入的,但是发现不是太适合,因为在main.js这个层级里面无法使用inject读取出来,这样的话,和状态等的操作就不太方便。

所以干脆放在静态对象里面好了,任何地方都可以访问到。

并不需要使用 use 挂载到 App 上面。

索引和查询

由于篇幅有限,这里就先不介绍了,如果大家感兴趣的话,可以在写一篇补充一下。

源码

封装前端存储

https://gitee.com/naturefw/nf-web-storage

在线演示

https://naturefw.gitee.io/vite2-vue3-demo

安装方式

yarn add nf-web-storage

vue3 专用 indexedDB 封装库,基于Promise告别回调地狱的更多相关文章

  1. 基于PROMISE解决回调地狱问题

    回调地狱问题: 在使用JavaScript时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱.比如说你要把一个函数 A 作为回调函数, ...

  2. 深入了解Promise对象,写出优雅的回调代码,告别回调地狱

    深入浅出了解Promise 引言 正文 一.Promise简介 二.Promise的三种状态 三.函数then( ) 四.函数catch( ) 五.函数finally( ) 六.函数all( ) 七. ...

  3. promise处理回调地狱

    promise 异步调用 异步结果分析 定时任务 ajax 自定义事件函数 多次异步调用依赖分析(promise 应用场景) 多次异步调用结果顺序不确定问题 $.ajax({ url: 'http:l ...

  4. async + promise 解决回调地狱

    // 解决异步回调地狱的方案: async + promise async function writeFile() {   // 打开文件   const fd = await new Promis ...

  5. Promise解决回调地狱(多层调用问题)

    Promise # Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息:从本意上讲,它是承诺,承诺它过一段时间会给你一个结果.promise有三 ...

  6. Promise解决回调地狱

    Promise是JavaScript异步操作解决方案.介绍Promise之前,先对异步操作做一个详细介绍. JavaScript的异步执行 概述 Javascript语言的执行环境是”单线程”(sin ...

  7. JS之用ES6 Promise解决回调地狱(这里以小程序为例)

    首先 写一个请求的方法,如: /** * 银行窗口 * 你需要给我提供相关的相关参数我帮你提交到服务器上 * 我会给你一个等待区的编号给你 你去等待区等待,我处理完成会去等待区通知你 * @param ...

  8. 前端项目中常用es6知识总结 -- Promise逃脱回调地狱

    项目开发中一些常用的es6知识,主要是为以后分享小程序开发.node+koa项目开发以及vueSSR(vue服务端渲染)做个前置铺垫. 项目开发常用es6介绍 1.块级作用域 let const 2. ...

  9. es6 promise 结束回调地狱

    promise的三种状态: pending---进行中 fulfiled---执行成功 rejected---执行失败 var promise = new Promise(function(resol ...

  10. ES6(promise)_解决回调地狱初体验

    一.前言 通过这个例子对promise解决回调地狱问题有一个初步理解. 二.主要内容 1.回调地狱:如下图所示,一个回调函数里面嵌套一个回调函数,这样的代码可读性较低也比较恶心 2.下面用一个简单的例 ...

随机推荐

  1. MQTT实现长连接,IM最简单实例

    1,引入MqttSDK. 2, 头文件 #import "MQTTSession.h" //定义主题#define kTopic @"lichanghong"/ ...

  2. 【HTML】Intermediate2:Text: AbbreviationsQuotations Code

    1.</abbr> attribute:title 2.Quotations blockquote :standalone often multi-line quotations-cite ...

  3. VS2010升级VS2012必备(MVC4 WebPage2.0 Razor2.0资料汇集)

    刚把项目升级到2012,发现发生了很多变化,以下是最近看过的网站和资料汇集,供需要者参考. 本文在最近一个月可能会不断更新. Razor2.0 新特性介绍: 介绍1:http://vibrantcod ...

  4. 【设计模式 - 1】之工厂模式(Factory)

    1      模式简介 1.1    工厂模式作用 工厂模式解决的是"使用new关键字获取对象造成松耦合"的问题. 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏 ...

  5. C++ 头文件系列(sstream)

    1. 简介 这个头文件主要定义了基于字符串类(string类)的流的4个模版: basic_stringbuf basic_istringstream basic_ostringstream basi ...

  6. iOS项目——项目开发环境搭建

    在开发项目之前,我们需要做一些准备工作,了解iOS扩展--Objective-C开发编程规范是进行开发的必备基础,学习iOS学习--Xcode9上传项目到GitHub是我们进行版本控制和代码管理的选择 ...

  7. 陪你解读Spring Batch(二)带你入手Spring Batch

    前言 说得多不如show code.上一章简单介绍了一下Spring Batch.本章将从头到尾搭建一套基于Spring Batch(2.1.9).Spring(3.0.5).mybatis(3.4. ...

  8. LUA Metatables

    __index:当我们访问一个表中的元素不存在时,则会触发去寻找__index元方法,如果不存在,则返回nil,如果存在,则返回结果. 博主注:__index有点像异常处理的意思 __newindex ...

  9. Hibernate Validator参数校验

    日常开发中经常会遇到需要参数校验的情况,比如某个字段不能为空.长度不能超过5等都属于参数校验的范围.对于简单的参数校验通过写几个if-else判断语句就搞定,但是对于复杂的多个参数校验的情况,就不是那 ...

  10. Msfvenom学习总结-MSF反弹webshell

    1.    –p (- -payload-options) 添加载荷payload. 载荷这个东西比较多,这个软件就是根据对应的载荷payload生成对应平台下的后门,所以只有选对payload,再填 ...