从零开始学Express,理解服务器,路由于中间件

当我们初学前端时,常常只关注页面效果和交互,但随着项目复杂度提升,我们迟早会遇到"服务端"的问题:如何让一个页面的数据是从数据库来的?怎么让不同的用户看到不同的内容?这时候,我们就需要一个后端框架来帮我们处理这些需求。而对于 JavaScript 开发者来说,Express 是入门服务端开发最友好的一把钥匙。

本文不会长篇大论地讲解底层原理,而是通过最直观、最简单的方式,带你快速理解:什么是服务器、路由、中间件,以及 Express 的核心用法。如果你刚刚接触 Node.js,希望用它写点后端的东西,这篇文章就是为你准备的。

1.Express的简单使用

express 是node中的服务器软件(封装的http模块),通过express可以快速的在node中搭建一个web服务器。

使用步骤

1.创建初始化项目

创建文件夹然后终端输入 npm init -y

2.安装express

npm install express

3.创建index.js 编写代码

javascript 复制代码
//引入express
const express = require('express')
//一切皆对象服务器在程序里面也是对象
//获取服务器的实例对象
const app = express()
/*
    如果希望服务器可以正常访问 则需要为服务器设置路由
        路由可以根据不同的请求方式和请求地址来处理用户请求
        app.METHOD(...)
        METHOD可以是get post
    中间件
        -在express中我们使用app.use定义一个中间件
            中间件和路由很像 用法很像
        通常情况下 我们访问的直接是路由 但是有时候想要加一个中间件
        但是路由不区分请求的方式 只看路径
        只要访问的路径包含它就可以访问到
        和路由的区别
            1.会匹配所有的请求
            2.路径设置父目录
*/
//中间件
//next是回调函数的第三个参数 调用函数后可以触发后续的中间件 
app.use('/',(req,res,next)=>{
    console.log('收到请求')
    // res.send('中间件给你的响应')
    next()//next不能在响应处理完毕之后调用
})
app.use('/',(req,res)=>{
    console.log('收到请求')
    res.send('中间件给你的响应2222')
    
})
//中间件用来干嘛 可以用来设置访问路由之前的权限 相当于访问路由之前设置一些(公关)处理
//http://localhost:3000 
//现在创建了一个路由 / (路由是接口)
//路由的回调函数执行的时候 会接受三个参数
//第一个 request 第二个response 
app.get('/hello',(req,res)=>{
    console.log('有人访问我了')
    //在路由中 应该做两件事
    //读取用户的请求(request)
    //req表示用户的请求信息 通过req可以获取用户的传递数据
    console.log(req.url)//用户请求的路由
    //根据用户的请求返回响应(response)
    //res表示服务器发送给客户端的响应信息
    //sendStatus()像客户端发送响应状态码
    // res.sendStatus(9999999)
    //status只是设置了状态码没有发送
    res.status(200)
    //send设置发送响应体
    res.send('这是我给你的东西 但其实没东西')
})
//启动服务器
//app.listen(端口号)用来启动服务器
//服务器就像一个房子就我们的计算机有非常多的软件
//怎么识别我们的服务器软件 
//端口楼里面的门牌号 3000端口号房间号 就是express服务你
//后面可以加一个回调函数 服务器启动后执行函数 现在可以通过3000端口访问服务器
//协议名://ip地址:端口号/路径
//http://localhost:3000/index.js localhost ===  127.0.0.1都是指本计算机
app.listen(3000,()=>{
    console.log('服务器已经启动')
})

这就是一个基础的使用,我们访问http://localhost:3000/hello就会返回给你send()里面的内容。状态码在自定义,定义接收请求的路由格式app.METHOD('路径',(req,res)=>{})req,res通过传递函数调用函数时候参数拿过来直接用,这里就是我们的请求信息和响应信息。

中间件就是当我们访问路由的时候,如果设置了中间件,就会被中间件拦截,这里就是可以给路由设置一些权限要求等。

2.nodemon工具以及中间件的作用

目前服务器代码修改后必须重启,希望有一种方式可以监视代码的修改。代码修改以后可以自动重启服务器 需要安装一个模块 nodemon(监视器)

使用方式

1.全局安装 npm install nodemon -g

终端输入nodemon 就可以了 修改代码可以自动重启

默认启动当前目录的index.js 如果不是index.js 加上名字 nodemon ./01_helloworld.js

2.在项目中安装 npm install nodemon -D 开发依赖

启动 npx nodemon npx执行node模块

当我们创建express实例之后,实际上express有非常多的功能的。但是我们需要在中间件之中进行配置。比如我们希望当我们get访问http://localhost:3000/index.html。

也就是服务器的代码希望可以被外部直接访问,我们就需要在中间件中配置静态资源目录。可以将页面html以及图片放进去这样可以从服务器获取到这些代码。

javascript 复制代码
const express = require('express')
const path = require('node:path')
//创建服务器实例
const app = express()
//use()中间件
//配置路由 
//设置这个中间件之后 浏览器访问时会自动去public目录下寻找是否有静态资源
app.use(express.static(path.resolve(__dirname,'./public')))
app.get('/',(req,res)=>{
    /*
    希望用户访问我的根目录 我可以返回一个网页
    */
    res.send('')
})
app.get('/login',(req,res)=>{
    //获取到用户名和密码
    //req.query表示查询字符串中的请求参数
    console.log(req.query)
    console.log(req.query.username)
    if(req.query.username==='奥巴马'&&req.query.password==='123123'){
    console.log('请求收到')
    res.send('登录成功')}else{
        res.send('登录失败')
    }
})
//启动服务器
app.listen(3000,()=>{
    console.log('服务器启动')
})

这样我们就可以直接从浏览器获取到服务器中的页面了。用express.static设置静态文件目录,然后把希望外部可以直接访问的资源放进去就可以了。就可以通过文件名直接访问了。

这里req和res就是请求和响应信息。

3.get请求的两种参数传递以及post请求

1.get请求

xml 复制代码
 <form action="/login" method="get">
        <div>
            用户名 <input type="text" name="username">
        </div>
        <div>
            密码 <input type="text" name="password">
        </div>
        <div>
            <input type="submit" value="登录">
        </div>
    </form>
javascript 复制代码
const express = require('express')
const path = require('node:path')
const app = express()
//配置一个静态资源的路径
//express很多功能都是中间件引入的
app.use(express.static(path.resolve(__dirname,'./public')))
//引入解析请求体的中间件
app.use(express.urlencoded())
///开头的路径 叫做绝对路径 /login实际上是 http://localhost/login
app.get('/login',(req,res)=>{
    if(req.query.username==='admin'&&req.query.password === '123123'){
        res.send('<h1>登录成功</h1>')
    }else{
        console.log('<h1>登录失败</h1>')
    }
})

当我们直接用get方法发送请求的时候,我们在输入框输入的数据会在req.quert中以对象的形式传递过来,而我们直接在路径上设置参数的话。

javascript 复制代码
//get请求发送参数第二种方式
///hello/:id 表示访问/hello/xxx时就会触发
//在路径中以冒号命名的部分我们称为param 在get请求它会被解析为请求参数
//params传递参数一般不会特别复杂 一般就是一个
app.get('/hello/:id',(req,res)=>{
    //约定由于配置
    //可以通过req.params获取参数
    console.log(req.params)
    //{ id: '123', age: '12', gender: '男' }
    res.send('<h1>这是hello路由</h1>')
})

就会存储到params中,而且这个参数是在地址栏里面填写的。两种方式本质是相同的,params按照顺序传递。

2.post请求

post请求需要请求体,也就是body。但是我们express默认是没有给我们添加这个功能的,我们可以用中间件添加这个功能。

javascript 复制代码
//引入解析请求体的中间件
app.use(express.urlencoded())
app.post('/login',(req,res)=>{
    console.log(req.query) //{}
    //默认情况下express不会自动解析请求体 需要中间件添加功能
    console.log(req.body)
    const username = req.body.username
    const password = req.body.password
    if(username==='admin'&&password==='123123'){
    res.send('<h1>登录成功</h1>')}
    else{
        res.send('登录失败')
    }
})

然后就可以接收post请求了。参数通过body拿取。

4.简单的登录案例

我们现在会写get和post路由了,那么我们就可以写简单的登录案例,比如当我们登录的时候,发送请求到这个路由,然后携带参数过来。我们提前写死一些账户然后进行对比,有的话就登录没有的话就返回登录失败。还有注册功能,当我们访问的注册路由的时候,携带参数过来,如果写死的账户里面没有,那么就用数组的方法把注册的参数数据推进去形成新用户。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>我是一个静态网页</h1>
    <hr>
    <h2>登录</h2>
    <form action="/register" method="post">
        <div>
            用户名 <input type="text" name="username">
        </div>
        <div>
            密码 <input type="password" name="password">
        </div>
        <div>
            确认密码 <input type="password" name="repwd" placeholder="确认密码">
        </div>
        <div>
            <input type="texx" name="nickname" placeholder="昵称">
        </div>
        <div>
            <input type="submit" value="登录">
        </div>
    </form>
</body>
</html>
javascript 复制代码
const express = require('express')
const path = require('node:path')
const app = express()
//创建一个数组存储用户信息
const USERS =[
    {
        username:'admin',
        password:'123123',
        nickname:'超级管理员'
    },
    {
        username:'孙悟空',
        password:'123123',
        nickname:'用户'
    }
]
app.use(express.static(path.resolve(__dirname,'./public')))
app.use(express.urlencoded())
app.get('/login',(req,res)=>{
    if(req.query.username==='admin'&&req.query.password === '123123'){
        res.send('<h1>登录成功</h1>')
    }else{
        res.send('登录失败')
        console.log('<h1>登录失败</h1>')
    }
})
app.get('/hello/:id',(req,res)=>{
    //约定由于配置
    //可以通过req.params获取参数
    console.log(req.params)
    //{ id: '123', age: '12', gender: '男' }
    res.send('<h1>这是hello路由</h1>')
})
app.post('/login',(req,res)=>{
    const username = req.body.username
    const password = req.body.password
    // USERS.map(user=>{
    //     if(user.username === username){
    //         if(user.password===password){
    //             res.send(登录成功 ${user.nickname})
    //             return
    //         }
    //     }
    // })
    // res.send('登录失败')
    const loginUser = USERS.find(item=>{
        return item.username === username && item.password===password
    })
    if(loginUser){
        res.send(登录成功 ${loginUser.nickname})
    }else{
        res.send('登录失败')
    }
    // if(username==='admin'&&password==='123123'){
    // res.send('<h1>登录成功</h1>')}
    // else{
    //     res.send('登录失败')
    // }
})
app.post('/register',(req,res)=>{
    //获取用户登录的数据
    console.log(req.body)
    const {username,password,repwd,nickname} = req.body
    //只验证用户名是否存在
    const user = USERS.find(item=>{
        return item.username === username || item.nickname === nickname
    })
    if(!user){
        //进入判断用户不存在
        USERS.push({
            username,
            password,
            nickname
        })
        res.send('恭喜你注册成功')
    }else{
        res.send('用户名存在')
    }
    
})
//params以及查询字符串本质上没有区别 查询字符串对象形式传递参数 params按照顺序传
app.listen(3000,()=>{
    console.log('服务器启动了')
})

这些代码就实现了登录注册的基本功能。

5.模板引擎

当然学后端node.js只是了解基本使用,比较现在还是主打前端开发。这里我们默认没有前端动态展示数据库数据,我们现在服务器写死一些数据希望可以去动态的展示,我们希望有一个东西,可以嵌套变量,在node里面叫做模板。

html是静态页面 不会跟随服务器数据变化而变化。希望有一个东西 长的像网页 但是可以嵌套变量 这个东西node里面叫做模版。node存在很多个模版引擎 template engines 比如ejs。

ejs是node中的一款模版引擎 使用步骤

1.安装ejs

2.配置express模版引擎为ejs

app.set('view engine','ejs')

3.配置模版路径

app.set('views',path.resolve(__dirname,'views'))

注意 模版引擎需要被express渲染后用户才能使用

res.render()用来渲染一个模版引擎并将其返回给浏览器 render就是把ejs渲染并且返回给用户跟react 一样的 只不过react在前端这里是后台 后端环境。

​编辑

这是模板的目录需要放到views文件夹中。

javascript 复制代码
const express = require('express')
const app = express()
const path = require('node:path')
const STUDENT = [
    {
        name:'sun',
        age:123,
        gender:'男',
        address:'花果山'
    },
    {
        name:'zhu',
        age:231,
        gender:'女',
        address:'高老庄'
    }
]
//将ejs配置为默认的模版引擎
app.set('view engine','ejs')
//配置模版路径
app.set('views',path.resolve(__dirname,'views'))
//配置静态资源路径
app.use(express.static(path.resolve(__dirname,'public')))
//配置请求体解析 body 
app.use(express.urlencoded())
//解析json请求体
//app.use(express.json())
/*
    可以写路由了
*/
app.get('/hello',(req,res)=>{
    res.send('hello')
})
//我们希望用户访问student路由的时候 可以返回一个页面里面有表格 存放的student数据
app.get('/students',(req,res)=>{
   //render可以将一个对象作为render的第二个参数传递 这样在模版中可以访问到对象中的数据
    //res.render('students',{name:'sun',age:'18'})//名字和ejs名一致
    //<%=name %>在ejs输出内容时 自动对字符串中的特殊符号进行转义 < 不会识别为标签
    //这个设计主要是为了避免xss 攻击
    //<%- %>会直接运行h1 以及js脚本等等
    res.render('students',{STUDENT})
})
//在所有路由的后面配置错误路由
//不写路径会匹配所以路径 上面路由都没有匹配到
app.use((req,res)=>{
    //这个执行 上面的路由都没有匹配
    res.status(404)
    res.send('你访问的地址被外星人劫持')
})
app.listen(3000,()=>{
    console.log('服务器已经启动')
})

这里我们用render去渲染我们的模块,这样访问这个路由就可以返回一个页面给浏览器。然后第二个参数是对象,可以将动态展示的数据传递到模板,模板进行展示。

​编辑

这里是模板的写法,仍然是html写法只不过变量需要用<%= %>获取js代码需要<%%>包裹。

说到底,Express 并不是一个神秘的东西,它只是对原生 Node HTTP 模块的一层"封装和简化"。我们只要记住三件事:

它帮我们创建服务器(app.listen())

它让我们能轻松写路由处理请求(app.get()、app.post())

它通过中间件机制帮我们定制各种服务逻辑

加上它对静态资源、模板引擎的良好支持,我们就能快速搭建出一个真正能跑的 Web 项目。

相关推荐
AntBlack12 分钟前
闲谈 :AI 生成视频哪家强 ,掘友们有没有推荐的工具?
前端·后端·aigc
Livingbody1 小时前
使用gradio构建一个大模型多轮对话WEB应用
后端
泉城老铁3 小时前
Spring Boot 对接阿里云 OSS 的详细步骤和流程
java·后端·程序员
Aurora_NeAr3 小时前
大数据之路:阿里巴巴大数据实践——元数据与计算管理
大数据·后端
喜欢板砖的牛马3 小时前
容器(docker container):你需要知道的一切
后端·docker
EnigmaGcl3 小时前
领域驱动设计,到底在讲什么?
后端·架构
丘山子4 小时前
API Gateway 工作原理介绍
前端·后端·面试
砌玉成璧4 小时前
Flask一个用户同时只能在一处登录实现
后端·python·flask
元闰子4 小时前
分离还是统一,这是个问题
后端·面试·架构