Node.js笔记06--路由中间件应用及cookie

1 路由中间件


将服务器中的四个路由拆分出去,UI路由和业务逻辑路由
router–>UIRouter.js

/*
* 专门用于管理展示界面的UI路由
* */

//引入Router构造函数
const {Router} = require('express')
//创建一个Router实例(路由器就是一个小型的app)
let router = new Router()
//引入path模块----Node中内置的一个专门用于解决路径问题的库
let {resolve} = require('path')


//用于展示登录界面的路由,无其他任何逻辑 ----- UI路由
router.get('/login',(req,res)=>{
  let url = resolve(__dirname,'../public/login.html')
  res.sendFile(url)
})

//用于展示注册界面的路由,无其他任何逻辑 ----- UI路由
router.get('/register',(req,res)=>{
  let url = resolve(__dirname,'../public/register.html')
  res.sendFile(url)
})

module.exports = function () {
  return router
}

router–>loginRegisterRouter.js

/*
* 专门用于管理登录、注册的业务路由
* */

//引入Router构造函数
const {Router} = require('express')
//创建一个Router实例(路由器就是一个小型的app)
let router = new Router()
//引入模型对象
let usersModel = require('../model/usersModel')

//用于处理用户的注册请求,有很多业务逻辑(校验数据的有效性等) -------- 业务路由
router.post('/register',(req,res)=>{
  /*
   { email: 'kobe@qq.com',
     nick_name: 'kobe',
     password: '123',
     re_password: '123' }
 */
  //1.获取用户的输入
  const {email,nick_name,password,re_password} = req.body
  /*
  * 2.校验数据的合法性:(一般是前台和后台同时验证。)
  *     2.1:校验成功
  *           -去数据库中查找该邮箱是否注册过
  *               2.1.1:注册过:提示用户邮箱已被占用。
  *               2.1.2:未注册:写入数据库
  *     2.2:校验失败
  *           -提示用户具体哪里输入的不正确
  * */

  //校验邮件的正则表达式
  const emailReg = /^[a-zA-Z0-9_]{4,20}@[a-zA-Z0-9]{2,10}\.com$/
  //校验昵称的正则表达式
  const nickNameReg = /[\u4e00-\u9fa5]/gm
  //校验密码的正则表达式
  const passwordReg = /^[a-zA-Z0-9_@#.+&]{6,20}$/

  //使用正则去校验
  if(!emailReg.test(email)){
    res.send('邮箱格式不合法,用户名必须4-20位,主机名必须2-10位')
  }else if(!nickNameReg.test(nick_name)){
    res.send('昵称格式不合法,必须为中文')
  }else if(!passwordReg.test(password)){
    res.send('密码格式不合法,必须6-20')
  }else if( password !== re_password){
    res.send('两次输入密码不一致')
  }else{
    //去数据库中查询该邮箱是否注册过
    usersModel.findOne({email},function (err,data) {
      if(data){
        //如果注册过
        //引入计数模块--当达到一个敏感的阈值,触发安全性机制。
        console.log(`邮箱为${email}的用户注册失败,因为邮箱重复`)
        res.send('该邮箱已被注册,请更换邮箱')
      }else{
        //如果没有注册过
        usersModel.create({email,nick_name,password},function (err) {
          if(!err){
            //如果写入成功了
            console.log(`邮箱为${email}的用户注册成功`)
            res.send('注册成功了')
          }else{
            //如果写入失败了
            //引入报警模块,当达到敏感阈值,触发报警。
            console.log(err)
            res.send('您当前的网络状态不稳定,稍后重试')
          }
        })
      }
    })
  }
})

//用于处理用户的登录请求,有很多业务逻辑(校验数据的有效性等) -------- 业务路由
router.post('/login',(req,res)=>{
  //1.获取输入
  const {email,password} = req.body
  //2.准备正则
  const emailReg = /^[a-zA-Z0-9_]{4,20}@[a-zA-Z0-9]{2,10}\.com$/
  const passwordReg = /^[a-zA-Z0-9_@#.+&]{6,20}$/
  if(!emailReg.test(email)){
    res.send('邮箱格式不合法,用户名必须4-20位,主机名必须2-10位')
  }else if(!passwordReg.test(password)){
    res.send('密码格式不合法,必须6-20')
  }else{
    //3.去数据库中查找:
    usersModel.findOne({email,password},(err,data)=>{
      if(err){
        //引入报警模块,当达到敏感阈值,触发报警。
        console.log(err)
        res.send('网络不稳定,稍后重试')
        return
      }
      if(data){
        res.redirect('https://wwww.baidu.com')
        return
      }
      res.send('用户名或密码输入错误!')
    })
  }
})

module.exports = function () {
  return router
}

server.js

//引入express
const express = require('express')
//创建app应用对象
const app = express()
//禁止服务器返回X-Powered-By,为了安全
app.disable('x-powered-by')
//使用内置中间件暴露静态资源,不访问路由直接写文件名+后缀也能看页面
app.use(express.static(__dirname+'/public'))
//引入db模块,用于连接数据库
const db = require('./db/db')
//使用内置中间件用于解析post请求的urlencoded参数
app.use(express.urlencoded({extended:true}))
//引入UI路由器
const UIRouter = require('./router/UIRouter')
//引入登录注册路由器
const loginRegisterRouter = require('./router/loginRegisterRouter')
//逻辑:如果数据库连接成功,随后立即启动服务器,在整个过程中,无论多少次请求,数据库只连接一次。
db(()=>{

  //使用UIRouter
  app.use(UIRouter())
  //使用loginRegisterRouter
  app.use(loginRegisterRouter())

  //绑定端口监听
  app.listen(3000,(err)=>{
    if(!err) console.log('服务器启动成功!')
    else console.log(err)
  })
},(err)=>{
  console.log('数据库连接失败',err)
})

2 ejs


让login和register页面的数据动起来,新建文件夹view,用来存放.ejs模板文件,使得前后端有数据交互,再用户post之后,在对应的位置指出所有错误,登陆失败则重新渲染登陆页面,一次性指出所有错误,注册同理

ejs语法:
            1.< % % >   里面能写任意js代码,但是不会向浏览器输出任何东西。
            2.< %- % >  能够将后端传递过来对象指定key所对应value渲染的页面
            3.< %= % >  能够将后端传递过来对象指定key所对应value渲染的页面
注意:需要下载ejs----yarn add ejs

示例

const express = require('express')
const app = express()
//让你的服务器知道你在用哪一个模板引擎-----配置模板引擎
app.set('view engine','ejs')
//让你的服务器知道你的模板在哪个目录下,配置模板目录
app.set('views','./haha')

//如果在express中基于Node搭建的服务器,使用ejs无需引入。
app.get('/show',function (request,response) {
  let personArr = [
    {name:'peiqi',age:4},
    {name:'suxi',age:5},
    {name:'peideluo',age:6}
  ]
  response.render('person',{persons:personArr,a:1})
})

app.listen(3000,function (err) {
  if (!err) console.log('服务器启动成功了')
  else console.log(err)
})

对应的.ejs文件,haha—>person.ejs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>show</title>
</head>
<body>
<!--
       ejs语法:
            1.< % % >   里面能写任意js代码,但是不会向浏览器输出任何东西。
            2.< %- % >  能够将后端传递过来对象指定key所对应value渲染的页面
            3.< %= % >  能够将后端传递过来对象指定key所对应value渲染的页面
-->

<h1>我是一个新的页面</h1>
<h1>data</h1>
<!--<div>< %-data% ></div>
<div>< %=data% ></div>-->
<%
    for (var i=0; i<persons.length; i++ ){
      let item = persons[i] %>
      <ul>
          <li class="name">姓名:<%=item.name%></li>
          <li>年龄:<%=item.age%></li>
      </ul>
<%}%>

<h1><%=a%></h1>


</body>
</html>

在登录注册项目中的使用
view–>login.ejs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>登录</title>
    <style>
        .err{
            color: red;
        }
    </style>
</head>
<body>
<span class="err"><%=errMsg.networkErr%></span>
<span class="err"><%=errMsg.loginErr%></span>
<form action="http://localhost:3000/login" method="post">
  
  邮箱:<input type="email" name="email" value="<%=errMsg.email%>"><span class="err"><%=errMsg.emailErr%></span><br><br>
  密码:<input type="password" name="password"><span class="err"><%=errMsg.passwordErr%></span><br><br>
  <input type="submit">
  
</form>

</body>
</html>

view–>register.ejs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>注册</title>
    <style>
        .err{
            color: red;
        }
    </style>
</head>
<body>

<span class="err"><%=errMsg.networkErr%></span>
<form action="http://localhost:3000/register" method="post">
  
  邮箱:<input type="email" name="email" id="email"><span class="err"><%=errMsg.emailErr%></span><br><br>
  昵称:<input type="text" name="nick_name"><span class="err"><%=errMsg.nickErr%></span><br><br>
  密码:<input type="password" name="password"><span class="err"><%=errMsg.passwordErr%></span><br><br>
  重复密码:<input type="password" name="re_password"><span class="err"><%=errMsg.rePasswordErr%></span><br><br>
  <input type="submit">

</form>
<script type="text/javascript">

</script>

</body>
</html>

server.js

//引入express
const express = require('express')
//创建app应用对象
const app = express()
//禁止服务器返回X-Powered-By,为了安全
app.disable('x-powered-by')
//使用内置中间件暴露静态资源,不访问路由直接写文件名+后缀也能看页面
app.use(express.static(__dirname+'/public'))
//配置模板引擎
app.set('view engine','ejs')
app.set('views','./views')
//引入db模块,用于连接数据库
const db = require('./db/db')
//使用内置中间件用于解析post请求的urlencoded参数
app.use(express.urlencoded({extended:true}))
//引入UI路由器
const UIRouter = require('./router/UIRouter')
//引入登录注册路由器
const loginRegisterRouter = require('./router/loginRegisterRouter')
//逻辑:如果数据库连接成功,随后立即启动服务器,在整个过程中,无论多少次请求,数据库只连接一次。
db(()=>{

  //使用UIRouter
  app.use(UIRouter())
  //使用loginRegisterRouter
  app.use(loginRegisterRouter())

  //绑定端口监听
  app.listen(3000,(err)=>{
    if(!err) console.log('服务器启动成功!')
    else console.log(err)
  })
},(err)=>{
  console.log('数据库连接失败',err)
})

router–>UIRouter.js
将res.send(''),变成res.render('login',{})或res.render('register',{})

/*
* 专门用于管理展示界面的UI路由
* */

//引入Router构造函数
const {Router} = require('express')
//创建一个Router实例(路由器就是一个小型的app)
let router = new Router()
//引入path模块----Node中内置的一个专门用于解决路径问题的库
let {resolve} = require('path')


//用于展示登录界面的路由,无其他任何逻辑 ----- UI路由
router.get('/login',(req,res)=>{
  const {email} = req.query
  res.render('login',{errMsg:{email}})
})

//用于展示注册界面的路由,无其他任何逻辑 ----- UI路由
router.get('/register',(req,res)=>{
  res.render('register',{errMsg:{}})
})

module.exports = function () {
  return router
}

router–>loginRegisterRouter.js
将res.send(''),变成res.render('login',{})或res.render('register',{}),且用户注册成功后,直接跳转到登录界面并填好电子邮件信息

/*
* 专门用于管理登录、注册的业务路由
* */

//引入Router构造函数
const {Router} = require('express')
//创建一个Router实例(路由器就是一个小型的app)
let router = new Router()
//引入模型对象
let usersModel = require('../model/usersModel')

//用于处理用户的注册请求,有很多业务逻辑(校验数据的有效性等) -------- 业务路由
router.post('/register',(req,res)=>{
  /*
   { email: 'kobe@qq.com',
     nick_name: 'kobe',
     password: '123',
     re_password: '123' }
 */
  //1.获取用户的输入
  const {email,nick_name,password,re_password} = req.body
  /*
  * 2.校验数据的合法性:(一般是前台和后台同时验证。)
  *     2.1:校验成功
  *           -去数据库中查找该邮箱是否注册过
  *               2.1.1:注册过:提示用户邮箱已被占用。
  *               2.1.2:未注册:写入数据库
  *     2.2:校验失败
  *           -提示用户具体哪里输入的不正确
  * */

  //校验邮件的正则表达式
  const emailReg = /^[a-zA-Z0-9_]{4,20}@[a-zA-Z0-9]{2,10}\.com$/
  //校验昵称的正则表达式
  const nickNameReg = /[\u4e00-\u9fa5]/gm
  //校验密码的正则表达式
  const passwordReg = /^[a-zA-Z0-9_@#.+&]{6,20}$/

  const errMsg = {}
  //使用正则去校验
  if(!emailReg.test(email)){
    errMsg.emailErr = '邮箱格式不合法,用户名必须4-20位,主机名必须2-10位'
  }
  if(!nickNameReg.test(nick_name)){
    errMsg.nickErr = '昵称格式不合法,必须为中文'
  }
  if(!passwordReg.test(password)){
    errMsg.passwordErr = '密码格式不合法,必须6-20'
  }
  if( password !== re_password){
    errMsg.rePasswordErr = '两次输入密码不一致'
  }
  if(JSON.stringify(errMsg) !== '{}'){
    //若进入此判断,证明用户一定有输入错误的项,重新“打”回注册页面。
    res.render('register',{errMsg})
    return
  }
  //去数据库中查询该邮箱是否注册过
  usersModel.findOne({email},function (err,data) {
    if(data){
      //如果注册过
      //引入计数模块--当达到一个敏感的阈值,触发安全性机制。
      console.log(`邮箱为${email}的用户注册失败,因为邮箱重复`)
      errMsg.emailErr = `${email}邮箱已被注册,请更换邮箱`
      res.render('register',{errMsg})
      return
    }
    //如果没有注册过
    usersModel.create({email,nick_name,password},function (err) {
      if(!err){
        //如果写入成功了
        console.log(`邮箱为${email}的用户注册成功`)
        res.redirect(`/login?email=${email}`)
      }else{
        //如果写入失败了
        //引入报警模块,当达到敏感阈值,触发报警。
        console.log(err)
        errMsg.networkErr = `您当前的网络状态不稳定,稍后重试`
        res.render('register',{errMsg})
      }
    })
  })
})

//用于处理用户的登录请求,有很多业务逻辑(校验数据的有效性等) -------- 业务路由
router.post('/login',(req,res)=>{
  //1.获取输入
  const {email,password} = req.body
  //2.准备正则
  const emailReg = /^[a-zA-Z0-9_]{4,20}@[a-zA-Z0-9]{2,10}\.com$/
  const passwordReg = /^[a-zA-Z0-9_@#.+&]{6,20}$/
  //3.准备一个用于收集错误的对象
  const errMsg = {}

  if(!emailReg.test(email)){
    errMsg.emailErr = '邮箱格式不合法,用户名必须4-20位,主机名必须2-10位'
  }
  if(!passwordReg.test(password)){
    errMsg.passwordErr = '密码格式不合法,必须6-20'
  }
  if(JSON.stringify(errMsg) !== '{}'){
    res.render('login',{errMsg})
    return
  }
  //3.去数据库中查找:
  usersModel.findOne({email,password},(err,data)=>{
    if(err){
      //引入报警模块,当达到敏感阈值,触发报警。
      console.log(err)
      errMsg.networkErr = '网络不稳定,稍后重试'
      res.render('login',{errMsg})
      return
    }
    if(data){
      res.redirect('https://wwww.baidu.com')
      return
    }
    errMsg.loginErr = '用户名或密码输入错误!'
    res.render('login',{errMsg})
  })

})

module.exports = function () {
  return router
}

3 实现用户登陆后跳转个人中心页面


views—>userCenter.ejs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>个人中心</title>
</head>
<body>

<h1>hello,<%=nickName%></h1>

</body>
</html>

两种实现方法,方法一:直接在登陆成功后重定向到个人中心页面且通过查询字符串的方式,将用户名传给页面. 需要修改router--->UIRouter.js和router--->loginRegisterRouter.js
此处仅标记关键代码 问题:当你在用户栏输入数据库中任意字符,都会展示相应的个人中心页面(localhost:3000/user_center?nick_name=哈哈),且地址栏暴露信息太多
router—>UIRouter.js

//用于展示个人中心界面的路由,无其他任何逻辑 ----- UI路由
router.get('/user_center',(req,res)=>{
  const {nick_name} = req.query
  res.render('userCenter',{nickName:nick_name})
})

router—>loginRegisterRouter.js


//用于处理用户的登录请求,有很多业务逻辑(校验数据的有效性等) -------- 业务路由
router.post('/login',(req,res)=>{
  //1.获取输入
  const {email,password} = req.body
  //2.准备正则
  const emailReg = /^[a-zA-Z0-9_]{4,20}@[a-zA-Z0-9]{2,10}\.com$/
  const passwordReg = /^[a-zA-Z0-9_@#.+&]{6,20}$/
  //3.准备一个用于收集错误的对象
  const errMsg = {}

  if(!emailReg.test(email)){
    errMsg.emailErr = '邮箱格式不合法,用户名必须4-20位,主机名必须2-10位'
  }
  if(!passwordReg.test(password)){
    errMsg.passwordErr = '密码格式不合法,必须6-20'
  }
  if(JSON.stringify(errMsg) !== '{}'){
    res.render('login',{errMsg})
    return
  }
  //3.去数据库中查找:
  usersModel.findOne({email,password},(err,data)=>{
    if(err){
      //引入报警模块,当达到敏感阈值,触发报警。
      console.log(err)
      errMsg.networkErr = '网络不稳定,稍后重试'
      res.render('login',{errMsg})
      return
    }
    if(data){
      res.redirect(`http://localhost:3000/user_center?nick_name=${data.nick_name}`) //此种方法浏览器的地址栏残留的东西太多了。
      return
    }
    errMsg.loginErr = '用户名或密码输入错误!'
    res.render('login',{errMsg})
  })

})

方法二:用户重新登陆后重新按照userCenter.ejs渲染页面,需要修改router--->loginRrgisterRouter.js 问题:此种方式会导致浏览器地址栏依然是login--浏览器的地址栏不变化。


//用于处理用户的登录请求,有很多业务逻辑(校验数据的有效性等) -------- 业务路由
router.post('/login',(req,res)=>{
  //1.获取输入
  const {email,password} = req.body
  //2.准备正则
  const emailReg = /^[a-zA-Z0-9_]{4,20}@[a-zA-Z0-9]{2,10}\.com$/
  const passwordReg = /^[a-zA-Z0-9_@#.+&]{6,20}$/
  //3.准备一个用于收集错误的对象
  const errMsg = {}

  if(!emailReg.test(email)){
    errMsg.emailErr = '邮箱格式不合法,用户名必须4-20位,主机名必须2-10位'
  }
  if(!passwordReg.test(password)){
    errMsg.passwordErr = '密码格式不合法,必须6-20'
  }
  if(JSON.stringify(errMsg) !== '{}'){
    res.render('login',{errMsg})
    return
  }
  //3.去数据库中查找:
  usersModel.findOne({email,password},(err,data)=>{
    if(err){
      //引入报警模块,当达到敏感阈值,触发报警。
      console.log(err)
      errMsg.networkErr = '网络不稳定,稍后重试'
      res.render('login',{errMsg})
      return
    }
    if(data){
      res.render('userCenter',{nickName:data.nick_name}) //此种方式会导致浏览器地址栏依然是login--浏览器的地址栏不变化。
      return
    }
    errMsg.loginErr = '用户名或密码输入错误!'
    res.render('login',{errMsg})
  })

})

4 cookie


cookie是一般服务器生成交给客户端(浏览器)保管的,是为了解决http无状态,是后端在返回响应时的同时"种"下(附属操作),客户端下次请求自动携带,分为:会话cookie,持久化cookie

/*
* 关于cookie:
*     1.是什么?
*         本质就是一个【字符串】,里面包含着浏览器和服务器沟通的信息(交互时产生的信息)。
*         存储的形式以:【key-value】的形式存储。
*         浏览器会自动携带该网站的cookie,只要是该网站下的cookie,全部携带。
*     2.分类:
*           --会话cookie(关闭浏览器后,会话cookie会自动消失,会话cookie存储在浏览器运行的那块【内存】上)。
*           --持久化cookie:(看过期时间,一旦到了过期时间,自动销毁,存储在用户的硬盘上,备注:如果没有到过期时间,同时用户清理了浏览器的缓存,持久化cookie也会消失)。
*
*     3.工作原理:
*           --当浏览器第一次请求服务器的时候,服务器可能返回一个或多个cookie给浏览器
*           --浏览器判断cookie种类
*               --会话cookie:存储在浏览器运行的那块内存上
*               --持久化cookie:存储在用户的硬盘上
*           --以后请求该网站的时候,自动携带上该网站的所有cookie(无法进行干预)
*           --服务器拿到之前自己“种”下cookie,分析里面的内容,校验cookie的合法性,根据cookie里保存的内容,进行具体的业务逻辑。
*
*      4.应用:
*           解决http无状态的问题(例子:7天免登录,一般来说不会单独使用cookie,一般配合后台的session存储使用)
*
*      5.不同的语言、不同的后端架构cookie的具体语法是不一样的,但是cookie原理和工作过程是不变的。
*         备注:cookie不一定只由服务器生成,前端同样可以生成cookie,但是前端生成的cookie几乎没有意义。
*
*     6.对比浏览器的本地存储:
*         1.localStorage:
*             (1).保存的数据,只要用户不清除,一直存在
*             (2).作为一个中转人,实现跨页签通信。
*             (3).保存数据的大小:5MB - 10MB
*         2.sessionStorage:
*             (1).保存的数据,关闭浏览器就消失
*             (3).保存数据的大小:5MB - 10MB
*         3.cookie:
*             (1).分类:会话cookie----关浏览器消失、持久化cookie----到过期时间消失
*             (2).保存数据的大小:4K ---  8K
*             (3).主要用于解决http无状态(一般配合后端的session会话存储使用)
*             (4).浏览器请求服务器时,会自动携带该网站的所有cookie
* */

在node平台使用express框架搭建服务器时,cookie的操作

 let express = require('express')
let cookieParser = require('cookie-parser')

let app = express()
app.use(cookieParser())

//demo路由不对cookie进行任何操作
app.get('/demo',function (req,res) {
  res.send('我是demo路由给你的反馈,我没有对cookie进行任何的操作')
})

//会话cookie,关闭浏览器即立刻消失
//demo1路由,负责给客户端“种”下一个会话cookie
app.get('/demo1',function (req,res) {
  //express中给客户端“种”cookie不需要任何的库
  let obj = {school:'atguigu',subject:'qianduan'}
  res.cookie('peiqi',JSON.stringify(obj)) //给客户端种下一个会话cookie
  res.send('我是demo1路由给你的反馈,我给你种下了一个【会话cookie】,你赶紧去浏览器里看看!')
})

//demo2路由,负责给客户端“种”下一个持久化cookie
app.get('/demo2',function (req,res) {
  let obj = {school:'atguigu2',subject:'qianduan2'}
  res.cookie('peiqi',JSON.stringify(obj),{maxAge:1000 * 60 * 60 *24 *30}) //给客户端种下一个持久化cookie
  res.send('我是demo2路由给你的反馈,我给你种下了一个【持久化cookie】,你赶紧去浏览器里看看!')
})

//demo3路由,负责读取客户端所携带过来的cookie
app.get('/demo3',function (req,res) {
  //express中读取客户端携带过来的cookie要借助一个中间件,名为:cookie-parser(yarn add cookie-parser)
  console.log(req.cookies);
  const {peiqi} = req.cookies
  let a = JSON.parse(peiqi)
  console.log(a.school)
  res.send('我是demo3路由,我读取了你携带过来的cookie,你去服务器控制台看看吧')
})

//demo4路由,负责告诉客户端删除一个cokkie
app.get('/demo4',function (req,res) {
  //res.cookie('peiqi','',{maxAge:0})
  res.clearCookie('peiqi')
  res.send('兄台,我删除了你所保存的key为peiqi的那个cookie')
})

app.listen(3000,function (err) {
  if(err) console.log(err)
  else console.log('演示cokkie服务器启动成功了')
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值