在大概1年前接触了typescript以后, 日渐被它所吸引. 甚至一个简单的本地测试文件node ./test.js
有时也会切到ts-node ./test.ts
. 在一样的时间节点以前, 仍是会不时地去学学node, mongodb相关的. 但是, 因为懒(需)惰(求), 在好久没碰以后, 不少知识点都忘了!😴node
综上, 因而就有了今天这个话题:react
论 如何在工做时间之余完成本身的我的项目并实现按时上床睡觉webpack
答案是: 不存在的😅ios
项目会不断维护. 不管是client端仍是server端, 都只提供简单的模板式的功能.git
client ts-react-webpackgithub
server showcaseweb
线上体验mongodb
typescript是两端的基调typescript
详看express
centos上mongodb的官网安装教程, 其余系统请自行查阅.
须要讲一下我为何选了nestjs:
nestjs
将typeScript
引入并基于express
封装. 意味着, 它与绝大部分express
插件的兼容性都很好.
nestjs
的核心概念是提供一种体系结构, 它帮助开发人员实现层的最大分离, 并在应用程序中增长抽象.
此外, 它对测试是很是友好的...
也须要声明的是, nestjs
的依赖注入特性是受到了angular
框架的启发, 相信作angular
开发的对整个程序体系会更容易看懂.
简单介绍下几个主流程模块
我是用nest-cli工具初始化项目的, 一切从src/main.ts
开始
import { NestFactory } from '@nestjs/core'
import * as dotenv from 'dotenv'
import { DOTENV_PATH } from 'config'
// 优先执行, 避免引用项目模块时获取环境变量失败
dotenv.config({ path: DOTENV_PATH })
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
// 支持跨域
app.enableCors()
await app.listen(9999)
}
bootstrap()
复制代码
一样地, 咱们能够提供一个express
实例到NestFactory.create
:
const server = express();
const app = await NestFactory.create(ApplicationModule, server);
复制代码
这样咱们就能够彻底控制express
实例生命周期, 好比官方FAQ中说到的建立几个同时运行的服务器
在我本地开发的时候, 根目录上还有一个.dev.env
, 这是未提交到github的, 由于里面包含了我我的的mongodb
远程ip
地址 其余内容与github上的.env
一致, 由于我本地并不想再安装一遍mongodb
, 若是是想把项目拉下来就跑起来的, 不管如何你都须要一个mongodb
服务, 固然你是能够本地安装就行了.
还须要说起到一点就是调试:
之前在vscode上调试node程序都须要在调试栏新增配置, 而后利用该配置去跑起应用才能实现断点调试, 新版的vscode支持autoAttach功能, 使用Command
+ Shift
+ P
唤起设置功能面板
启动它!
这样, 在项目的.vscode/setting.json
里面会多了一个选项: "debug.node.autoAttach": "on"
, 在咱们的启动script里面加上--inspect-brk
就能够实现vscode的断点调试了. 对应地, npm run start:debug
是个人启动项, 可参考nodemon.debug.json
import { Module } from '@nestjs/common'
import { MongooseModule } from '@nestjs/mongoose'
import { DB_CONN } from 'config/db'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import modules from 'routers'
@Module({
imports: [
MongooseModule.forRoot(DB_CONN, {
useNewUrlParser: true,
}),
...modules,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
复制代码
每一个 Nest 应用程序至少有一个模块, 即根模块. 根模块是 Nest 开始安排应用程序树的地方. 事实上, 根模块多是应用程序中惟一的模块, 特别是当应用程序很小时, 可是对于大型程序来讲这是没有意义的. 在大多数状况下, 您将拥有多个模块, 每一个模块都有一组紧密相关的功能. 固然, 模块间也能够共享.
概念 | 解释 |
---|---|
providers | 由Nest 注入器实例化的提供者,而且能够至少在整个模块中共享 |
controllers | 必须建立的一组控制器 |
imports | 导入模块所需的导入模块列表 |
exports | 此模块提供的提供者的子集, 并应在其余模块中使用 |
AppController
在这个程序当中只是为了测试能返回Hello World!!!
, 其实它不是必须的, 咱们能够把它直接干掉, 把所有接口, 所有逻辑放到各个module
中实现, 以modules/user
为例, 接着往下看.
目录结构
user
├── dto -------------- 数据传输对象
├── index.ts --------- UserModule, 概念同AppModule
├── controller.ts ---- 传统意义的控制器, `Nest`会将控制器映射到相应的路由
├── interface.ts ----- 类型声明
├── schema.ts -------- mongoose schema
├── service.ts ------- 处理逻辑
复制代码
有必要讲讲controller.ts
和service.ts
, 这是nestjs的概念中很重要的部分
import { Get, Post, Body, Controller } from '@nestjs/common'
import UserService from './service'
import CreateDto from './dto/create.dto'
@Controller('user')
export default class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll()
}
@Post('create')
create(@Body() req: CreateDto) {
return this.userService.create(req)
}
}
复制代码
装饰器路由为每一个路由声明了前缀,因此Nest
会在这里映射每一个/user
的请求
@Get()
装饰器告诉Nest
建立此路由路径的端点
一样地, @Post()
也是如此, 而且这类Method装饰器接收一个path
参数, 如@Post('create')
, 那么咱们就能够实现post到路径/user/create
到此, 日后的逻辑交给service
实现
import { Injectable } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'
import logger from 'utils/logger'
import { cryptData } from 'utils/common'
import ServiceExt from 'utils/serviceExt'
import { IUser } from './interface'
import CreateDto from './dto/create.dto'
@Injectable()
export default class UserService extends ServiceExt {
constructor(@InjectModel('User') private readonly userModel: Model<IUser>) {
super()
}
async create(createDto: CreateDto) {
if (!createDto || !createDto.account || !createDto.password) {
logger.error(createDto)
return this.createResData(null, '参数错误!', 1)
}
const isUserExist = await this.isDocumentExist(this.userModel, {
account: createDto.account,
})
if (isUserExist) {
return this.createResData(null, '用户已存在!', 1)
}
const createdUser = new this.userModel({
...createDto,
password: cryptData(createDto.password),
})
const user = await createdUser.save()
return this.createResData(user)
}
async findUserByAccount(account: string) {
const user = await this.userModel.findOne({ account })
return user
}
async findAll() {
const users = await this.userModel.find({})
return this.createResData(users)
}
}
复制代码
至此, 咱们运行npm run start:dev
启动一下服务:
直接在浏览器端访问http://localhost:9999/#/
没错, 的确失败了!!! 由于咱们使用了jsonwebtoken
, 在modules/auth
能够看到它的实现.
如今咱们在postman
中登陆了再试试吧!
bingo!!!
(若是是想拉下来跑的话, 也能够照着schema
的格式用postman
先伪造条用户数据, 把系统打通!!!)
关于client端的实现我不会细讲, 能够看项目github, 和我以前的文章(typescript-react-webpack4 起手与踩坑), 项目结构会有改动.
讲一下接入了真实服务器以后http请求对于token的一些处理, 查看http.ts
首先是建立axios
实例时须要在header
处把token带上
const axiosConfig: AxiosRequestConfig = {
method: v,
url,
baseURL: baseUrl || DEFAULTCONFIG.baseURL,
headers: { Authorization: `Bearer ${getCookie(COOKIE_KEYS.TOKEN)}` }
}
const instance = axios.create(DEFAULTCONFIG)
复制代码
token也能够存放在localStorage
另一点是, 对应服务端返回的token错误处理
const TOKENERROR = [401, 402, 403]
let authTimer: number = null
...
if (TOKENERROR.includes(error.response.status)) {
message.destroy()
message.error('用户认证失败! 请登陆重试...')
window.clearTimeout(authTimer)
authTimer = window.setTimeout(() => {
location.replace('/#/login')
}, 300)
return
}
复制代码
两端项目都是简单的模板项目, 不存在什么繁杂的业务, 属于比较初级的学习实践. 对nestjs
的掌握程度有限, 只是拿来练练手. 可能后续会基于这篇文章继续深刻地去讲讲, 好比部署之类的, 两个项目也会不断去维护. 后续也有计划会合二为一. 看时间吧!