ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 项目需求与技术架构 完成一个新闻发布,修改,删除,展示的网站,及完成对新闻的 CRUD。因为要操作新闻数据,所以要使用 Mongoose + MongoDB,也需求处理用户的请求,需使用 Express + Node.js。 # 功能分析 ## 项目效果图 通过查询准备好的演示项目看。 * 新闻列表:在此页面展示数据库中的新闻数据。 * 新闻发布:发布新闻页面,页面有一个表单,包括:新闻标题、作者、来源、发布时间、内容,填好提交。 * 新闻删除:新闻列表页面,点击删除链接,直接删除掉。 * 浏览新闻:点击新闻列表的标题进入查看详情页面,展示新闻详情。 * 新闻修改:新闻列表页面,点击修改链接,进入修改的页面,回显被修改的新闻数据,修改完之后提交。 ## 数据从页面到数据库要经历哪些过程 数据库不会主动给数据,都是浏览器先发起请求。 * 浏览器发起一个请求。 * 请求提交数据,比如新闻发布的数据。 * 后端程序接收数据。 * 后端程序保存数据到数据库。 ## 数据从数据库到页面要经历哪些过程 * 浏览器发起一个请求。 * 后端程序接收请求。 * 后端程序查询数据库。 * 后端程序把拿到的数据返回给浏览器。 # 项目搭建 ## express-generator 使用 ``` 使用 express-generator 来完成项目搭建,使用命令搭建项目(),提高项目搭建效率。 ``` ``` // 全局安装 express-generator npm install -g express-generator // 设置使用的视图技术,并创建项目 express --view=ejs 项目所在位置 ``` ``` bin : 二进制,里面有一个 www 文件public : 静态资源目录 routers : 路由目录 views : 视图目录 app.js : 应用文件 ``` ## 安装包 在 package.json 中增加 mongoose: ``` "mongoose": "*", ``` 通过 npm install 可以一次性安装所有依赖的包, 若不写具体的版本号 可以用 \* 表示安装最新版本。 ## 项目启动 在 app.js 中的代码包含引入模块(包括路由模块),设置视图技术,应用中间件(静态处理等)。若要启动项目,修改app.js 文件代码, 删除导出代码, 加入 app.listen(8888, () => console.log('启动成功8888')) 之后再使用 nodemon app.js 运行项目, 并测试一下. # RESTful API RESTful 架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。 针对是动态资源路径的一种风格 * 每一个 URI 代表一种资源; * 客户端和服务器之间,传递这种资源的某种表现层; * 客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现"表现层状态转化"。 以下符合 RESTful 风格的 URI: * GET /zoos:列出所有动物园 * POST /zoos:新建一个动物园 * GET /zoos/ID:获取某个指定动物园的信息 * PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息) * PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) * DELETE /zoos/ID:删除某个动物园 * GET /zoos/ID/animals:列出某个指定动物园的所有动物 * DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物 其实遵循上述就已经符合 RESTful 风格了,若想遵循更标准的,对响应的内容也有要求,但因为东西过多,就不做描述了与实现,我们就统一响应 JSON 格式的数据,一下就是根据项目需求定义好 RESTful 接口。 * GET /api/articles:列出所有文章 * POST /api/articles:发布文章 * GET /api/articles/ID:获取某篇文章信息 * PUT /api/articles/ID:更新某篇文章 * DELETE /api/articles/ID:删除某篇文章 # 文章发布页面 把前端开发好的页面,若是内容会变化的,改成后缀为 .ejs,放置到项目根目录下 views 目录中;若是内容不会变化的,放置到 public/articles 目录,比如新闻发布的页面,修改页面,详情页面。 在浏览器输入 [http://localhost:3000/articles/new.html](http://localhost:3000/articles/new.html),即可跳转到新闻发布的页面。 # 文章发布 * 修改 new.html,修改页面表单元素的 name 属性值,使用 Ajax 提交请求。 ~~~ $(function(){    $('#btnSave').click(function(){        $.ajax({            url: '/api/articles/',            type: 'POST',            data : $('#articleForm').serialize(),            success: function(data) {                // 提示保存结果                alert(data.msg);                // 再重新跳转文章列表页面, 重新获取文章数据                location.href = '/articles/list.html'           }       });   }); }); ~~~ * 重命名 routers/user.js 为 routers/articlesRouter.js,修改 app.js 中对这个路由模块引入和应用代码。 ~~~ var articlesRouter = require('./routes/articlesRouter'); app.use('/api', articlesRouter); ~~~ * 在 app.js 中编写连接数据库的代码。 ~~~ const mongoose = require('mongoose'); // 在项目启动过程中完成数据库链接 mongoose.connect('mongodb://localhost/test', {user:'root', pass:'12345', authSource:'admin'}); mongoose.connection.on('connected', () => console.log('链接数据库成功')); ~~~ * 添加文件 models/articleModel.js,在此文件中定义文章模块。 ~~~ const mongoose = require('mongoose'); // 定义 Schema let ArticleSchema = new mongoose.Schema({    title:String,    author:String,    source:String,    content:String,    createTime:String }); // 定义 Model let ArticleModel = mongoose.model('article', ArticleSchema); module.exports = ArticleModel; ~~~ * 修改 routers/articlesRouter.js 中的内容,针路径是 /api/articles 且 post 方式请求的处理,获取请求参数。 ~~~ var express = require('express'); var router = express.Router(); var articleService = require('../services/articleService'); ​ // 负责接收参数组成 js 对象 // 调用对应业务方法 // 根据调用结果做相应响应 router.post('/articles', (req, res) => {  let { title, author, content, source, createTime = new Date().toLocaleString() }    = req.body; ​  articleService.save({ title, author, content, source, createTime })   .then(function () { // 成功      res.json({ success: true, msg: '保存成功' })   }).catch(function () { // 失败      res.json({ success: false, msg: '保存失败' })   }); }); ​ module.exports = router; ~~~ * 修改 services/articleService.js 中的内容,提供保存文章的方法,并导出。 ~~~ var mongoose = require('mongoose'); var ArticleModel = require('../models/articleModel'); module.exports = {    save : art => new ArticleModel(art).save(); } ~~~ # 文章列表 * 准备一个静态页面 list.html 放置在 public 目录下articles 目录中 * 在 list.html 编写发送请求代码, 页面加载完发送请求获取文章数据 ~~~ $(function(){    $.get('/api/articles', function(data){        data.forEach(function(article){ // 遍历数据            var trString = `                <tr>                <td><a href="#">${article.title}</a></td>                <td class="tdcenter">${article.source}</td>                <td class="tdcenter">${article.author}</td>                <td class="tdcenter">${article.createTime}</td>                <td><a href="#">修改</a><a class='del' data-id='' href="#">删除</a></td>                </tr>                `            // 页面表格的 id 属性值为 articleTable            $('#articleTable').append(trString);       })   }); }); ~~~ * 修改 routers/articlesRouter.js 文件内容,针路径是 /api/articles 且 get 方式请求的处理。 ~~~ router.get('/articles', (req, res) => {  articleService.selectAll()   .then(function (result) { // 成功      res.json(result)   }).catch(function () { // 失败      res.json([]);   }); }); ~~~ * 修改 services/articleService.js 中的内容,提供查询所有文章的方法,并导出。 ~~~ var mongoose = require('mongoose'); var ArticleModel = require('../models/articleModel'); module.exports = {    save: (art) => new ArticleModel(art).save(),    selectAll   :   Article.find() } ~~~ # 文章删除 * 修改 views/articles/list.html 文件中删除 a 标签,给其绑定处理事件,发送 Ajax 请求,请求方式 delete,带上删除文文章的 id。 ~~~ $(function(){    $('#articleTable').on('click', 'a.del', function(){        // console.log(this); // 被点击 a 标签        var id = $(this).attr('data-id');        console.log(id); ​        $.ajax({            type:'delete',            url:'/api/articles/' + id,            success: function(data){                // 提示删除结果                alert(data.msg);                // 再重新跳转文章列表页面, 重新获取文章数据                location.href = '/articles/list.html'           }       })   }); }); ~~~ * 修改 routers/articlesRouter.js 中的内容,针路径是 /api/articles/ID 且 delete 方式请求的处理,获取被删除文章的 id。 ~~~ router.delete('/articles/:id', function(req, res, next) {    articleService.deleteById(req.params.id)       .then(resutl => {            res.json({msg : '文章删除成功'});       }).catch(err => {            res.json({msg : '文章删除失败'});       }); }); ~~~ * 修改 services/articleService.js 中的内容,提供根据 id 删除文章的方法,并导出。 ~~~ module.exports = {    save: (art) => new ArticleModel(art).save(),    selectAll: () => ArticleModel.find({}),    deleteById: (id) => ArticleModel.findByIdAndRemove(id) } ~~~ # 文章详情 * 修改 views/articles/list.html 文件中文章标题的 a 标签的 href 属性,值改为 /articles/view.html?id=${article.\_id} * 修改 public/articles/view.html 文件,等页面加载完发送 Ajax 请求获取文章数据,并回显。 ~~~ $(function(){    var id = location.href.split('=')[1];    $.get('/api/articles/' + id, function(data){        $('#title').html(data.title);        $('#author').html(data.author);        $('#source').html(data.source);        $('#createTime').html(data.createTime);        $('#content').html(data.content);   }); }) ~~~ * 修改 routers/articlesRouter.js 中的内容,针路径是 /api/articles/ID 且 GET 方式请求的处理,获取被查询文章的 id。 ~~~ router.get('/articles/:id', (req, res) => {  let id = req.params.id; // 获取被删除文章 id 值  articleService.selectById(id)   .then(function (result) { // 成功      res.json(result)   }).catch(function () { // 失败      res.json({})   }); }); ~~~ * 修改 services/articleService.js 中的内容,提供根据 id 删除文章的方法,并导出。 ~~~ module.exports = {    save: (art) => new ArticleModel(art).save(),    selectAll: () => ArticleModel.find({}),    deleteById: (id) => ArticleModel.findByIdAndRemove(id),    selectById: (id) => ArticleModel.findById(id) } ~~~ # 文章修改页面 * 修改 views/articles/list.html 文件中文章修改 a 标签的 href 属性,值改为 /articles/edit.html?id=${article.\_id} * 修改 public/articles/edit.html 文件,等页面加载完发送 Ajax 请求获取文章数据,并回显。 ~~~ $(function(){    var id = location.href.split('=')[1]    $.get('/api/articles/' + id, function(data){        $('#title').val(data.title);        $('#author').val(data.author);        $('#source').val(data.source);        $('#content').val(data.content);   }); }); ~~~ # 文章修改 * 修改 public/articles/edit.html 文件,给修改按钮绑定时间处理函数,使用 Ajax 发送修改文章的请求。 ~~~ $('#btnUpdate').click(function(){    $.ajax({        type:'put',        url:'/api/articles/' + id,        data:$('#articleForm').serialize(),        success:function(data){            alert(data.msg);            location.href = '/articles/list.html'       }   }) }); ~~~ * 修改 routers/articlesRouter.js 中的内容,针路径是 /api/articles/ID 且 PUT 方式请求的处理,获取被修改文章的 id及修改的数据。 ~~~ router.put('/articles/:id', (req, res) => {  let id = req.params.id; // 获取被删除文章 id 值  let { title, author, content, source} = req.body; ​  articleService.updateById(id, {title, author, content, source})   .then(function (result) { // 成功      res.json({ success: true, msg: '修改成功' })   }).catch(function () { // 失败      res.json({ success: false, msg: '修改失败' })   }); }); ~~~ * 修改 services/articleService.js 中的内容,提供根据 id 修改文章的方法,并导出。 ``` module.exports = { save: (art) => new ArticleModel(art).save(), selectAll: () => ArticleModel.find({}), deleteById: (id) => ArticleModel.findByIdAndRemove(id), selectById: (id) => ArticleModel.findById(id), updateById: (id, updates) => ArticleModel.findByIdAndUpdate(id, updates) } ```