最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Node RESTful

    正文概述 掘金(将军肚)   2021-01-06   527

    Node RESTful

    一 RESTful API 理论

    1.1 REST 是什么?

      • web软件架构风格
        • 用来创建网络服务
      • Representational State Transfer
        • Representational: 数据的表现形式( 最佳实践JSON )
        • State: 当前状态或数据
        • Transfer: 数据传输
    • 6个限制
      • 客户-服务器(Client - Server)
        • 关注点分离
        • 服务端专注数据存储,提升了简单性
        • 前端专注用户界面,提升了可移植性
      • 无状态 (Stateless)
        • 所有用户会话信息都保存在客户端
        • 每次请求必须包括所有信息,不能依赖上下文信息
        • 服务端不保存会话信息:提升了简单性、可靠性、可见性
      • 缓存(Cache)
        • 所有服务端响应都要被标为可缓存或不可缓存
        • 缓存减少前后端交互,提升了性能
      • 统一接口 (Uniform Interface)
        • 统一指接口设计尽可能统一通用,提升了简单性、可见性
        • 接口与实现解耦,使前后端可独立开发迭代
        • 统一接口的子限制
          • 资源的标识
            • 资源是任何可命名的事物,如用户、评论等
            • 每个资源可被URI唯一标识
          • 通过表述来操作资源
            • 表述就是Representation 如JSON XML等
            • 客户端不能直接操作SQL服务端资源
            • 客户端应通过表述(如JSON)来操作资源
          • 自描述消息
            • 每个消息(请求或响应)必须提供足够的信息让接受者理解
            • 通过媒体类型(application/json , application/xml)
            • HTTP方法:GET 、 POST 、 DELETE
            • 是否缓存: Cache-control
          • 超媒体作为应用状态引擎
            • 超媒体:带文字的链接
            • 应用状态:一个网页
            • 引擎:驱动、跳转
            • 合起来:点击链接跳转到另一个网页
      • 分层系统(Layered System)
        • 每层只知道相邻的一层,后面隐藏的就不知道了
        • 客户端不知道是和代理还是真实服务器通信
        • 其他层:安全层、负载均衡、缓存层等
      • 按需代码(Code-On-Demand) 可选
        • 客户端可下载运行服务端传来的代码(如JS)
        • 通过减少一些功能,简化了客户端

    1.2 RESTful API

    • 即符合REST架构风格的API
    • 基本的URI
      • api.gethub.com/users
    • 标准HTTP方法
      • GET / POST / PUT / PATCH / DELETE
    • 传输的数据媒体类型
      • JSON / XML
    • 实例
      • GET /users ---- 获取user列表
      • GET /users/12 ---- 查看某个具体的 user
      • POST /users ---- 新建一个 user
      • PUT /users/12 ---- 整体替换更新 user 12
      • PATCH /users/:name ---- 部分更新,只更新name
      • DELETE /users/12 ---- 删除 user12

    1.3 RESTful API 最佳实践

    • 请求设计规范
      • URI使用名词,尽量用复数,如/users
      • URI使用嵌套表示关联关系,如/users/12/repos/5
      • 使用正确的HTTP方法,如GET / POST / PUT / DELETE
      • 不符合CRUD情况:POST/action/子资源
    • 响应设计规范
      • 查询
        • https://api.github.com/users?since=100
      • 分页
        • https://api.github.com/user/repos?page=2&per_page=100
      • 字段过滤
        • https://api.github.com/users/username?fileds=name
      • 状态码
        • Status
      • 错误处理
        •   HTTP/1.1 422  
            {  
              “message”: “Validation Failed”,  
              “errors”: [  
                {  
                  “resource”: ‘Issue’,  
                  “field”: “title”,  
                  “code”: “missing_field”     
                }  
              ]  
            }  
          
    • 安全
      • HTTPS
      • 鉴权
      • 限流
    • 增删改查应返回什么响应?
      • 增改:应返回修改的对象
      • 删:应返回 204 (没有内容但成功了)

    二 Koa2

    2.1 简介

    • 基于NodeJS下一代web框架
    • Web应用和API开发领域
    • 更小、更富有表现力、更健壮
    • 利用async函数,丢弃了回调函数
    • 增强错误处理:try catch
    • 没有捆绑任何中间件

    2.2 Hello world

    • npm init

    • npm i koa --save

    • npm i nodemon --save-dev

    • package.json

      "scripts": {  
          "start": "nodemon index.js"  
        },  
      
    • index.js

      const Koa = require("koa");  
      const app = new Koa();  
          
      app.use((ctx) => {  
        ctx.body = "hello world";  
      });  
          
      app.listen(3000);  
      
    • npm start

    2.3 koa中间件与洋葱模型

    •   app.use(async (ctx, next) => {  
          console.log(1);  
          await next();  
          console.log(3);  
        });  
        app.use((ctx) => {  
          console.log(2);  
        });  
      

    2.4 路由

    • 处理不同的URL
    • 处理不同的HTTP方法
    • 解析URL上的参数
    • 在koa中,是一个中间件
      •   const Koa = require("koa");  
          const app = new Koa();  
                  
          app.use(async ctx => {  
            if (ctx.url === "/") {  
              ctx.body = "this is home page";  
            } else if (ctx.url === "/users") {  
              if (ctx.method === "GET") {  
                ctx.body = "this is users list page";  
              } else if (ctx.method === "POST") {  
                ctx.body = "this is create new user page";  
              } else {  
                ctx.status = 405;    
                // 方法不允许 Method Not Allowed  
              }  
            } else if (ctx.url.match(/\/users\/\w+/)) {  
              const userId = ctx.url.match(/\/users\/(\w+)/)[1];  
              ctx.body = `this is user id ${userId}`;  
            } else {  
              ctx.status = 404;  
              // 未找到 Not Found  
            }  
          });  
                  
          app.listen(3000);  
        
    • 使用koa-router实现路由
      •   const Koa = require("koa");  
          const Router = require("koa-router");  
          const app = new Koa();  
          const router = new Router();  
          const usersRouter = new Router({ prefix: "/users" });  
                  
          app.use(router.routes());  
          app.use(usersRouter.routes());  
                  
          const auth = async (ctx, next) => {  
            if (ctx.url !== "/users") {  
              ctx.throw(401);   
               // 未受权 Unauthorized  
            }  
            await next();  
          };  
                  
          router.get("/", (ctx) => {  
            ctx.body = "this is home page";  
          });  
                  
          usersRouter.get("/", auth, (ctx) => {  
            ctx.body = "this is users list page";  
          });  
                  
          usersRouter.post("/", auth, (ctx) => {  
            ctx.body = "this is create new user";  
          });  
                  
          usersRouter.get("/:id", auth, (ctx) => {  
            ctx.body = `this is user ${ctx.params.id}`;  
          });  
                  
          app.listen(3000);  
        

    2.5 HTTP options

    • 检测服务器所支持的请求方法
      • 响应中 headers => allow 中会列举支持的HTTP方法
    • CORS中的预检请求
    • koa-router中的allowedMethods
      • app.use(usersRouter.allowedMethods());
      • 响应options方法,告诉它所支持的请求方法
      • 自动返回405(不允许)
        • 比如users未实现delete,但发送了DELETE请求,将返回405
      • 自动返回501(没实现)
        • koa不支持的方法会返回501

    2.6 增删改查响应

    •   usersRouter.get("/", (ctx) => {  
          // this is users list page  
          ctx.body = [{ name: "jon" }, { name: "lily" }];  
        });  
            
        usersRouter.post("/", (ctx) => {  
          // this is create new user  
          ctx.body = { name: "jon" };  
        });  
            
        usersRouter.get("/:id", (ctx) => {  
          // this is user jon  
          ctx.body = { name: "jon" };  
        });  
            
        usersRouter.put("/:id", (ctx) => {  
          // this is after change  
          ctx.body = { name: "jon2" };  
        });  
            
        usersRouter.delete("/:id", (ctx) => {  
          // this is after del  
          ctx.status = 204;  
        });  
      

    2.7 控制器

    • 拿到路由分配的任务,并执行
    • 在koa中,控制器也是中间件
    • 获取HTTP请求参数
      • Query String
        • 如?q=keyword
          • ctx.query:{q:li}
      • Router Params
        • 如/users/jon
          • ctx.params:{id:’jon’}
      • Body
        • 如{name: ‘jon’}
          • npm i koa-bodyparser -S
            1. Postman设置body -> row => JSON
            2. postman Headers添加数据 {"name": “jon”}
          • ctx.request.body
      • Header
        • 如Accept, Cookie
          • ctx.header
      • VSCode 调试API
          1. 停止服务
          2. 点击Run -> Run Script: start
          3. 打断点
          4. Postman中发送请求
          5. 调试
          • 可右键 add to watch
    • 处理业务逻辑
    • 发数HTTP响应
      • 发送Status
        • 如200 / 400 等
          • ctx.status = 204
      • 发送Body
        • 如{name:’jon’}
          • ctx.body = { name: "jon" };
      • 发送Header
        • 如Allow, Content-Type
          • ctx.set(‘Allow’, ‘GET, POST’)
    • 最佳实践
      • 每个资源可控制器放在不同的文件里
        • 批量挂载路由
          • src/routes/index.js 批量挂载路由

            const fs = require("fs");  
                            
            module.exports = (app) => {  
              fs.readdirSync(__dirname).forEach((file) => {  
                if (file === "index.js") {  
                  return;  
                }  
                const route = require(`./${file}`);  
                app.use(route.routes()).use(route.allowedMethods());  
              });  
            };  
            

            src/index.js

            const routing = require("./routes");  
            routing(app);  
            
        • 将路由单独放在一个目录
          •   src  
              --routes  
              ----index.js  
              ----home.js  
              ----users.js  
            
          • routes/users.js

            const Router = require("koa-router");  
            const router = new Router({ prefix: "/users" });  
            const {find, delete: del } = require("../controllers/users");  
                            
            router.get("/", find);  
            router.delete("/:id", del);  
            //...  
                            
            module.exports = router;  
            
        • 将控制器单独放在一个目录
          •   src  
              --controlles  
              ----home.js  
              ----users.js  
            
      • 尽量使用类+类方法的形式编写控制器
        • controlles/users.js
          class UsersCtl {  
            find(ctx) {  
              ctx.body = db;  
            }  
            delete(ctx) {  
              db.splice(ctx.params.id * 1);  
              ctx.status = 204;  
            }  
            //...  
          }  
                      
          module.exports = new UsersCtl();  
          
      • 严谨的错误处理
        • 运行时错误,都返回 500
        • 逻辑错误
          • 找不到 404
          • 先决条件失败 412
            • ctx.throw(412)
          • 无法处理的实体(参数格式不对)422
            • npm i koa-parameter -S
            • src/index.js
              const parameter = require("koa-parameter");  
              app.use(parameter(app));  
              
              controlles/users.js
              create(ctx){  
                // 检验请求 (name 为string 必选)  
                // 如不满足自动返回 422  
                ctx.verifyParams({  
                  name: {type: ‘string’, required: true}  
                })  
              }  
              
        • 手写错误处理中间件
          • src/index.js 确保挂载在第一个中间件

            app.use(async (ctx, next) => {  
              try {  
                await next();  
              } catch (err) {  
                ctx.status = err.status || err.statusCode || 500;  
                ctx.body = { message: err.message };  
              }  
            });  
            
              • 能够捕获ctx.throw()
                • 对于500, 需增加短路条件
                • 对于404, 中间件无法捕获
        • koa-json-error
          • npm i koa-json-error -S
          • src/index.js
            const error = require("koa-json-error");  
            app.use(  
              error({  
                postFormat: (err, { stack, ...rest }) =>  
                  process.env.NODE_ENV === "production" ? rest : { stack, ...rest },  
              })  
            );  
            

    三 登录认证

    3.1 session

    • 1 客户端发送用户名密码给服务端
      2 服务端登录,生成sessionID保存到redis中,并返回给客户端存入cookie,设置httpOnly 和 expires
      3 因客户端每次请求都会带上cookie,所以之后的每次请求sessionID 也会跟到服务端
      4 服务端验证 sessionID,以此判断是否登录

    3.2 JWT

    • JSON Web Token
      • 定义了一种紧凑且独立的方式,可将各方之间的信息作为 JSON对象进行安全传输
      • 该信息可验证和信任,因为是经过数字签名的
    • 构成
      • 头部 Header
        • typ: token的类型,这里固定为JWT
        • alg: 使用的hash算法
      • 有效载荷 Payload
        • 存储需要传递的信息,如用户ID、用户名
        • 还包含元数据,如过期时间,发布人等
        • 与Header不同,Payload可加密
      • 签名 Signature
        • 对Header和Payload部分进行签名
        • 保证Tonken在传输工程中没有被篡改或损坏
    • 工作原理
      • 1 客户端发送用户名密码给服务端
        2 服务端登录,生成Token,并返回给客户端存入 localStorage 或 sessionStorage
        3 之后的每次将Token加入请求头中的Authorization:Bearer
        4 服务端验证 Token,以此判断是否登录

    3.3 session 对比 JWT

    • 可拓展性
      • JWT多服务器更好,无需redis
    • 安全性
      • 都会受的攻击,不要把敏感数据方式cookie后JWT中
    • RESTful API
      • REST要求程序无状态,session违反了此要求
    • 性能
      • JWT每次传输量更大
      • session需要查询数据库
    • 时效性
      • JWT无法实时更新,需等过期

    3.4 在node中使用JWT

    • npm i jsonwebtoken -S
      启动node
      node
      设置环境变量
      jwt = require(‘jsonwebtoken’);
      然后签名
      token = jwt.sign({name:’jon’}, ‘secret’);
      解码
      jwt.decode(token);
      验证是否被窜改
      jwt.verify(token, 'secret');

    起源地下载网 » Node RESTful

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元