ExpressLearning

Init

npm init
npm i express
npm nodemon  //修改start

创建express对象,设置好路由和监听

const express = require('express');

const app = express(); //创建对象 express app下可以调用express的方法


//路由  当接收到http的get方法
app.get("/",(req,res) => {
  res.status(200).json({
      message:"hello from the back",
      app:"fatdove"
    })  //返回json对象
})

app.post("/",(req,res)=>{
  res.send("you can post to this endpoint...")
})


//方便监听
const port = 3000;
app.listen(port,()=>{
  console.log(`app running on port ${port}...`);
})

rest-API

  • separate API into logical resources

    resources means information
    
  • expose structured,resource-based URLs

  • use HTTP methods

    which is means we should use verbs in the endpoint of url
    like: xxx/tours(/1)
    the endpoint name can be same but the route can be different such as get or post 
    get-read
    post-create
    patch/put -update
    delete
    special:
    /getToursByUser->get /user/3/tours
    /deleteToursByUser->delete /user/3/tours/9
    
  • send data as json

    //Jsend
    {
    	"status":"success",
    	"data":{
    		"id":5,
    		"name":"fatdove"
    	}
    }
  • be stateless

    each requrst must contain all the information necessary to process a certain request.
    get tours/nextPage ❌
    get tour/page/6 ✅
    

json文件数据集

get

//__dirname是本地根文件  fs读取的文件需要转换成json格式 不然是二进制
const tours = JSON.parse(fs.readFileSync(`${__dirname}/data/tours.json`));
console.log(tours);

app.get("/api/v1/tours",(req,res)=>{
  res.status(200).json({
    status:"success",
    total:tours.length,  //用户让用户知道数组的长度 方便
    data:{
      tours:tours
    }
  })

})

传参(更改路由):

//__dirname是本地根文件  fs读取的文件需要转换成json格式 不然是二进制
const tours = JSON.parse(fs.readFileSync(`${__dirname}/data/tours.json`));
console.log(tours);
//参数可以多加 可选参数后面加?
app.get("/api/v1/tours/:id/:x/:y?",(req,res)=>{
	req.params //拿到传参
})


app.get("/api/v1/tours/:id",(req,res)=>{
	req.params //拿到传参
})

重构:把方法定义出来,把路由放在一起,提高可读性

const getAllTours = (req, res) => {
  res.status(200).json({
    status: "success",
    total: tours.length,  //用户让用户知道数组的长度 方便
    data: {
      tours: tours
    }
  })
}

app.get("/api/v1/tours",getAllTours )

post

app.post("/api/v1/tours", (req, res) => {
  console.log(req.body); //look request data
  const newId = tours[tours.length - 1].id + 1;
  const newTour = Object.assign({ id: newId }, req.body);
  tours.push(newTour);
  //想要立即读取就是用writeFile 不然postman无法读取writefileSync
  fs.writeFile(`${__dirname}/data/tours.json`, JSON.stringify(tours), (err) => {
    //201 means created
    res.status(201).json({
      status: "success",
      data: {
        tours: newTour
      }
    })
  })
  console.log(newTour);
  res.send("done")
})

refactor router

不是链式反应,只是把路由相同的不同方法整合在一起

app.route("/api/v1/tours").get(getAllTours).post(createTour);

middleware

❗❗express根据定义顺序执行

定义中间件

自定义必须使用next不然会堵塞,express.json()应该是已经封装好了的

funciton MiddleWare = (req,res,next)=>{

}

注册中间件

app.use()
app.use((req,res,next)=>{
  req.requestTime = new Date().toISOString();
  next();
})

const getAllTours = (req, res) => {
  console.log(req.requestTime);
  res.status(200).json({
    status: "success",
    total: tours.length,  //用户让用户知道数组的长度 方便
    data: {
      tours: tours
    }
  })
}

app.route("/api/v1/tours").get(getAllTours).post(createTour);

//log  2023-10-23T12:00:12.105Z

morgan

日志中间件

npm i morgan
app.use(morgan('dev')) 


//after requesting output :GET /api/v1/tours 200 23.548 ms - 275

express.Router()整理route(mounting)

const tourRouter = express.Router();  //新建Router对象
//声明中间件
app.use("/api/v1/tours",tourRouter)  //整合路径一致的所有路由
tourRouter.route("/").get(getAllTours).post(createTour);  //这里route的/就是/api/v1/tours
tourRouter.route("/:id").get(getTour) 

file structure

app.js用来存放middleware

const express = require('express');
const fs = require('fs');
const morgan = require('morgan');
const tourRouter = require('./routes//tourRoutes')
const userRouter = require('./routes//userRoutes')
const app = express(); //创建对象 express app下可以调用express的方法

///////////middleware
//middleware is a function which is to modify request data
app.use(morgan('dev'))
app.use(express.json());
app.use((req,res,next)=>{
  console.log("this is middleware");
  next();
})

app.use((req,res,next)=>{
  req.requestTime = new Date().toISOString();
  next();
})




//////////route
app.use("/api/v1/tours",tourRouter)
app.use("/api/v1/tours",userRouter)

module.exports = app;

routes/tourRoutes

const express = require('express');
const tourController = require("../controllers/tourController.js")
const router = express.Router();




router
  .route("/")
  .get(tourController.getAllTours)
  .post(tourController.createTour);


module.exports = router;

controllers/tourControllers

const fs = require('fs');

const tours = JSON.parse(fs.readFileSync(`${__dirname}/../data/tours.json`));

exports.getAllTours = (req, res) => {
  console.log(req.requestTime);
  res.status(200).json({
    status: "success",
    total: tours.length,  //用户让用户知道数组的长度 方便
    data: {
      tours: tours
    }
  })
}

exports.createTour = (req, res) => {
  const newId = tours[tours.length - 1].id + 1;
  const newTour = Object.assign({ id: newId }, req.body);
  tours.push(newTour);
  fs.writeFile(`${__dirname}/data/tours.json`, JSON.stringify(tours), (err) => {
    //201 means created
    res.status(201).json({
      status: "success",
      data: {
        tours: newTour
      }
    })
  })
}

server.js

/////////start the server
const app = require('./app.js')
//方便监听
const port = 3000;
app.listen(port, () => {
  console.log(`app running on port ${port}...`);
})

发送http请求,中间件(app.js),路由参数中间件(router),路由返回

params middleware

一般定义在指定的route文件中,val就是路由参数中id的值

router.param("id",(req,res,next,val)=>{
  console.log("tour id is ",val);
  next()
})

用法:

//检查参数
exports.checkId = (req,res,next,value)=>{
  if(value>10){
    return res.status(404).json({
      status:"fail",
      message:"invalid id"
    })
  }
  next();  //可加可不加 因为在参数中间件中已经返回
}


router.param("id",tourController.checkId)

post请求,路由中不带参数,我们需要检查res.body的参数,那么我们需要如下这么写中间件

router
  .route("/")
  .get(tourController.getAllTours)
 .post(middleware,tourController.createTour)
qie
//检查创建的参数正确性 req.body
exports.bodyCheck = (req, res, next) => {
  console.log("检查body");
  if (!req.body.name) {
    return res.status(400).json({
      status: "fail",
      message: "missing name"
    })
  }
  next();
}

static file

定义根节点

app.use(express.static(`${__dirname}/public`));

这样我们直接在网站输入:

http://127.0.0.1:3000/test.html

就能打开网页,不需要输入/public...

environment variables

开发环境和测试环境的切换,需要用到环境变量

console.log(app.get("env"))   //development

设置环境变量

npm i dotenv

config.env:

NODE_ENV=development
PORT=8000
USER=FATDOVE
PASSWORD=12345

server.js: 读取config.env文件然后赋值给环境变量

const dotenv = require('dotenv');
dotenv.config({
  path:"./config.env"
})


console.log(app.get("env")); //打印出我们设置的环境变量

测试的时候,指定测试环境的中间件:

if(process.env.NODE_ENV === 'production'){
  app.use(morgan('dev'))
}
const port = process.env.port||3000;  //process.env.port存在就返回,不然返回3000

package定义 通过这种方式启动两种不同的开发环境,但是config.env也会起作用

"scripts": {
    "start:dev": "nodemon server.js",
    "start:prod": "NODE_ENV=production nodemon server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
 npm run start:dev

MongoDB

基本shell指令

https://dotblogs.com.tw/grayyin/2020/06/01/133724

mongo 启动数据库
use dbname 切换到对应数据库 
db.tours.insertOne([bson])  给collection添加一个元素,这里直接就相当于创建了集合
db.tours.find()  查看集合下面的所有文件

db.tours.find({name:"zjx"})  //查找包含name=zjx
db.tours.find()  //get all
db.tours.find({age:{$lt:50}})   //小于50  lte小于等于 gt大于
db.tours.find({$or:[{age:{lt:50}},{name:"zjx"}]})  //或
db.tours.find({$or:[{age:{lt:50}},{price:{$lt:500}}]{name:"zjx"}})  //内部只写范围  外部限定name  

db.tours.insertOne({name:"zjx"})

db.tours.insertMany([{name:"zjx"},{name:"fatdove"}])

db.tours.updateOne({name :"fatdove"},{$set:{age:22} }) 先查找出来,再进行修改$set


db.tours.replaceOne  //替换

 db.tours.deleteMany({age:{$gte:23}})
 db.tours.deleteMany({})  //delete all

atlas

关闭防火墙

mongod.exe

复制connect string

cluster0.drvhgpe.mongodb.net

what is mongoose

https://mongoosejs.com/docs/queries.html

schema

https://blog.csdn.net/qappleh/article/details/95097931

在 Mongoose 中,所有数据都由一个 Schema 开始创建。每一个 schema 都映射到一个 Mongodb 的集合(collection),并定义了该集合(collection)中的文档(document)的形式。

image-20240203194330281

mvc

image-20240203203156953

image-20240203203819113

express 连接mongo

[切记运动mongod.exe]

config.env粘贴connection string

DATABASE=mongodb+srv://fatdove:zjx412523@cluster0.drvhgpe.mongodb.net/fatdove[数据库名]


//TOTAL
NODE_ENV=development
PORT=8900
DATABASE=mongodb+srv://fatdove:zjx412523@cluster0.drvhgpe.mongodb.net/fatdove?retryWrite=true
//DATABASE_LOCAL=mongodb://localhost:27017/fatdove\
DATABASE_PASSWORD=zjx412523

下载mongodb连接器(driver)

 npm i mongoose@5

server.js

/////////start the server
const mongoose = require('mongoose')
const dotenv = require('dotenv');
dotenv.config({
  path: "./config.env"
})

//env要在app前读取 不然app.js中无法使用环境变量
const app = require('./app.js')

//mongodb
const DB = process.env.DATABASE.replace('<password>',process.env.DATABASE_PASSWORD)
//connect to mongo
mongoose.connect(DB,{
  useNewUrlParser:true,
  useCreateIndex:true,
  useFindAndModify:false,
  useUnifiedTopology: true
}).then(con=>{
  // console.log("DB connection successful!");
}).catch(err=>{
  console.log(err);
})

//方便监听
const port = process.env.port || 3000;
app.listen(port, () => {
  console.log(`app running on port ${port}...`);
})

route->controller<->model

/////////start the server
const mongoose = require('mongoose')

//创建schema
const userSchema = new mongoose.Schema({
  name:{
    type:String,
    required:[true,"A user must have a name"],
    unique:true
  },
  major:String,
  passWard:String,
  // createTime:{
  //   type:Date,
  //   default:Date.now()
  // }
})

//创建model
const User = mongoose.model('User',userSchema)

module.exports = User