一、需求分析
二、开发工具
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路径,需要写全,即哪个路由下面的某某路径,比如
用户管理
如果是三层,比如:添加分类,那么对应的请求接口为:
router.get('/category/add')
②、与此相对应的,是router下面的路由文件,因为路由文件,比如本身已经命名为admin.js,那么下面请求的接口只需要写router.get('/user')即可
③、res.render渲染的模板路径,也要写全,是哪个文件夹下的某某文件,比如:
res.render('admin/category_add'),后缀名可以不用写
④添加分类,跳转到添加分类页
这种写法默认是get请求,所以处理路由就得使用get进行:router.get('/category_add')
如果要传递数据过去,在表单里面
20、a链接点击实现post方式提交,
修改
//点击页面会跳转到category_edit,匹配了get和post路由
router.get('/category_add')、router.post('/category_add')
//此时,form表单可以不需要写action的url地址,因为还是在当前页,当前页的路由还是/category_add,还是会匹配到对应的路由,执行对应的操作
对应的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了,当前页面不变,路由自然就会继续执行