这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能。开始之前需要源码同学可以先在git上fork:https://github.com/stoneniqiu/ReadingClub

一、form验证

MVC的form验证有三个地方可以做,第一道关就是前端提交之前,第二道关就是在数据保存之前,也就是在controller中做验证,第三道关就是数据保存的时候,也就是如果提交的数据模型不符合实体定义的约束,数据是无法保存的,这是最后一道防线。第一道关主要是依赖于js或者jquery框架,比较常用的是jquery.validate.js。如果是Asp.net MVC 可以自动生成验证规则,这里就不细究了,网上有很多文章。第二层和各自的业务逻辑有关,也需要做一些必要验证,防止前端禁止JavaScript,而提交不合法数据,这里是要讲基于Mongoose的第三层验证。

1.回顾模型定义

我们先回顾一下之前用Mongoose定义的book模型:

var bookSchema = new mongoose.Schema({
title: { type: String, required: true },
rating: {
type: Number,
required: true,
min: 0,
max: 5
},
info: { type: String, required: true },
img: String,
tags: [String],
brief: { type: String, required: true },
ISBN: String,
});

每个属性定义了类型和是否必须,还可以添加min,max,默认值等其他约束。如果提交的模型不满足这些约束,将不能保存成功。相当于Asp.net MVC中的DataAnnotations的作用。后面的form验证就基于此。

2.添加路由

我们需要增加4个路由规则,2个用于添加(一个get,一个post),一个用于删除,一个用于上传图片:

router.get('/book/create', homeController.bookcreateview);
router.post('/book/create', homeController.doBookCreate);
router.delete('/book/:id', homeController.delete);
router.post('/uploadImg', homeController.uploadImg);

基于Express的路由,我们可以创建Restful的路由规则。路由位于app_server文件夹下。

3.添加控制器方法

home.bookcreateview:

module.exports.bookcreateview = function (req, res) {
res.render('bookCreate', { title: '新增推荐图书' });
};

这里直接是一个get请求,所以直接用render去渲染视图,当然这个bookCreate视图接下来会创建。

doBookCreate:

module.exports.doBookCreate = function (req, res) {
var requestOptions, path, postdata;
path = "/api/book";
postdata = {
title: req.body.title,
info: req.body.info,
ISBN: req.body.ISBN,
brief: req.body.brief,
tags: req.body.tags,
img: req.body.img,
rating:req.body.rating,
};
requestOptions = {
url: apiOptions.server + path,
method: "POST",
json: postdata,
};
request(requestOptions, function (err, response, body) {
console.log("body.name", body.name, response.statusCode);
if (response.statusCode === 201) {
res.redirect("/detail/"+body._id);
}
else if (response.statusCode == 400 && body.name && body.name == "ValidationError") {
res.render('bookCreate', { title: '新增推荐图书', error:"val"});
}
else {
console.log("body.name",body.name);
info(res, response.statusCode);
}
});
};

info:

function info (res, status) {
var title, content;
if (status === 404) {
title = "404, 页面没有找到";
content = "亲,页面飞走了...";
} else if (status === 500) {
title = "500, 内部错误";
content = "尴尬...,发生错误";
} else {
title = status + ", 有什么不对劲";
content = "某些地方可能有些错误";
}
res.status(status);
res.render('info', {
title : title,
content : content,
status: status,
});
};

上一节,我们创建了数据操作的api部分。代码的流程就是先从req中获取到前端传过来的数据,然后用request模块调用api,如果添加成功(状态码是201)就返回到detail页面,如果验证失败,就原路返回,并给出提示。如果错误,交给info方法去处理。

delete:

module.exports.delete = function (req, res) {
var requestOptions, path;
path = "/api/book/" + req.params.id;
requestOptions = {
url: apiOptions.server + path,
method: "delete",
json: {},
};
request(requestOptions, function (err, response, body) {
if (response.statusCode == 204) {
res.json(1);
}
else {
res.json(0);
}
});
};

如果删除成功,返回的状态码是204,然后返回json(1)让前端去处理界面。

4.添加视图

1) 先需要在图书列表的右侧边栏增加一个按钮:

在books视图中修改:

   .col-md-3
.userinfo
p stoneniqiu
a(href='/book/create').btn.btn-info 新增推荐

当用户点击会跳转到/book/create页面

2)新增推荐页面:

extends layout
include _includes/rating block content
.row
.col-md-12.page.bookdetail
h3 新增推荐书籍
.row
.col-xs-12.col-md-6
form.form-horizontal(action='',method="post",role="form")
- if (error == "val")
.alert.alert-danger(role="alert") All fields required, please try again
.form-group
label.control-label(for='title') 书名
input#name.form-control(name='title')
.form-group
label.control-label(for='info') 信息
input#name.form-control(name='info')
.form-group
label.control-label(for='ISBN') ISBN
input#name.form-control(name='ISBN')
.form-group
label.control-label(for='brief') 简介
input#name.form-control(name='brief')
.form-group
label.control-label(for='tags') 标签
input#name.form-control(name='tags')
.form-group
label.control-label(for='rating') 推荐指数
select#rating.form-control.input-sm(name="rating")
option 5
option 4
option 3
option 2
option 1
.form-group
p 上传图片
a.btn.btn-info(id="upload", name="upload") 上传图片
br
img(id='img')
.form-group
button.btn.btn-primary(type='submit') 提交

if语句的地方是用来显示错误提示;图片上传,稍后完整介绍;所以提交页面基本长成这样:

3)Mongoose验证

这个时候没有加前端验证,form可以直接提交。但是node打印出了错误日志,Book validation failed,验证失败。

这是Mongoose给我们返回的验证信息,这时界面上回显示一个提示信息:

这是因为在controller中的处理:

  else if (response.statusCode == 400 && body.name && body.name == "ValidationError") {
res.render('bookCreate', { title: '新增推荐图书', error:"val"});
}

以上说明了Mongoose会在数据保存的时候验证实体,如果实体不满足path规则,将不能保存。但至此有三个问题,第一个问题是提示信息不明确,当然我们可以遍历输出ValidatorError;第二个就是,验证错误之后,页面原来的数据没有了,需要再输入一遍,这个我们可以参考Asp.net MVC将模型数据填充到视图中可以解决;第三个问题就是页面前端还没有验证,form直接就可以提交了,这个可以通过简单的Jquery脚本就可以做到;这三点先不细究。继续往下看,如果规范输入,这个时候是可以提交的,提交之后在books页面可以看到:

4)删除

在标题的右侧增加了一个删除符号(books视图中):

         .col-md-10
p
a(href="/Detail/#{book._id}")=book.title
span.close(data-id='#{book._id}') ×

并添加脚本:

$(".close").click(function() {
if (confirm("确定删除?")) {
var id = $(this).data("id");
var row = $(this).parents(".booklist");
$.ajax({
url: "/book/" + id,
method: "delete",
}).done(function(data) {
console.log(data);
row.fadeOut();
});
}
});

脚本可以先位于layout视图下方:

  script(src='/javascripts/books.js')

这样,删除完成之后会隐藏当前行。下面解决图片上传问题。

二、图片上传

前面我们在路由里面定义了一个uploadimg方法,现在实现它。一般都涉及两个部分,一个是前台图片的提交,一个是后端数据的处理。

1.uploadimg 方法实现

先需要安装formidable模块。

然后在Public文件下创建一个upload/temp文件夹

脚本:

var fs = require('fs');
var formidable = require('formidable');
module.exports.uploadImg = function (req, res) {
var form = new formidable.IncomingForm(); //创建上传表单
form.encoding = 'utf-8'; //设置编辑
form.uploadDir = './../public/upload/temp/'; //设置上传目录
form.keepExtensions = true; //保留后缀
form.maxFieldsSize = 3 * 1024 * 1024; //文件大小 form.parse(req, function(err, fields, files) {
console.log(files);
if (err) {
console.log(err);
return res.json(0);
}
for (var key in files) {
console.log(files[key].path);
var extName = ''; //后缀名
switch (key.type) {
case 'image/pjpeg':
extName = 'jpg';
break;
case 'image/jpeg':
extName = 'jpg';
break;
case 'image/png':
case 'image/x-png':
default:
extName = 'png';
break;
}
var avatarName = (new Date()).getTime() + '.' + extName;
var newPath = form.uploadDir + avatarName; fs.renameSync(files[key].path, newPath); //重命名
return res.json("/upload/temp/"+ avatarName);
}
}); };

这个form会自动将文件保存到upLoadDir目录,并以upload_xxxx格式重新命名,所以最后使用fs模块对文件进行重命名。然后返回给前端。

2.前端

我喜欢用插件,前端我用的是plupload-2.1.8,拥有多种上传方式,比较方便。放置在Public文件下。在layout.jade中引用js:

   script(src='/plupload-2.1.8/js/plupload.full.min.js')
script(src='/javascripts/books.js')

而在bookCreate.jade视图中,修改如下:

            a.btn.btn-info(id="upload", name="upload") 上传图片
br
img(id='img')
input#imgvalue(type='hidden',name='img',value='')

a标签用来触发上传,img用来预览,input用来存放路径。在books.js下增加以下代码:

var uploader = new plupload.Uploader({
runtimes: 'html5,flash,silverlight,html4',
browse_button: "upload",
url: '/uploadImg',
flash_swf_url: '/plupload-2.1.8/js/Moxie.swf',
silverlight_xap_url: '/plupload-2.1.8/js/Moxie.xap',
filters: {
max_file_size: "3mb",
mime_types: [
{ title: "Image files", extensions: "jpg,gif,png" },
{ title: "Zip files", extensions: "zip" }
]
},
init: {
PostInit: function () {
},
FilesAdded: function (up, files) {
plupload.each(files, function (file) {
uploader.start();
});
},
UploadProgress: function (up, file) {
},
Error: function (up, err) {
}
}
});
uploader.init();
uploader.bind('FileUploaded', function (upldr, file, object) {
var data = JSON.parse(object.response);
console.log(data);
$("#img").attr("src", data);
$("#imgvalue").val(data);
});

提交:

上传成功后跳转到detail页面。

至此,围绕form的提交这一节学习了Mongoose的数据验证,以及使用plupload上传,以及后端用formidable和fs模块处理图片。相对于Asp.net MVC而言,Asp.net MVC因为有自动化的form相对快捷一些。下一节将介绍Angular,作为MEAN中的A,该出场了。

源码:https://github.com/stoneniqiu/ReadingClub

Nodejs之MEAN栈开发(四)---- form验证及图片上传的更多相关文章

  1. form验证及图片上传

    form验证及图片上传 这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能.开始之前需要源码同学可以先在git上fork:https://github.com/ ...

  2. vuejs开发组件分享之H5图片上传、压缩及拍照旋转的问题处理

    一.前言 三年.net开发转前端已经四个月了,前端主要用webpack+vue,由于后端转过来的,前端不够系统,希望分享下开发心得与园友一起学习. 图片的上传之前都是用的插件(ajaxupload), ...

  3. form input file 图片上传360IE兼容问题

    <form action="" class="form-box" class="form_box" enctype="mul ...

  4. AntDesign vue学习笔记(七)Form 读写与图片上传

    AntDesign Form使用布局相比传统Jquery有点繁琐 (一)先读写一个简单的input为例 <a-form :form="form" layout="v ...

  5. HTML5 开发APP(打开相册以及图片上传)

    我们开发app,常常会遇到让用户上传文件的功能.比如让用户上传头像.我公司的业务要求是让用户上传支付宝收款二维码,来实现用户提现的功能.想要调用相册要靠HTML Plus来实现.先上效果图 基本功能是 ...

  6. Nodejs之MEAN栈开发(九)---- 用户评论的增加/删除/修改

    由于工作中做实时通信的项目,需要用到Nodejs做通讯转接功能,刚开始接触,很多都不懂,于是我和同事就准备去学习nodejs,结合nodejs之MEAN栈实战书籍<Getting.MEAN.wi ...

  7. Nodejs之MEAN栈开发(三)---- 使用Mongoose创建模型及API

    继续开扒我们的MEAN栈开发之路,前面两节我们学习了Express.Jade引擎并创建了几个静态页面,最后通过Heroku部署了应用. Nodejs之MEAN栈开发(一)---- 路由与控制器 Nod ...

  8. NodeJs实现图片上传

    关于formidable NodeJs实现图片上传,此处主要用了插件:formidable github上关于formidable的资料如下: https://github.com/felixge/n ...

  9. C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginator插件和knockout.js完成分页功能 图片在线裁剪和图片上传总结 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi

    C#设计模式总结 一. 设计原则 使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性.并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则.开放封闭原则.里氏代替 ...

随机推荐

  1. bzoj3048+3049+3050

    这套月赛题不是特别难 T1:离散化+单调队列,队列里出现数的种类不超过K+1,找最大的num[a[i]] T2:一眼可以看出BFS+状压DP,还要SPFA预处理出各个块之间的dis T3:线段树,没什 ...

  2. JDBC的连接和操作

    package Test; import java.sql.*; public class Test21 { public static void main(String[] args) { Conn ...

  3. iPhone6/6 Plus兩款大屏智能機

    蘋果終於順應時代潮流,於今年推出了iPhone6/6 Plus兩款大屏智能機.但很快就有人開始懷念老款iPhone的“一手掌控”,畢竟不是所有人都有一雙大手.不過近期就有傳言稱,蘋果將於明年重新推出一 ...

  4. Event,delegate,handler之间的关系

    在C#或者说.Net的事件处理机制中,有三个关键的概念 - 事件(Event),委托(Delegate)和处理器(Handler),另外在面向对象的背景中,还有另外两个概念 - 发送者(Sender) ...

  5. 验证码类库CaptchaMvc

    CaptchaMvc是一个有弹性的.简单的解决方案,它能够解决你项目中所有与验证码相关的问题.你需要做的所有事情就是向你的项目中添加一个类库,添加之后验证码就准备就绪了.该项目拥有使用验证码所需要的所 ...

  6. Expert 诊断优化系列------------------内存不够用么?

    现在很多用户被数据库的慢的问题所困扰,又苦于花钱请一个专业的DBA成本太高.软件维护人员对数据库的了解又不是那么深入,所以导致问题迟迟不能解决,或只能暂时解决不能得到根治.开发人员解决数据问题基本又是 ...

  7. Lesson 13 The Greenwood Boys

    Text The Greenwood Boys are group of pop singers. At present, they are visiting all parts of the cou ...

  8. ASP.NET MVC 控制器激活(三)

    ASP.NET MVC 控制器激活(三) 前言 在上个篇幅中说到从控制器工厂的GetControllerInstance()方法来执行控制器的注入,本篇要讲是在GetControllerInstanc ...

  9. hibernate学习笔记之三 持久化的三种状态

    Hibernate持久化对象有3中状态,瞬时对象(transientObjects),持久化对象(persistentObjects),离线对象(detachedObjects) 下图显示持久化三种状 ...

  10. iOS-----写一个规范的单例--->

    1.集成了一个宏 2.两句代码集成单例 3.一句代码调用单例 -------------> 1.集成了一个宏 //这里就要注意了,因为每个单例中,方法名可以不一样,那么我们就不能把名字写死,要灵 ...