• egg框架的使用


    一、EGG框架简介和安装

    1.egg.js

    • egg.js是约定(按照一套统一的约定进行应用开发 )优先于配置的一个Node.js web 框架
    • 基于koa为底层 由阿里nodejs团队封装的企业级Web应用解决方案
    • 核心设计理念:约束和规范化团队开发、帮助开发团队和开发人员降低开发和维护成本
    • 为企业级框架和应用而生 可以开发企业级的应用
    • 可以通过egg更平滑地学习后端的相关知识

    2.egg安装

    1. 创建一个项目文件夹
    2. 在此目录下cmd打开命令行窗口
    3. 输入快速初始化指令 npm init egg --type=simple ; npm i
    4. 启动项目  npm run dev

    注:下载过程中可能因为种种原因导致丢包,重新安装或npm i即可

    3.约定规则

    app/router.js:用于配置URL路由规则

    app/controller/:用于解析用户的输入,处理后返回相应的结果

    app/service/用于编写业务逻辑层

    app/public/: 用于放置静态资源

    config/config.default.js: 用于编写配置文件

    config/plugin.js 用于配置需要加载的插件

    二、骨架认知

    1.静态资源 public

    • public文件夹内部的所有文件或文件夹都将被托管起来

    2.路由Router

    • 主要用来描述请求 URL 和具体承担执行动作的 Controller 的对应关系
    • 即用户访问不同的路径时应该有不同的Controller去响应不同的内容

    1)注册路由:

    • 当用户访问的pathname为/test时 后端就会执行controller文件夹中的home.js文件夹中的test函数
    router.get('/test', controller.home.test);

    2)注册的路由名与前端静态资源文件名冲突时优先访问静态资源:

    • 优先读取public的静态资源 没有就去看路由有没有注册过 也没有注册过就返回404
    • 网络请求时 后端的处理顺序:静态文件>路由匹配(按照顺序匹配)

     示例:

     

     3)相同的路由名仅匹配第一个函数, 不同的路由名可以调相同的函数

    • 相同的路由名从上至下仅匹配一次函数 不会继续匹配 即执行test1函数不会执行test2函数

    4)星号路由 * 代表所有的网址都能匹配

    • 一般放在最后 否则在其之后所有的文件都会调用这个函数 不会调用原本对应的函数

    3.控制器Controller

    • 负责解析用户的输入 处理后返回相应的结果
    • 可以自定义要导出模块的类继承Controller
    • 类里面的每一个方法都可以作为一个 Controller 在 Router 中引用到
    • 可以从 app.controller 根据文件名和方法名定位到这个类
    • 项目中的 Controller 类继承于 egg.Controller
    • 所有的Controller 文件都必须放在 app/controller目录下
    • 支持多级目录,访问时可以通过目录名级联访问

    this的属性:

    this.ctx: 当前请求的上下文 Context 对象的实例,通过它可以拿到框架封装好的处理当前请求的各种便捷属性和方法

    this.ctx.body="":  body指的是数据包的响应体  相当于res.end 只执行一次 后断开网络连接

    this.app: 当前应用 Application 对象的实例,通过它可以拿到框架提供的全局对象和方法

    this.service:应用定义的 Service,通过它可以访问到抽象出的业务层==> this.ctx.service 

    this.config:应用运行时的配置项

    this.logger:logger 对象,上面有四个方法(debug,info,warn,error),分别代表打印四个不同级别的日志,使用方法和效果与 context logger 中介绍的一样,但是通过这个 logger 对象记录的日志,在日志前面会加上打印该日志的文件路径,以便快速定位日志打印位置

    三、跨域

    1.CORS配置

    • egg-cors 插件实现cors跨域请求

     1)下载
       npm i egg-cors

     2)开启并配置插件

     默认origin只支持一个具体的域名或*表示全部,如果想支持具体的多个指定域名需进行如下设置:

    1. config.cors = {
    2.         origin:function(ctx) { //设置允许来自指定域名请求
    3.             console.log(ctx);
    4.             const whiteList = ['http://www.baidu.com','http://www.hqyj.com']; //白名单列表
    5.             let url = ctx.request.header.origin;
    6.             if(whiteList.includes(url)){
    7.                 return url;
    8.             }
    9.             return 'http://localhost' //默认允许本地请求可跨域
    10.         },
    11.         allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
    12.     };

     本来本地服务器是不能请求到7001服务器的数据的 但是因为配置了cors插件 允许所有的服务器访问 解决了跨域问题

    2.JSONP配置

    • 如果前端的参数中有cb=fn参数(jsonp接口参数),将会返回JSONP格式的数据,否则返回JSON格式的数据 设置如下:

     通过返回的函数名判断是否存在函数,若存在就执行函数

    3.代理配置

    egg中的网络请求技术:

    this.ctx.curl(url, option)

    option常用配置:

      method:'GET/POST'

      data:{name:"karen"}//会自动字符串化

    返回一个promise对象

    示例:(仅以百度为示例)

    index.html自己请求百度网址的数据时会跨域 报错

    解决办法:

    四、GET请求前后端

    • 发送的数据会显示在数据包中 参数拼接到url中 不安全 但速度快
    • egg后端接收前端GET发送的参数通过this.ctx.request.query获取

    1.ajax发送GET请求

    • GET请求将参数传给后端
    • 后端返回的数据由前端的xhr对象接收 程序员用js语言来使用返回的数据

      示例:

      get.html

    1. <h1>ajax发送GET请求h1>
    2. <input type="text" id="search">
    3. <button onclick="fn()">搜索button>
    4. <script>
    5. function fn() {
    6. var searchvalue=document.querySelector("#search").value
    7. var xhr = new XMLHttpRequest()
    8. var url=`http://192.168.43.17:7001/get/?keyword=${searchvalue}`
    9. xhr.open("GET",url)
    10. xhr.send()
    11. xhr.onreadystatechange = () => {
    12. if (xhr.readyState == 4 && xhr.status == 200) {
    13. console.log(xhr.responseText)
    14. }
    15. }
    16. }
    17. script>

      router.js

    router.get('/get', controller.home.get);

       home.js

    1. async get() {
    2. //keyword为egg后端接收到的前端GET请求发送的参数
    3. var keyword = this.ctx.request.query
    4. console.log(keyword)
    5. this.ctx.body = {
    6. info: "get接口的数据"
    7. }
    8. }

     请求结果:

     2.axios发送GET请求

    • ajax技术封装出来的 底层就是ajax 参数可以写在对象中{params}  也可以像ajax直接拼在url中
    • 后端返回的数据由前端的xhr对象接收 程序员用js语言来使用返回的数据

    示例:

     get.html

    1. <h1>axios发送GET请求h1>
    2. <input type="text" id="search">
    3. <button onclick="fn()">搜索button>
    4. <script>
    5. function fn() {
    6. var searchvalue = document.querySelector("#search").value
    7. var url = `http://192.168.43.17:7001/get`
    8. axios(url, {params: {keyword: searchvalue}})
    9. .then(res=>console.log(res))
    10. /*参数querystring直接拼在网址中
    11. axios("协议://ip:port/pathname?参数querystring")
    12. .then(res=>console.log(res))
    13. */
    14. }
    15. script>

      router.js

    router.get('/get', controller.home.get);

       home.js

    1. async get() {
    2. //keyword为egg后端接收到的前端GET请求发送的参数
    3. var keyword = this.ctx.request.query
    4. console.log(keyword)
    5. this.ctx.body = {
    6. info: "get接口的数据--axios"
    7. }
    8. }

     请求结果:

    3.浏览器的地址栏发送GET请求

    • 只能发送GET请求
    • 接收的数据浏览器会直接读取渲染(图片、文本等)   读取/解析失败如压缩包会下载

    4.a-href发送GET请求

    • 只能发送GET请求
    • 点击事件触发了默认事件才会发送GET请求(必须满足两个事件的条件)
    • 即先触发a标签的点击事件 再触发href的网络请求事件
    • 发送网络请求给href的网址,后端返回的数据,会直接读取渲染(target决定是否在新窗口渲染),读取(解析)失败如压缩包会下载

    示例:

    <a href="http://192.168.43.17:7001/get?name=嘻嘻嘻">a标签做网络请求a>

    请求结果: 

    5.img-src发送GET请求

    • 只能发送GET请求
    • 返回的数据渲染成图片 如果是非图片编码就会“碎裂” 地址正确一定能请求到编码 只关心能否渲染

    6.link-href发送GET请求

    • 只能发送GET请求 返回的数据按照加载的数据的对应功能使用

    7.form发送GET请求

    • 唯一能传输文件的网络请求技术
    • 可以发送get/post/delete等请求 给action属性对应的url发送请求
    • 用户点击了(表单内部)提交按钮或者触发表单的提交事件event.submit()
    • GET请求会把form表单中的数据全部解析为url的querystring发送给后端
    • 返回的数据 同a标签 渲染/下载

     示例:

    1. <form action="http://192.168.43.17:7001/get" method="get" target="_blank">
    2. //表单的input标签的name属性的值就是querystring的key值
    3. <input type="text" name="a">
    4. <input type="text" name="b">
    5. <button type="submit">提交button>
    6. form>

     请求结果:

    五、POST请求前后端

    • 不会把用户的隐私数据信息直接拼到url中发给后端
    • 用暗文发送 可以发送大量的数据给后端  响应数据相对较慢 但是较为安全
    • POST请求时 会有安全验证问题 需要关闭安全验证
    • egg后端接收POST字段和文件通过 this.ctx.request.body | this.ctx.request.files

    1.ajax发送POST请求

     示例:

      post.html

    1. <h1>ajax发送POST请求h1>
    2. 账号:<input type="text" id="id"><br>
    3. 密码:<input type="text" id="pwd">
    4. <button onclick="fn()">登录button>
    5. <script>
    6. function fn(){
    7. var id=document.querySelector("#id").value
    8. var pwd=document.querySelector("#pwd").value
    9. var xhr = new XMLHttpRequest()
    10. var url="http://192.168.43.17:7001/post"
    11. xhr.open("POST",url,true)
    12. xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")
    13. xhr.send(`id=${id}&pwd=${pwd}`)
    14. //send函数接收字符串querystring
    15. //如果是“POST”就会把这个请求的数据放在“请求数据包”-HTTPRequestMessage的请求体中的
    16. //如果是“GET” 不会报错 但是不会把这个数据拼接到url中发送
    17. xhr.onreadystatechange = () => {
    18. if (xhr.readyState == 4 && xhr.status == 200) {
    19. console.log(xhr.responseText)
    20. }
    21. }
    22. }

      router.js

    router.post('/post', controller.home.post);

       home.js

    1. async post() {
    2. //前端POST发送给egg的参数字段
    3. let obj= this.ctx.request.body
    4. console.log(obj)
    5. this.ctx.body = {
    6. info: "post接口的数据",
    7. res:obj
    8. }
    9. }

    请求结果: 

    2.axios发送POST请求

    1)axios.post(url,{参数字段}).then() 

    示例:

    1. <h1>axios发送POST请求h1>
    2. 账号:<input type="text" id="id"><br>
    3. 密码:<input type="text" id="pwd">
    4. <button onclick="fn()">登录button>
    5. <script>
    6. function fn(){
    7. var id=document.querySelector("#id").value
    8. var pwd=document.querySelector("#pwd").value
    9. var url="http://192.168.43.17:7001/post"
    10. axios.post(url,{id:id,pwd:pwd}).then(res=>console.log(res))
    11. }
    12. script>

     请求结果:


    2)axios.post(url,参数字段/文件).then()

    1.上传文件

     示例:

      postfile.html

    1. <h1>POST发送文件h1>
    2. 账号:<input type="text" id="id"><br><br>
    3. 密码:<input type="text" id="pwd"><br><br>
    4. 选择头像:<input type="file" id="f1" multiple><br><br>
    5. <button onclick="fn()">登录button>
    6. <script>
    7. //给后端发送“大量”数据 文件 处理成表单数据
    8. function fn() {
    9. var id=document.querySelector("#id").value
    10. var pwd=document.querySelector("#pwd").value
    11. var f1=document.querySelector("#f1")
    12. var url="http://192.168.43.17:7001/postfile"
    13. var fdata = new FormData()
    14. fdata.append("id", id)
    15. fdata.append("pwd", pwd)
    16. fdata.append("img", f1.files[0])
    17. console.log(f1.files[0])
    18. axios.post(url,fdata).then(res=>console.log(res))
    19. }
    20. script>

      router.js

    router.post('/postfile', controller.home.postfile);

      home.js

    1. async postfile() {
    2. //前端POST发送给egg的参数字段
    3. let ziduan= this.ctx.request.body
    4. //前端POST发送给egg的参数文件
    5. let f=this.ctx.request.files //必须启用file接收文件
    6. console.log(ziduan,f)
    7. this.ctx.body = {
    8. info: "postfile接口的数据"
    9. }
    10. }

     请求结果:

    2.移动文件

    • 用户上传的文件会保存在服务器的计算的临时路径 需要移动到项目目录中(在同一个磁盘下)

    示例:

     home.js

    1. //引入模块
    2. const fs=require("fs")
    3. const path=require("path")
    4. async postfile() {
    5. //前端POST发送给egg的参数字段
    6. let ziduan= this.ctx.request.body
    7. //前端POST发送给egg的参数文件
    8. let f=this.ctx.request.files //必须启用file接收文件
    9. console.log(ziduan,f)
    10. if(f[0]){
    11. let oldpath=f[0].filepath
    12. let fname=path.basename(oldpath)
    13. let newpath=__dirname+"/../public/upload"+fname //将文件移动到upload中
    14. fs.renameSync(oldpath,newpath)
    15. }
    16. this.ctx.body = {
    17. info: "postfile接口的数据"
    18. }
    19. }

    六、前端注册实例

    1.前端表单验证--减轻服务器的负载--DOM操作写提示

         -邮箱验证:绑定change事件  判断value是否符合邮箱正则

         -密码验证:密码要符合格式(自己定制)两次密码一致

         -昵称验证:昵称要符合格式(自己定制)

         -身份证的验证:图片的格式和大小清晰度-预览

    2.点击提交按钮时 获取用户交互的信息

    3.把数据post发送给后端

    4.等待后端返回数据

    5.操作数据

      register.html

    1. <style>
    2. .main {
    3. width: 600px;
    4. height: 800px;
    5. margin: 0 auto;
    6. text-align: center;
    7. border: 1px black solid;
    8. }
    9. .idbox {
    10. width: 200px;
    11. height: 100px;
    12. background-image: url("img/rose.jpg");
    13. background-size: 200px 100px;
    14. margin: auto;
    15. }
    16. #idimg {
    17. width: 200px;
    18. height: 100px;
    19. opacity: 0;
    20. }
    21. .txbox {
    22. width: 100px;
    23. height: 100px;
    24. background-image: url("img/rose1.jpg");
    25. background-size: 200px 100px;
    26. margin: auto;
    27. }
    28. #tximg {
    29. width: 100px;
    30. height: 100px;
    31. opacity: 0;
    32. }
    33. style>
    34. <div class="main">
    35. <h1>用户注册h1>
    36. <hr>
    37. 昵称: <input type="text" id="nc"><br><br>
    38. 邮箱: <input type="text" id="email"><br><br>
    39. 密码: <input type="password" id="pwd1"><br><br>
    40. 确认密码: <input type="password" id="pwd2"><br><br>
    41. 上传身份证: <div class="idbox"> <input type="file" id="idimg">div>
    42. <span style="color: red;visibility: hidden;" id="span1">/* 只能是png、jpeg、jpg格式的图片 */span><br>
    43. <span style="color: red;visibility: hidden;" id="span2">/* 小于了15kb或大于了1M */span><br>
    44. 选择头像:<div class="txbox"> <input type="file" id="tximg">div>
    45. <br>
    46. <button onclick="fn()">提交button>
    47. div>
    48. <script>
    49. var arr = new Array(6).fill(true)
    50. //邮箱验证
    51. var email = document.querySelector("#email")
    52. email.addEventListener("change", () => {
    53. var reg = /^[a-zA-Z0-9]+([-_.][A-Za-zd]+)*@([a-zA-Z0-9]+[-.])+[A-Za-zd]{2,5}$/
    54. if (reg.test(email.value)) {
    55. email.style.border = "3px solid green"
    56. arr[0] = true
    57. } else {
    58. email.style.border = "3px solid red"
    59. arr[0] = false
    60. }
    61. })
    62. //密码验证
    63. var pwd1 = document.querySelector("#pwd1")
    64. var pwd2 = document.querySelector("#pwd2")
    65. pwd1.addEventListener("change", () => {
    66. /*1.密码必须由字母、数字组成,区分大小写
    67. 2.密码长度为8-18位
    68. */
    69. var reg = /(?=.*[a-zA-Z])(?=.*[0-9])[A-Za-z0-9]{8,18}$/
    70. if (reg.test(pwd1.value)) {
    71. pwd1.style.border = "3px solid green"
    72. arr[1] = true
    73. } else {
    74. pwd1.style.border = "3px solid red"
    75. arr[1] = false
    76. }
    77. })
    78. pwd2.addEventListener("change", () => {
    79. /*1.密码必须由字母、数字组成,区分大小写
    80. 2.密码长度为8-18位
    81. */
    82. var reg = /(?=.*[a-zA-Z])(?=.*[0-9])[A-Za-z0-9]{8,18}$/
    83. if (reg.test(pwd2.value) && (pwd2.value == pwd1.value)) {
    84. pwd1.style.border = "3px solid green"
    85. arr[2] = true
    86. } else {
    87. pwd2.style.border = "3px solid red"
    88. arr[2] = false
    89. }
    90. })
    91. //昵称验证
    92. var nc = document.querySelector("#nc")
    93. nc.addEventListener("change", () => {
    94. var reg = /(^[\u4E00-\u9FA5·]{2,16}$)|^[a-zA-Z][a-zA-Z\s]{0,20}[a-zA-Z]$/
    95. if (reg.test(nc.value)) {
    96. nc.style.border = "3px solid green"
    97. arr[3] = true
    98. } else {
    99. nc.style.border = "3px solid red"
    100. arr[3] = false
    101. }
    102. })
    103. //身份证验证
    104. var idimg = document.querySelector("#idimg")
    105. idimg.addEventListener("change", () => {
    106. console.log(idimg.files)
    107. //预览
    108. var url1 = window.URL.createObjectURL(idimg.files[0])
    109. console.log(url1)
    110. var idbox = document.querySelector(".idbox")
    111. idbox.style.backgroundImage = `url(${url1})`
    112. //验证格式和大小
    113. var arr = ["image/png", "image/jpeg", "image/jpg"]
    114. if (arr.includes(idimg.files[0].type)) {
    115. span1.style.visibility = "hidden"
    116. arr[4] = true
    117. } else {
    118. span1.style.visibility = ""
    119. arr[4] = false
    120. }
    121. if (15 * 1024 > idimg.files[0].size || idimg.files[0].size > 1 * 1024 * 1024) {
    122. span2.style.visibility = ""
    123. arr[4] = false
    124. } else {
    125. span2.style.visibility = "hidden"
    126. arr[4] = true
    127. }
    128. })
    129. //头像验证
    130. var tximg = document.querySelector("#tximg")
    131. tximg.addEventListener("change", () => {
    132. var txbox = document.querySelector(".txbox")
    133. var url = window.URL.createObjectURL(tximg.files[0])
    134. txbox.style.backgroundImage = `url(${url})`
    135. })
    136. async function fn() {
    137. if (arr.includes(false)) {
    138. } else {
    139. //所有的验证都通过了才会向后端发送数据
    140. var fdata = new FormData()
    141. var nc = document.querySelector("#nc")
    142. var email = document.querySelector("#email")
    143. var pwd = document.querySelector("#pwd")
    144. var idimg = document.querySelector("#idimg")
    145. var tximg = document.querySelector("#tximg")
    146. fdata.append("昵称",nc.value)
    147. fdata.append("邮箱",email.value)
    148. fdata.append("密码",pwd1.value)
    149. fdata.append("身份证",idimg.files[0])
    150. fdata.append("头像",tximg.files[0])
    151. var re = await axios.post("/register", fdata)
    152. console.log(re)
    153. }
    154. }
    155. script>

      register.js

    1. async register() {
    2. let ziduan=this.ctx.request.body
    3. let f1=this.ctx.request.files
    4. console.log(ziduan,f1)
    5. this.ctx.body="注册"
    6. }

      router.js

    router.post('/register', controller.register.register);

     请求结果:

     

  • 相关阅读:
    echarts地图的散点,在时间轴上,为什么点击中国地图的江苏省,中国地图上的散点会散开
    Linux之基于Centos系统安装Redis、MySQL、Nginx
    代理模式和单一职责原理一文读懂(设计模式与开发实践 P6)
    Python文件操作
    Oracle EBS Interface/API(45)- 销售发运自动创建交货单
    ESP32(基于Arduino)连接EMQX的Mqtt服务器上传信息与命令控制
    第二章《Java程序世界初探》第8节:条件运算符
    LZ4压缩算法简介
    方法递归调用
    S32Kxxx bootloader之UDS bootloader
  • 原文地址:https://blog.csdn.net/qq_56668869/article/details/126142942