一、需求分析
二、开发工具
1、NodeJs:基础核心开发语言
2、express:一个简洁而灵活的nodejs web应用框架,提供一系列强大的特效帮助我们创建各种web应用
从本质上来说,一个 express 应用就是在调用各种中间件。
3、mongodb:数据库
4、第三方模块/中间件
bodyParser:解析post请求数据
cookies:读/写cookie
swig:模板解析引擎
mongoose:操作mongodb数据
markdown:markdown语法解析生成模块
require一个模块,如果是通过npm安装的或者是express内置的模块,直接require(‘模块名’) 如果是自定义的模块,必须加上相对路径
三、开发实战
1、构建项目目录结构、安装各种所需要的模块框架等等
npm init
npm install --save XXX
2、目录结构
3、创建应用,监听端口
//app.js
var express = require("express");
var app = express();
var server = app.listen(8888, function(){
console.log("Server is running on http://localhost:8888");
});
4、用户访问流程 用户通过URL访问web应用,如:http://localhost:8888 web后端根据用户访问的URL处理不同的业务逻辑 5、路由绑定 通过app.get()或ap.post()等方法可以把一个url路径和一个/N个函数进行绑定
app.get( ‘/’,function(req,res,next){
//req:request对象,保存客户端发送请求的一些数据 === http.request
//res:response对象,服务端输出对象,提供了一些服务器端输出的相关方法 === http.response
res下面有write、end、statusCode、setHeader、writeHead等属性和方法
//next:方法,用于执行下一个和路径匹配的函数
} )
6.1、直接内容输出 通过res.send(string)发送内容至客户端 6.2、通过模板渲染内容 res.render():将会根据views中的模板文件进行渲染 如果不想使用views文件夹,想自己设置文件夹名字,那么app.set("views","aaaa"); 注:如果想写一个快速测试页,当然可以使用res.send(),send这个函数将根据内容,自动帮我们设置了Content-Type头部和200状态码。send()只能用一次,和end一样。和end不一样在哪里?能够自动设置MIME类型。 7、模板的使用 后端逻辑和页面表现分离-即前后端分离 8、模板的配置
var swig = require('swig');
app.engine('html',swig.renderFile);
//定义当前应用所使用的模板引擎,使用swig.renderFile方法解析为后缀为html的文件
app.set('views','./views');
//设置模板存放目录
app.set('view engine','html');
//注册模板引擎
swig.setDefault({cache:false});
9、设置静态文件托管目录 css、img、js等都通过下面的方法进行托管 app.use('/public',express.static(__dirname+'/public')); 在public目录下划分并且存放好相关的静态资源文件 意思是:当用户访问的url以/public开始,那么直接返回对应__dirname+'/public'下的文件
10、前后端工作流程: 用户发送http请求 -> 这个请求是一个url -> 后端解析url,提取信息 -> 找到匹配的规则 -> 满足规则以后,执行绑定函数,返回对应内容给用户
划分:静态文件、动态文件处理两部分 静态文件: 访问/public下的文件 -> 静态文件 -> 直接读取指定目录下的文件,直接渲染,返回给用户 动态文件: 处理业务逻辑,加载模板,解析模板 -> 返回数据给用户 通过路由匹配来获取指定文件内容
11、模块划分 根据功能进行模块划分 前台模块 main.js 后台管理模块 admin.js API模块(页面使用ajax请求的模块)api.js app.js:整个应用的入口文件
//使用app.use()进行模块划分
app.use('/admin',require('./router/admin')); //请求admin.js,当url匹配都/admin,就会请求./router/admin这个下面的admin.js文件,下面类似
app.use('/api',require('./router/api')); //请求api.js
app.use('/',require('./router/main')); //请求main.js
admin.js
let express = require('express');
let router = express.Router();
router.get('/',(req,res,next)=>{
res.send('admin');
})
module.exports = router;
12、数据库连接、表结构Schema定义、Model创建
进入到mongoDB安装目录下的bin目录,执行命令: mongod --dbpath=D:\妙味课堂文件夹\190122Nodejs开发博客系统\db --port=27017 定义数据库存放位置和端口号 然后连接数据库,这里通过可视化界面工具来连接数据库:Robomongo软件来连接
13、连接数据库成功之后,在schemas文件夹下,新建用户表结构,user.js,定义用户的表 但是这里不是直接操作这个表结构的,而是通过model模型类来对表进行增删改查, model模型类:可以理解成对数据操作的封装,Model中封装对数据进行处理的逻辑业务。
14、ajax发送请求的url地址:url : '/api/user/register',发送信息到后台,后台进行接收前台传过来的数据
‘api/user/register’ 是解析router配置的第一级路径'/api'的,然后在二级router下再匹配对应的 ‘user/register’
对应api.js文件下的 '/user/register' 路由 这里使用post请求,接收前端传递过来的数据,使用body-parser中间件 使用:
//在入口文件app.js里面:
//加载body-parser,用来处理post提交过来的数据
var bodyParser = require('body-parser');
//bodyparser设置
app.use( bodyParser.urlencoded({extended: true}) );
这样在模型类api.js里面,通过req.body就可以获取到前端传递过来的值
router.post('/user/register',(req,res,next)=>{
console.log(req.body); //{ username: 'xiaoxiao', password: '11', repassword: '11' }
res.json(responseData); //将responseData对象转为json格式,返回给前端
})
就可以获取到post提交过来的数据,
反正前端通过post提交表单数据,那么后端就可以通过req.body来获取到该数据
数据的key值是input表单的name值,value值是用户输入的值
//查询数据库中相同用户名和密码的记录是否存在,如果存在则登录成功
15、获取到了前台传递过来的数据之后,后台再进入数据库进行操作,分2种情况: 注册:此时,数据库如果有该信用户名,说明数据库已经存在,则返回信息给前端,提示用户名已经存在 数据库如果没有注册信息,则需要把注册信息存入数据库,提示前台注册成功 这里面就涉及到了对数据的查找,可以使用find或者findOne进行查找 没有数据还要执行存储使用save进行操作
16、 Request Headers:是浏览器向服务器发送请求的时候,传递给服务端的数据 Response Headers:是服务器发传递给客户端的数据
17、渲染模板的同时带入数据 res.render('admin/index',{ userCookie:req.userCookie //userCookie可以直接带入到模板里面,使用{{}}就可以获取到数据 })
18、模板的使用 在index页面引入layout页面:{% extends 'layout.html' %} 在index页面重写layout页面内容
layout页面需要被重写的内容使用:{%block main%}{%endblock%}代替 index页面,使用语法: {% block main %} //这里面是重写的内容 {% endblock %} 即把layout页面需要被重写的内容剪切到index页面,然后各自用语法代替,语法如上所示
19、各自路径的设置 ①、html页面里的href路径,需要写全,即哪个路由下面的某某路径,比如 <a rel="nofollow" href="/admin/user">用户管理</a> 如果是三层,比如:<a rel="nofollow" href="/admin/category/add">添加分类</a>,那么对应的请求接口为: router.get('/category/add') ②、与此相对应的,是router下面的路由文件,因为路由文件,比如本身已经命名为admin.js,那么下面请求的接口只需要写router.get('/user')即可 ③、res.render渲染的模板路径,也要写全,是哪个文件夹下的某某文件,比如: res.render('admin/category_add'),后缀名可以不用写 ④<a rel="nofollow" href="/admin/category_add">添加分类</a>,跳转到添加分类页 这种写法默认是get请求,所以处理路由就得使用get进行:router.get('/category_add') 如果要传递数据过去,在表单里面 <form action="/admin/category_add" method="POST" role="form"></form> 就使用router.post('/category_add') 地址都是/category_add,只不过一个是get请求,一个是post请求 ⑥⑤
20、a链接点击实现post方式提交,
<a rel="nofollow" href="/admin/category_edit?id={{item._id.toString()}}">修改</a>
//点击页面会跳转到category_edit,匹配了get和post路由
router.get('/category_add')、router.post('/category_add')
//此时,form表单可以不需要写action的url地址,因为还是在当前页,当前页的路由还是/category_add,还是会匹配到对应的路由,执行对应的操作
对应的form表单:
<form method="POST" role="form">
<legend>修改分类</legend>
<div class="form-group">
<label for="">分类名称</label>
<input type="text" name="categoryName" value="{{categoryName}}" class="form-control" id="" placeholder="修改分类">
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
post表单用于有前台信息(一般是form表单)传递到后台、其他情况可以使用get请求
错误集结:
1、throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
这种错误一般是模块没有导出,使用module.exports = 导出即可
2、
报错:Cannot set headers after they are sent to the client
nodejs+express中出现这个错误都在路由里,大部分是程序运行了res.xxx之后,后面还有和请求,响应相关的操作造成的.
主要是程序运行的先后顺序没理解透,也就是nodejs的一大特性 : 异步.初学者特别要注意.
-- 理解到了nodejs程序运行的先后顺序,这个问题就迎刃而解.
-- 还有就是res.xxx操作之后最好不要再有代码,就算是打印输出的代码也写在res.xxx之前,
-- 另外,nodejs程序就算res.xxx响应以后,程序还会继续执行,return下更好.
3、Error: Failed to lookup view "index" in views directory "/views" view模板文件目录找不到 views错误与否取决你是怎么运行app.js,我使用cmd到指定目录下运行app.js就不会出现这个问题,而使用webstorm直接运行app.js就会出现这个问题。这个应该是文件目录的问题。想要在两种方式下都可以找到views的方法是使用:
var path = require('path');
app.set('views', path.join(__dirname, 'views'));
4、报下面的错误
CastError: Cast to ObjectId failed for value "" at path "_id" for model "Category"
是因为:submit提交还是在当前页面,路由不变,还是会走router.post('/category_edit'),而form表单加了action="/admin/category_edit",所以估计是重复报错,删掉form表单的action就行了;
以后注意:如果提交的页面还是在当前页面,那么就不用加action了,当前页面不变,路由自然就会继续执行