简介 #
简单
设置服务器 #
首先,我们将通过以下方式创建我们的package.json文件,使用默认值
npm init -y
npm: 这是Node Package Manager,是用于管理Node.js包和依赖项的命令行工具。
init: 这是一个用于初始化新的Node.js项目的命令,通过创建一个package.json文件,其中包含有关项目的信息,如名称、版本、描述、依赖项等。
-y: 这是一个代表“是”的标志。在与npm init一起使用时,它告诉npm自动接受所有提示的默认值,并初始化package.json文件,无需任何用户输入。
- 现在我们将添加express mongoDB和js。
npm i express mongoose ejs
进入全屏模式退出全屏模式
npm i express mongoose ejs命令用于安装三个Node.js包:Express、Mongoose和EJS。
以下是每个包的功能:
Express: Express是用于Node.js的快速、无固定意见和极简主义的Web框架。它提供了一套强大的功能,用于构建Web应用程序和API,包括路由、中间件支持和模板渲染。
Mongoose: Mongoose是一个面向MongoDB和Node.js的对象数据建模(ODM)库。它提供了一个直观的基于模式的解决方案,用于建模应用程序数据并与MongoDB数据库交互。Mongoose简化了定义模式、验证数据和执行CRUD操作的过程。
EJS: EJS(嵌入式JavaScript)是一种简单的模板语言,让我们可以使用纯JavaScript生成HTML标记。它允许我们直接在HTML模板中嵌入JavaScript代码,从而轻松通过动态呈现数据创建动态网页。
现在我们将添加开发依赖和nodemon(它将帮助我们自动刷新网页);
npm i --save-dev nodemon
要运行nodemon,我们必须添加
"devStart": "nodemon server.js"
进入全屏模式退出全屏模式
在我们的package.json文件中。
创建索引路由 #
app.set('view engine', 'ejs');
进入全屏模式退出全屏模式
这里,app.set是一个用于为我们的Express应用分配设置的方法。'view engine'设置用于指定应用程序的模板引擎。
'EJS'代表嵌入式JavaScript。它是一种简单的模板语言/引擎,让我们可以使用纯JavaScript生成HTML标记。
现在我们将创建一个views文件夹并创建index.ejs文件,编写一些基本的HTML代码,并通过以下方式从server.js文件访问它,
res.render('index');
创建文章路由 #
在我们的项目中将有很多路由,如显示、编辑、删除等。我们不会从server.js文件访问它们。因此,我们将创建routes文件夹,在其中创建article.js。
const express = require('express');
const router = express.Router();
module.exports = router;
进入全屏模式退出全屏模式
在server.js中,我们可以轻松访问article.js添加,
const articleRouter = require('./routes/articles');
app.use(articleRouter);
我们想在每个路由文件的前面加上/article
。这样我们可以使用
app.use('/articles', articleRouter);
进入全屏模式退出全屏模式
这将使articles.js文件在搜索localhost:5000/articles
时获得 '/'。在articles.js中,我们可以通过以下方式轻松捕获它,
articleRouter是一个Express路由的实例。路由器是一种中间件,允许您以模块化的方式对应用程序的不同部分进行分组和处理路由。这个路由器将处理所有以/articles开头的请求。
router.get('/', (req, res) => {
res.send('This is from article.js');
})
``` * * *
#### 将文章传递给索引
目前我们只传递索引,但我们希望传递所有文章的值。
```javascript
const articles = [{
title: '测试文章',
createdAt: Date.now(),
description: '测试描述'
}]
res.render('index', {articles: articles});
我们稍后将在index.ejs中传递文章值。
- 现在我们将在index.ejs文件中添加Bootstrap链接。
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
并添加新文章按钮。
<div class="container">
<h1 class="mb-4">博客文章</h1>
<a href="/articles/new" class="btn btn-success">新文章</a>
</div>
创建主体 #
对于主体,我们将在index.ejs中添加forEach方法并在server.js上做一些更改。
在index.ejs中我们要添加,
<div class="container">
<h1 class="mb-4">博客文章</h1>
<a href="/articles/new" class="btn btn-success">新文章</a>
<% articles.forEach(article => {
%>
<div class="card mt-4">
<div class="card-body">
<h4 class="card-title"> <%= article.title %></h4>
<div class="card-subtitle text-muted mb-2">
<%= article.createdAt.toLocaleDateString() %>
</div>
<div class="card-text mb-2">
<%= article.description %>
</div>
</div>
</div>
<%
}) %>
</div>
在server.js中我们更新了文章的创建时间值,
createdAt: new Date(),
创建新文章路由 #
一开始我们创建了“新文章”按钮。现在我们要为其创建新路由。
- 在article.js中,我们更新了路由get,
router.get('/new', (req, res) => {
res.render('articles/new');
})
现在我们在views中创建一个新文件夹,命名为articles,并在其中创建一个名为new.ejs的新文件,将index.ejs文件移动到articles文件夹中。
我们在server.js中更新路径,
res.render('articles/index', {articles: articles});
- 在new.ejs中我们添加,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<title>博客</title>
</head>
<body>
<div class="container">
<h1 class="mb-4">新文章</h1>
<form action="/articles" method="POST">
</form>
</div>
</body>
</html>
但这个新页面还没有完成,我们还需要做更多。我们需要创建一个示例格式,为我们提供与“新文章”输入和“编辑文章”相同界面的样式。因此,我们将在文件夹views->articles->_form_fields.ejs中创建一个名为_form_fields.ejs的新文件。 <%-
这个符号帮助我们渲染实际的HTML,而不是文本。
现在我们将把这部分添加到我们的 new.ejs 文件,
<%- include('_form_fields') %>
通过这种方式,我们可以访问 _form_fields.ejs,无论我们在表单文件中写了什么,都会显示在我们的新文章页面上。
现在我们写一些示例,
输出为,
好的,现在我们要修改我们的 form_fields 文件,
- 现在我们编辑后的 _form_fields.ejs 文件如下,
<!-- 这是标题-->
<div class="form-group">
<label for="title">标题</label>
<input required type="text" name="title" id="title" class="form-control"> <!-- form-control 为我们提供了漂亮的表单格式,required 为我们提供了必须填写表单的约束-->
</div>
<!-- 这是描述-->
<div class="form-group">
<label for="description">描述</label>
<textarea name="description" id="description" class="form-control"></textarea><!-- form-control 为我们提供了漂亮的表单格式,required 为我们提供了必须填写表单的约束-->
</div>
<!-- 这是markdown-->
<div class="form-group">
<label for="markdown">Markdown</label>
<textarea name="markdown" id="markdown" class="form-control"></textarea><!-- form-control 为我们提供了漂亮的表单格式,required 为我们提供了必须填写表单的约束-->
</div>
<!-- 取消按钮将取消编辑并带我们回到主页-->
<a href="/" class="btn btn-secondary">取消</a>
<!-- 提交按钮-->
<button type="submit" class="btn btn-primary">保存</button>
现在这个取消按钮将带我们回到主页,保存按钮将把数据发布到我们的服务器,但是我们必须将数据存储到我们的数据库中。因此,我们需要将我们的数据库连接到服务器。
连接数据库 #
- 首先我们要打开我们的 server.js 文件。并添加这些行,
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog');
就是这样。
根据您的mongodb版本不同,您可能会遇到不同的 过时警告,比如我没有遇到任何过时警告,但如果您遇到过时警告,您必须添加到,
mongoose.connect('mongodb://localhost/blog', { useNewUrlPerser: true});
.
现在我们将创建一个 mongoose模型 来存储文章。
Mongoose 模型 #
在 article.js 中,我们将添加以下内容,
const mongoose = require('mongoose');
const articleSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
description: {
type: String
},
markdown:{
type: String,
required: true
},
craetedAt: {
type: Date,
default: Date.now
}
})
// 导出模式
module.exports = mongoose.model('Article', articleSchema)
好的,现在我们要从
routes->articles.js 访问它,
const Article = require('./../models/article')
, 这将有路径到 models article.js现在我们要创建一个对象,它将接收用户的发布数据,
const article = new Article({
})
但是我们必须告诉 express 如何访问这个对象。因此,我们需要进入 server.js 并添加,
app.use(express.urlencoded({extended: false}))
在将此添加到 server.js 后,我们可以通过 req.body.title、req.body.description、req.body.markdown 轻松访问 新文章 的标题、描述和 markdown。
const express = require('express');
// 访问模型 article
const Article = require('./../models/article')
const router = express.Router();
router.get('/new', (req, res) => {
res.render('articles/new');
})
router.get('/:id', (req, res) ```
router.get('/:id', async (req, res) => {
const article = await Article.findById(req.params.id)
if(article == null) res.redirect('/')
res.render('articles/show', {article: article })
})
在articles.js中的此部分中,我们首先要编辑这个部分。 在vies->articles-show.ejs中创建一个名为show.ejs的新文件。
在修复之后,
访问所有文章 #
所以,我们将访问新创建的数据。
我们必须转到server.js。我们需要删除此部分,并且我们需要从server.js访问文章模型。它在models->article.js中。
const Article = require('./models/article')
const articles = await Article.find().sort({
createdAt: 'desc'
})
添加显示按钮 #
我们将在主页上创建一个显示按钮。
首先,我们必须转到我们的index.ejs文件。并添加这个,
现在我们将使用slug,slug是article_id的替代品。
SLUG #
所以我们将导入slug库
npm i marked slugify
marked: 这是一个允许我们在Node.js中解析Markdown的软件包。它将Markdown语法转换为HTML。
slugify: 此软件包用于将字符串转换为slug,slug是字符串的URL友好版本。它删除特殊字符,将空格转换为破折号,并将字符串转换为小写。
然后我们转到models->article.js。并添加,
const marked = require('marked')
const slugify = require('slugify')
另外,
articleSchema.pre('validate', function(next) {
if(this.title) {
this.slug = slugify(this.title, { lower: true, strict: true })
}
next()
})
为了使用slug,我们需要做一些更改。首先在routes-articles.js中,
router.get('/:slug', async (req, res) =>
添加slug取代id。const article = await Article.find({ slug: req.params.id })
将findById更改为添加slug部分。res.redirect(
/articles/${article.slug})
删除id并添加slug。
但是如果您刷新站点,会出现一些错误。
为了解决这个问题,我们需要转到views->articles->index.ejs并更改,
<a href="articles/<%= article.slug %>" class="btn btn-primary">Read More</a>
将article.id更改为article.slug。
问题仍未解决。所以我们需要转到routes->articles.js并更改,
const article = await Article.findOne({ slug: req.params.id })
我们将find更改为findOne。
一些类型被修复 #
在routes->articles.js中:const article = await Article.findOne({ slug: req.params.slug })
在models->article.js中:this.slug = slugify(this.title, { lower: true,
修复完所有这些问题后,可能会显示弃用的内容,就像一开始显示的那样。请像以前在数据库连接中显示的那样添加这些弃用内容。所有这些都是由于mongodb版本引起的。
创建DELETE路由 #
所以我们可以看到,只有新闻文章添加了slug,而旧文章则没有显示任何内容。
不同的文章显示不同。
为了解决这个问题,我们将删除旧文章。为此,我们必须创建一个DELETE路由。
在routes->articles.js中首先要做的是,我们需要DELETE方法,因此,我们必须导入它。因此,在终端中我们使用:
npm i method-override
method-override包允许我们在客户端不支持的地方使用诸如PUT或DELETE之类的HTTP动词。这在处理Express.js中的表单数据时可能很有用,因为HTML表单只支持GET和POST请求。
创建DELETE表单 #
首先我们要去我们的index.ejs文件,然后,
<form action="/articles/<%= article.id %>?_method=DELETE" method="POST" class="d-inline"><button type="submit" class="btn btn-danger">删除</button></form>
删除部分已经完成,但在routes->articles.js中有一些拼写错误。请更正redirect的拼写。
DOM净化 #
我们需要导入一些包,
npm i dompurify jsdom
我们去models->article.js,并在顶部添加这些内容,
const createDomPurify = require('dompurify')
const { JSDOM } = require('jsdom')
const dompurify = createDomPurify(new JSDOM().window)
在图片中我们写了createdomPurify,这个拼写错误。应该是createDomPurify。
然后我们还添加这些内容,
sanitizedHtml: {
type: String,
required: true
}
然后添加这些内容,
if(this.markdown) {
this.sanitizedHtml = dompurify.sanitized(marked(this.markdwon))
}
接下来我们要去show.ejs并更新,
<%- article.sanitizedHtml %>
编辑博客 #
所以我们在routes->articles.js。
我们添加路由,
router.get('/edit/:id', async (req, res) => {
const article = await Article.findById(req.params.id)
res.render('articles/edit', {article: article})
})
``` 我们在views->articles中添加了一个新文件**edit.ejs**。然后添加,
<title>Blog</title>
Edit Article
<form action="/articles/<%= article.id %>?_method=PUT" method="POST">
<%- include('_form_fields') %>
</form>
</div>
然后我们添加了put方法,因为put和post方法相同,所以我们将使用中间件。
function saveArticleAndRedirect(path) {
return async (req, res) => {
let article = req.article
article.title = req.body.title
article.description = req.body.description
article.markdown = req.body.markdown
try{
article = await article.save()
res.redirect(/articles/${article.slug}
)
}catch(e){
res.render(articles/${path}
, {article: article })
}
}
}
1.我们还在这里更新了我们的**POST**方法,
router.post('/', async(req, res, next) =>{
req.article = new Article()
next()
}, saveArticleAndRedirect('new'))
所以我们的post现在完成了,**PUT**。
但是在添加该方法后,它停止工作。
我们稍后会继续解决这个问题。