TypeScript实战篇 - TS实战: 服务层开发-令牌

news2024/11/14 20:35:58

目录

接口说明

服务设计

WHY NOT Socket?

@huatian/svc【node.js 接口服务端】

huatian-svc/package.json

huatian-svc/tsconfig.json

huatian-svc/src/main.ts

huatian-svc/nodemon.json

huatian-svc/src/context/AccountContext.ts

huatian-svc/src/repo/UserRepository.ts

huatian-svc/src/dao/Token.ts

@huatian/model 模板子项目

huatian-model/src/index.ts 索引文件

huatian-model/package.json


接口说明

  • POST /token 登录接口{uname,pwd}
  • POST /send 发消息接口
  • GET /read 读取未读消息的接口

服务设计

大型聊天平台

WHY NOT Socket?

【Socket主打体验,我们可以用它做收发数据,其他的操作不建议用Socket】

  • Socket有状态【内存消耗更多】
    • 需要保持连接
    • 服务宕机不好恢复
  • 目前的设计可以支持更高的并发

@huatian/svc【node.js 接口服务端】

huatian-svc/package.json

// 开发前先添加一些工具
// cd huatian-svc
// npm add @types/node // node方法
// npm add express // 外部服务器
// npm add sequelize // 存数据用的
// npm add sqlite3 // 开发环境,用一个文件就可以创造数据库了
// npm add typescript // ts
// npm add @types/express // express 的类型
// npm add nodemon // 做服务的启动和刷新功能
{
  "name": "@huatian/svc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon src/main.ts" // npm run dev
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/express": "^4.17.13",
    "@types/node": "^16.11.7",
    "cookie-parser": "^1.4.5",
    "express": "^4.17.1",
    "nodemon": "^2.0.15",
    "sequelize": "^6.9.0",
    "sqlite3": "^5.0.2",
    "typescript": "^4.4.4"
  }
}

huatian-svc/tsconfig.json

// ts配置文件
{
  "compilerOptions": {
    "target": "ES6",
    "lib": [
      "DOM",
      "ESNext"
    ],
    "moduleResolution": "node",
    "esModuleInterop": true// 引用一些文件时,不用写如: import * as express from "express";
  },
  "include": [
    "src/**/*.ts"
  ]
}

huatian-svc/src/main.ts

import express, {
  NextFunction,
  Request,
  Response
} from "express";
// 安装cookie-parser: npm add cookie-parser
import cookieParser from "cookie-parser";
import { AccountContext } from "./context/AccountContext";
import { Token } from "./dao/Token";
// 创建一个服务
const app = express()
// cookie功能,整理好cookie,req.cookies
app.use(cookieParser())
async function sendStdResponse<T>(res: Response, f: T)
async function sendStdResponse(res: Response, f: Promise<any>)
async function sendStdResponse(res: Response, f: () => Promise<any>)
async function sendStdResponse(res: Response, f: any) {
  try {
    let data = typeof f === 'function' ? f() : f
    if (data instanceof Promise) {
      data = await data
    }
    res.send({
      success: true,
      data
    })
  } catch (ex: any) {
    console.error(ex)
    res.status(500).send({
      success: false,
      message: ex.toString()
    })
  }
}

// token
async function token(
  req: Request & { uid: number },// req 是带着uid的
  res: Response,
  next: NextFunction
) {
  // tokenHash~=sessionid
  const tokenHash = req.cookies["x-token"] as string
  const token = Token.getInstance()
  const tokenObject = token.getToken(tokenHash)
  if (tokenObject === null) {
    res.status(401).send({
      success: false
    })
    return
  }
  req.uid = tokenObject.uid
  next()
}

// 请求已登录用户uid
app.get('/foo', token, (req: Request & { uid: number }, res) => {
  res.send(req.uid + '-ok')
})

// 登录接口,json传参【express.json()解析json】
app.post('/token', express.json(), async (req, res) => {
  const { uname, pwd } = req.body
  const account = AccountContext.getInstance()
  const user = await account.veritfy(uname, pwd)
  console.log(uname, pwd, user.getId())
  const token = Token.getInstance()
  // 刷新token
  const tokenObject = token.refreshToken(user.getId())
  res.cookie("x-token", tokenObject.token)
  sendStdResponse(res, "ok")
})

// 监听一个6001端口号的服务
app.listen(6001, () => {
  console.log('listen at 6001')
})

huatian-svc/nodemon.json

{
  // node 监控器配置
  "restartable": "rs", // 重启
  "watch": [ // 监听改变
    "src",
    "../huatian-model//src"
  ],
  "execMap": {
    "ts": "ts-node", // 执行文件的配置
    "js": "node"
  },
  "verbose": true // 详细报错信息
}

huatian-svc/src/context/AccountContext.ts

// 账户场景
import { UserRepository } from "../repo/UserRepository"

export class AccountContext {
  // static 静态成员
  private static inst: AccountContext
  private repo: UserRepository = UserRepository.getInstance()
  public static getInstance() {
    // 单例场景
    if (!AccountContext.inst) {
      AccountContext.inst = new AccountContext()
    }
    return AccountContext.inst
  }
  // 异步方法,用户名,密码查是否有这个用户
  public async veritfy(uname: string, passwd: string) {
    const user = this.repo.getUser(uname, passwd)
    return user
  }
}

huatian-svc/src/repo/UserRepository.ts

// 用户信息仓库
import { User } from "@huatian/model";
export class UserRepository {
  // 本地信息存储,用户信息不会高频变化,尤其是id这样的信息
  private users: Record<number, User> = {}
  // 做个单例,js单线程
  public static inst = new UserRepository()
  public static getInstance() {
    return UserRepository.inst
  }
  // 需要去huatian-model: npm link
  // 回到huatian-svc: npm link @huatian/model
  // 函数重载
  public getUser(uid: number): User
  public getUser(user: string, passwd: string): User
  public getUser(identity: number | string, passwd?: string): User {
    // 做个窄化
    if (typeof identity === 'number') {
      const uid = identity
      // 拿一下缓存
      if (this.users[uid]) {
        return this.users[uid]
      }
      // 没有就生成一个,做一下缓存
      const newUser = new User(uid)
      this.users[uid] = newUser
      return newUser
    } else {
      // 写死的用户名,密码
      const user = identity
      const idmap = {
        "zhangsan": 1,
        "lisi": 2,
        "wangwu": 3
      }
      // 递归调用自己,把他变成整数型的调用
      return this.getUser(idmap[user] || 1)
    }
  }
}

huatian-svc/src/dao/Token.ts

// dao【data Access object】
// hash 摘要算法
import crypto from "crypto";
type TokenObject = {
  uid: number,
  token: string,
  expires: number
}
export class Token {
  static inst: Token = new Token()
  static getInstance() {
    return Token.inst
  }
  // 缓存
  private cache: Record<string, TokenObject> = {}
  private create(uid: number) {
    const token = Math.random() + "-" + new Date().getTime()
    // token一天后过期
    const expires = new Date().getTime() + 3600 * 24
    const sha = crypto.createHash("sha1")
    sha.update(token)
    const hash = sha.digest('hex')// 16进制串
    const tokenObject = {
      uid,
      token: hash,
      expires
    }
    // 缓存一下新创建的token
    this.catchSet(tokenObject.token, tokenObject)
    return tokenObject
  }
  // 缓存token
  private catchSet(hash: string, token: TokenObject) {
    this.cache[hash] = token
  }
  private catchGet(hash: string) {
    return this.cache[hash] || null
  }
  public getToken(hash: string) {
    const token = this.catchGet(hash)
    // token 不存在
    if (!token) {
      return null
    }
    // token有效期内
    if (token.expires > new Date().getTime()) {
      return token
    }
    // 过期了
    return null
  }
  public refreshToken(uid: number) {
    return this.create(uid)
  }
}

@huatian/model 模板子项目

huatian-model/src/index.ts 索引文件

// @huatian/model 索引文件
export * from "./User"
export * from "./Message"
export * from "./ChatSession"
export * from "./UserChat"

huatian-model/package.json

{
  "name": "@huatian/model",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.ts", // 入口索引文件
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试面试真题 | 什么是PO设计模式?

面试官问&#xff1a;UI自动化测试中有使用过设计模式吗&#xff1f;了解什么是PO设计模式吗&#xff1f; 考察点 《page object 设计模式》&#xff1a;PageObject设计模式的设计思想、设计原则 《web自动化测试实战》&#xff1a;结合PageObject在真实项目中的实践与应用情…

活动目录密码更改

定期更改密码是一种健康的习惯&#xff0c;因为它有助于阻止使用被盗凭据的网络攻击&#xff0c;安全专家建议管理员应确保用户使用有效的密码过期策略更改其密码。 管理员可以通过电子邮件通知用户在密码即将过期时更改其密码&#xff0c;但在许多组织中&#xff0c;用户只能…

举个栗子!Tableau 技巧(256):灵活折叠文本表的多级数据行

通常&#xff0c;Tableau 默认的图表分层结构是统一打开或关上&#xff0c;有什么办法可以按需选择展开或折叠&#xff1f;如下示例&#xff1a;单击“”展开层级&#xff0c;单击“-“收起层级。 可以试试集操作&#xff01;今天的栗子&#xff0c;就来分享具体实现方法吧~ 本…

解读Spring的context:property-placeholder

在spring中&#xff0c;如果要给程序定义一些参数&#xff0c;可以放在application.properties中&#xff0c;通过<context:property-placeholder>加载这个属性文件&#xff0c;然后就可以通过value给我们的变量自动赋值&#xff0c;如果你们的程序可能运行在多个环境中&…

什么是头脑风暴法,有哪些原则?

1. 什么是头脑风暴法&#xff1f; 头脑风暴法&#xff08;Brainstorming&#xff09;是一种用于创造性思维和问题解决的方法。它旨在通过集体讨论和思维碰撞&#xff0c;激发团队成员的创造力和想象力&#xff0c;从而产生新的创意和解决方案。 在头脑风暴会议中&#xff…

数据结构 10-排序4 统计工龄 桶排序/计数排序(C语言)

给定公司名员工的工龄&#xff0c;要求按工龄增序输出每个工龄段有多少员工。 输入格式: 输入首先给出正整数&#xff08;≤&#xff09;&#xff0c;即员工总人数&#xff1b;随后给出个整数&#xff0c;即每个员工的工龄&#xff0c;范围在[0, 50]。 输出格式: 按工龄的递…

【福建事业单位-推理判断】01图形推理(位置,样式、属性、特殊)

【福建事业单位-推理判断】01图形推理 一、位置规律&#xff08;&#xff08;元素组成相同&#xff09;&#xff09;1.1平移旋转翻转1.1.1先判定方向&#xff0c;再确定路径1.1.2分内外圈走 1.2 旋转1.3翻转左右翻只有左右变&#xff0c;上下翻只有上下变&#xff0c;旋转180全…

真的不想知道如何进行语音翻译才简单吗

郑希&#xff1a;嘿&#xff0c;王浩&#xff01;我听说你最近去了日本旅游&#xff0c;怎么样&#xff1f;体验如何&#xff1f; 王浩&#xff1a;哈哈&#xff0c;太棒了&#xff01;日本真是一个充满魅力的国家。不过&#xff0c;要说令我惊喜的还是语音翻译技术&#xff0…

大同市副市长孟维君赴大同互联网职业技术学院指导建设工作

8月2日&#xff0c;大同市副市长孟维君一行莅临大同互联网职业技术学院&#xff08;以下简称&#xff1a;大同互联网学院&#xff09;&#xff0c;对学院的建设工作进行了重要指导。孟维君副市长深入施工现场&#xff0c;详细了解了项目施工的进展情况&#xff0c;并提出三点重…

【LeetCode-简单】剑指 Offer 18. 删除链表的节点(详解)

题目 定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。 返回删除后的链表的头节点。 注意&#xff1a;此题对比原题有改动 题目地址&#xff1a;剑指 Offer 18. 删除链表的节点 - 力扣&#xff08;LeetCode&#xff09; 方法 删除一个节点…

初始创建一个apex应用程序

在应用程序构建器 点击创建选择新建应用程序你只需要填写你应用程序的名称然后点击创建应用程序 在工作区页面&#xff0c;点开上方SQL工作室&#xff0c;点击对象浏览器然后你可以在右上角看到一个加号&#xff0c;点开&#xff0c;选“表”起好表名&#xff0c;并写好你需要的…

CPU缓存那些事儿

CPU缓存那些事儿 CPU高速缓存集成于CPU的内部&#xff0c;其是CPU可以高效运行的成分之一&#xff0c;本文围绕下面三个话题来讲解CPU缓存的作用&#xff1a; 为什么需要高速缓存&#xff1f;高速缓存的内部结构是怎样的&#xff1f;如何利用好cache&#xff0c;优化代码执行…

Nacos 抽取公共配置

文章目录 创建一个公共配置文件其他配置文件引用springboot配置文件 创建一个公共配置文件 其他配置文件引用 ${变量} springboot配置文件 spring:cloud:nacos:discovery:server-addr: current.ip:8848namespace: word_register_proconfig:server-addr: current.ip:8848auto-r…

【Valgrind】如何使用Valgrind监控内存

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

FIFO不常规的应用

一、fifo的复位后的满标志位状态 IP中设置方法 1、Block Ram和Distributed Ram 通过设置上图中的值为1&#xff08;默认&#xff09;或者为0&#xff0c;设置full相关信号在复位后的状态。从下表中可以看出对应关系。 2、Bulitin FIFO 该类型下&#xff0c;读写FIFO的位宽一…

【程序崩溃的原因及处理方法】

程序的编写时&#xff0c;可能经常会遇到程序崩溃的现象。一般来说&#xff0c;程序崩溃是由于内存操作不当引发的。但是具体来讲&#xff0c;由哪些原因可以导致程序崩溃呢&#xff1f;以及当程序崩溃时该如何找到错误的位置呢&#xff1f; 1.资源不足&#xff1a;当应用程序…

应用案例|基于3D视觉的高反光金属管件识别系统解决方案

Part.1 项目背景 在现代制造业中&#xff0c;高反光金属管件的生产以及质量的把控是一个重要的挑战。传统的2D视觉系统常常难以准确地检测和识别高反光金属管件&#xff0c;因为它们的表面特征不够明显&#xff0c;容易受到光照和阴影的干扰。为了应对这个问题&#xff0c;基于…

TWS真无线蓝牙耳机哪家好?六款口碑好的TWS真无线蓝牙耳机分享

为了帮助大家在这个充满选择的世界中找到最理想的蓝牙耳机&#xff0c;我们特别为您精心挑选了几款备受赞誉的产品&#xff0c;它们在音质、舒适度、功能和性价比等方面都有出色的表现。在本文中&#xff0c;我们将深入探讨这些蓝牙耳机的特点和优势&#xff0c;帮助您更好地了…

python调用pytorch的clip模型时报错

使用python调用pytorch中的clip模型时报错&#xff1a;AttributeError: partially initialized module ‘clip’ has no attribute ‘load’ (most likely due to a circular import) 目录 现象解决方案一、查看项目中是否有为clip名的文件二、查看clip是否安装成功 现象 clip…

wpf画刷学习1

在这2篇博文有提到wpf画刷&#xff0c; https://blog.csdn.net/bcbobo21cn/article/details/109699703 https://blog.csdn.net/bcbobo21cn/article/details/107133703 下面单独学习一下画刷&#xff1b; wpf有五种画刷&#xff0c;也可以自定义画刷&#xff0c;画刷的基类都…