资讯专栏INFORMATION COLUMN

node博客项目开发手记

Jiavan / 2449人阅读

摘要:所以呢解决这个问题也很简单,就是对提交的内容进行或者其他形式的编码,在服务器端进行解码,即可解决。项目地址完结撒花

NodeJs开发个人博客项目

预览地址:http://baijiawei.top

GitHub地址:https://github.com/bjw1234/blog

需要安装的模块

body-parser 解析post请求

cookies 读写cookie

express 搭建服务器

markdown Markdown语法解析生成器

mongoose 操作Mongodb数据库

swig 模板解析引擎

目录结构

db 数据库存储目录

models 数据库模型文件目录

public 公共文件目录(css,js,img)

routers 路由文件目录

schemas 数据库结构文件

views 模板视图文件目录

app.js 启动文件

package.json

app.js 文件

1.创建应用、监听端口

const app = express();

app.get("/",(req,res,next) => {
    res.send("Hello World !");
});
app.listen(3000,(req,res,next) => {
    console.log("app is running at port 3000");
});

2.配置应用模板

定义使用的模板引擎 app.engine("html",swig.renderFile) 参数1:模板引擎的名称,同时也是模板文件的后缀 参数2:表示用于解析处理模板内容的方法

设置模板文件存放的目录 app.set("views","./views")

注册所使用的模板引擎 app.set("view engine","html")

3.用模板引擎去解析文件

/**
 * 读取views目录下的指定文件,解析并返回给客户端 
 * 参数1:模板文件
 * 参数2:给模板传递的参数 
 */
 
res.render("index",{
    title:"首页 ",
    content: "hello swig"
});

4.开发过程中需要取消模板缓存的限制

swig.setDefaults({
  cache: false
});

app.set("view cache", false);

5.设置静态文件托管

 // 当用户访问的是/public路径下的文件,那么直接返回
app.use("/public",express.static(__dirname + "/public"));

划分模块

前台模块

后台模块

API模块

// 根据不同的功能划分模块
app.use("/",require("./routers/main"));
app.use("/admin",require("./routers/admin"));
app.use("/api",require("./routers/api"));

对于管理员模块 admin.js

var express = require("express");
var router = express.Router();

// 比如访问 /admin/user
router.get("/user",function(req,res,next) {
    res.send("User");
});

module.exports = router;
前台路由 + 模板

main 模块
/ 首页
/view 内容页

api模块
/ 首页
/register 用户注册
/login 用户登录
/comment 评论获取
/comment/post 评论提交

后台(admin)路由+模板

首页
/ 后台首页

用户管理
/user 用户列表

分类管理
/category 分类列表
/category/add 分类添加
/category/edit 分类修改
/caterory/delete 分类删除

文章内容管理
/article nei内容列表
/article/add 内容添加
/article/edit 内容修改
/article/delete 内容删除

评论内容管理
/comment 评论列表
/comment/delete 评论删除

功能开发顺序

功能模块开发顺序

用户

栏目

内容

评论

编码顺序

通过Schema定义设计数据存储结构

功能逻辑

页面展示

连接数据库(mongoDB)

启动MongoDB服务端:
mongod --dbpath=G:datadb --port=27017
启动服务设置数据库的存储地址以及端口

var mongoose = require("mongoose");
// 数据库链接
mongoose.connect("mongodb://localhost:27017/blog",(err) => {
    if(err){
        console.log("数据库连接失败");
    }else{
        console.log("数据库连接成功");
      // 启动服务器,监听端口  
      app.listen(3000,(req,res,next) => {
            console.log("app is running at port 3000");
        });
    }
});
定义数据表结构和模型

对于用户数据表(users.js)在schema文件夹下:

var mongoose = require("mongoose");
module.exports = new mongoose.Schema({
    // 用户名
   username:String,
   // 密码
   password:String
});

在models目录下创建user.js模型类

var mongoose = require("mongoose");
var userSchema = require("../schemas/users");

module.exports = mongoose.model("User",userSchema);
处理用户注册

前端通过ajax提交用户名和密码

url: /api/register

后端对前端提交(POST)的数据解析

var bodyParser = require("body-parser");
// bodyParser 配置
// 通过使用这一方法,可以为req对象添加一个body属性
app.use( bodyParser.urlencoded({extended:true}));

// 在api模块中:
// 1.可以定义一个中间件,来统一返回格式
var responseData;
router.use( function(req,res,next){ // path默认为"/",当访问该目录时这个中间件被调用
    responseData = {
         code:0,
       message:""
    };
    next();
});

router.post("/register",(req,res,next) => {
    console.log(req.body);
   // 去判断用户名、密码是否合法
   // 判断是否用户名已经被注册
   // 通过 res.json(responseData) 给客户端返回json数据
   
   // 查询数据库
   User.findOne({    // 返回一个promise对象
           username: username
   }).then(function( userInfo ) {
           if( userInfo ){ // 数据库中有该条记录
            ...
          res.json(responseData);
          return;
       }
       // 给数据库中添加该条信息
       var user = new User({ username:username,password:password });
       return user.save(); // 返回promise对象
   }).then(function( newUserInfo ){
            console.log(newUserInfo);
       res.json(responseData);  // 数据保存成功  
   });
});
cookies 模块的使用

全局(app.js)注册使用

// 设置cookie
// 只要客户端发送请求就会通过这个中间件
app.use((req, res, next) => {
    req.cookies = new cookies(req, res);

    /**
     * 解析用户的cookies信息
     * 查询数据库判断是否为管理员 isAdmin
     * 注意:查询数据库是异步操作,next应该放在回调里边
     */
    req.userInfo = {};
    if (req.cookies.get("userInfo")) {
        try {
            req.userInfo = JSON.parse(req.cookies.get("userInfo"));
            // 查询数据库判断是否为管理员
            User.findById(req.userInfo._id).then(function (result) {
                req.userInfo.isAdmin = Boolean(result.isAdmin);
                next();
            });
        } catch (e) {
            next();
        }
    } else {
        next();
    }
});

// 当用户登录或注册成功之后,可以为其设置cookies
req.cookies.set("userInfo",JSON.stringify({
     _id:result._id,
    username:result.username 
}));
swig模板引擎

1.变量
{{ name }}

2.属性
{{ student.name }}

3.if判断
{ % if name === "郭靖" % }
hello 靖哥哥
{ % endif % }

4.for循环
// arr = [1, 2, 3]
{ % for key, val in arr % }

{ { key } } -- { { val } }


{ % endfor % }

5.set命令
用来设置一个变量,在当前上下文中复用

{% set foo = [0, 1, 2, 3, 4, 5] %}

{% extends "layout.html" %} // 继承某一个HTML模板

{% include "page.html" %} // 包含一个模板到当前位置

{% block main %} xxx {% endblock %} //重写某一区块

6.autoescape 自动编码
当想在某个div中显示后端生成的HTML代码,模板渲染时会自动编码,
以字符串的形式显示。通过以下方式,可以避免这个情况:

 
{% autoescape false %} {{ data.article_content_html }} {% endautoescape %}
用户管理和分页

CRUD用户数据

const User = require("../models/user");

// 查询所有的用户数据
User.find().then(function(users){

});

// 根据某一字段查询数据
User.findOne({
    username:username
}).then(function(result){

});

// 根据用户ID查询数据
User.findById(id).then(function(user){

});

// 根据ID删除数据
User.remove({
    _id: id
}).then(function(){

});

// 修改数据
User.update({
    _id: id
},{
    username: name
}).then(function(){
    
});

数据分页管理

两个重要方法
limit(Number): 限制获取的数据条数
skip(Number): 忽略数据的条数 前number条

忽略条数:(当前页 - 1) * 每页显示的条数

// 接收传过来的page
let query_page = Number(req.query.page) || 1;
query_page = Math.max(query_page, 1);  // 限制最小为1
query_page = Math.min(Math.ceil(count / limit), query_page); // 限制最大值 count/limit向上取整


var cur_page = query_page;  // 当前页
var limit = 10; // 每页显示的条数
var skip = (cur_page - 1) * limit; //忽略的条数

User.find().limit(limit).skip(skip).then(function(users){
    ...
  // 将当前页 page 传给页面
  // 将最大页码 maxPage 传给页面
});
文章的表结构
// 对于content.js
var mongoose = require("mongoose");
var contentSch = require("../schemas/contentSch");

module.exports = mongoose.model("Content",contentSch);


// contentSch.js
module.exports = new mongoose.Schema({
    
   // 关联字段 - 分类的id
   category:{
        // 类型
        type:mongoose.Schema.Types.ObjectId,
        // 引用
        ref:"Category"  
    },
    
    // 内容标题
    title: String,
    
    // 简介
    description:{
        type: String,
        default: ""  
    },
    
    // 内容
    content:{
        type:String,
        default:""
    }
});

// 文章查询时关联category字段
Content.find().populate("category").then(contents => {
    // 那么通过这样的方式,我们就可以找到Content表中的
   // 关联信息     content.category.category_name 
});
MarkDown语法高亮

在HTML中直接使用






// marked相关配置
marked.setOptions({
    renderer: new marked.Renderer(),
    gfm: true,
    tables: true,
    breaks: false,
    pedantic: false,
    sanitize: true,
    smartLists: true,
    smartypants: false,
    highlight: function (code) {
        return hljs.highlightAuto(code).value;
    }
});

// MarkDown语法解析内容预览
$("#bjw-content").on("keyup blur", function () {
    $("#bjw-previous").html(marked($("#bjw-content").val()));
});

node环境中使用

// 在模板页面引入默认样式



const marked = require("marked");
const hljs = require("highlight.js");

// marked相关配置
marked.setOptions({
    renderer: new marked.Renderer(),
    gfm: true,
    tables: true,
    breaks: false,
    pedantic: false,
    sanitize: true,
    smartLists: true,
    smartypants: false,
    highlight: function (code) {
        return hljs.highlightAuto(code).value;
    }
});

// 对内容进行markdown语法转换
data.article_content_html = marked(article.content);
使文本域支持Tab缩进
$("#bjw-content").on("keydown",function(e){
    if(e.keyCode === 9){ // Tab键
         var position = this.selectionStart + 2; // Tab === 俩空格
       this.value = this.value.substr(0,this.selectionStart) + "  " + this.value.substr(this.selectionStart);
       this.selectionStart = position;
       this.selectionEnd = position;
       this.focus();
       e.preventDefault();
    }
});
layer 弹框
// 显示弹框
function showDialog(text, icon, callback) {
    layer.open({
        time: 1500,
        anim: 4,
        offset: "t",
        icon: icon,
        content: text,
        btn: false,
        title: false,
        closeBtn: 0,
        end: function () {
            callback && callback();
        }
    });
});
随机用户头像生成
// 引入对应的库
const crypto = require("crypto");
const identicon = require("identicon.js");

// 当用户注册时,根据用户的用户名生成随机头像
let hash = crypto.createHash("md5");
hash.update(username);
let imgData = new identicon(hash.digest("hex").toString());
let imgUrl = "data:/image/png;base64,"+imgData;
form表单提交的小问题

当使用form表单提交一些代码的时候,会出现浏览器拦截的现象,原因是:浏览器误以为客户进行xss攻击。所以呢解决这个问题也很简单,就是对提交的内容进行base64或者其他形式的编码,在服务器端进行解码,即可解决。

项目地址:https://github.com/bjw1234/blog

完结 撒花 ^_^ ~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93432.html

相关文章

  • node博客项目开发手记

    摘要:所以呢解决这个问题也很简单,就是对提交的内容进行或者其他形式的编码,在服务器端进行解码,即可解决。项目地址完结撒花 NodeJs开发个人博客项目 预览地址:http://baijiawei.top GitHub地址:https://github.com/bjw1234/blog 需要安装的模块 body-parser 解析post请求 cookies 读写cookie express...

    learn_shifeng 评论0 收藏0
  • 《Build your own AngularJS》手记

    摘要:最近在看,打算跟着书中的代码敲一遍,加深对的理解。在这里记录过程中的问题与心得。根据排查内存耗尽应该是这个版本的问题,换成后问题消失。因此认为这种写法是有风险的,必须用顶上那一行注释表明我确实要全局都的才行。不得不感叹的严谨。 最近在看 build your own angularjs ,打算跟着书中的代码敲一遍,加深对AngularJS的理解。在这里记录过程中的问题与心得。 Int...

    zsy888 评论0 收藏0
  • 阿里云轻量应用服务器部署Node项目手记

    摘要:本地和服务器环境本地位服务器阿里云轻量应用服务器使用的连接工具当然还有这几个中任选一个就好啦。连接工具使用方法创建填入服务器地址和用户名,点击确认然后输入密码即可。执行测试项目新建项目文件。使用编辑器打开项目文件。 本地和服务器环境 本地:Windows10 64位 服务器:阿里云轻量应用服务器CentOS 7.3 使用的连接工具:puttymobaxterm当然还有Xshell这几个...

    KaltZK 评论0 收藏0
  • 工作手记之移动端中文输入法触发oninput事件的解决方法

    摘要:经过在网上的查找,找到了和两个事件进行一个开关判断。关于事件是的标准事件,对于检测和这几个元素通过用户界面发生的内容变化非常有用,在内容修改后立即被触发,不像事件需要失去焦点才触发。补充最近测试了下发现在事件之后才触发。。。 事件背景 工作过程中涉及到了移动端输入内容长度的限定,这就要求需要对输入过程中内容的变化进行监控和判定,以决定是否可以继续输入,所以就想着是否可以在相关输入处监听...

    姘存按 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<