Nodejs之MEAN棧開發(四)---- form驗證及圖片上傳

這一節增長推薦圖書的提交和刪除功能,來學習node的form提交以及node的圖片上傳功能。開始以前須要源碼同窗能夠先在git上fork:https://github.com/stoneniqiu/ReadingClubjavascript

1、form驗證html

 MVC的form驗證有三個地方能夠作,第一道關就是前端提交以前,第二道關就是在數據保存以前,也就是在controller中作驗證,第三道關就是數據保存的時候,也就是若是提交的數據模型不符合實體定義的約束,數據是沒法保存的,這是最後一道防線。第一道關主要是依賴於js或者jquery框架,比較經常使用的是jquery.validate.js。若是是Asp.net MVC 能夠自動生成驗證規則,這裏就不細究了,網上有不少文章。第二層和各自的業務邏輯有關,也須要作一些必要驗證,防止前端禁止JavaScript,而提交不合法數據,這裏是要講基於Mongoose的第三層驗證。前端

1.回顧模型定義

咱們先回顧一下以前用Mongoose定義的book模型:html5

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驗證就基於此。java

2.添加路由

 咱們須要增長4個路由規則,2個用於添加(一個get,一個post),一個用於刪除,一個用於上傳圖片:node

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文件夾下。jquery

3.添加控制器方法

home.bookcreateview:git

module.exports.bookcreateview = function (req, res) {
    res.render('bookCreate', { title: '新增推薦圖書' });
};

這裏直接是一個get請求,因此直接用render去渲染視圖,固然這個bookCreate視圖接下來會建立。github

doBookCreate:ajax

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,
    });
};
View Code

上一節,咱們建立了數據操做的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')

這樣,刪除完成以後會隱藏當前行。下面解決圖片上傳問題。

2、圖片上傳

前面咱們在路由裏面定義了一個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

相關文章
相關標籤/搜索