国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

通過nodeclub項目源碼來講解如何做一個nodejs + express + mongodb項目

kaka / 2650人閱讀

摘要:是的源碼,算是一個基本的博客系統(tǒng),包含文章發(fā)布,關(guān)注,評論等功能。這些功能可以說是任何一個網(wǎng)站的基礎(chǔ)。比如運營數(shù)據(jù)配置和其他數(shù)據(jù)配置分開,因為很有可能需要做一個小的工具來讓非技術(shù)人員配置相關(guān)參數(shù)。模式在中有一個專門的章節(jié)來講解。

1. About

1.1 what:
nodeclub是cnodejs.com的源碼,cnode算是一個基本的博客系統(tǒng),包含文章發(fā)布, 關(guān)注,評論等功能。這些功能可以說是任何一個網(wǎng)站的基礎(chǔ)。從nodeclub里可以學(xué)到什么?

1.基本的架構(gòu)

2.開發(fā)測試過程

3.MVC的設(shè)計

4.middleware 的正確用法

5.如何設(shè)計mongodb schema

6.如何正確的使用mongoose

7.如何實現(xiàn)一個標簽系統(tǒng)

8.plugins? services ?

9.如何正確的使用ejs helper

10.到底該怎樣寫路由, restful?

11.如何做基本的控制驗證

12.如何發(fā)郵件

13.session

14.github 用戶登錄

15.圖片上傳

16.消息發(fā)送

除了nodeclub源碼的學(xué)習(xí)筆記以外, 還會有一點最近搗鼓這一塊的經(jīng)驗分享

1.一個完整的消息訂閱設(shè)計

2.消息推送, socket + express如何合作?

3.包裝action

4.蛋疼的異步回調(diào)如何處理

nodeclub源碼

1.2 why:
對于想用nodejs + express + mongodb 來做網(wǎng)站技術(shù)基礎(chǔ)的項目, nodeclub可以說是很好的源碼級指南,當然也是我的指南,這篇文章權(quán)當做個人學(xué)習(xí)nodeclub的學(xué)習(xí)筆記。

1.3 who

  who = 一名本應(yīng)該在寫前端的但不知怎的一直在寫后端的馬膿 -> 
    @echo "github: https://github.com/6174"
    @echo "weibo: http://weibo.com/u/2254313183"
    @echo "email: 57017125@qq.com"
    @echo "ps: 一直在求后端partner中,有意者聯(lián)系我" 
    @send()
2. nodeclude 中用到了哪些開源技術(shù)

2.1 nodejs項目一大優(yōu)點就是有一個package.json, 里邊的dependencies & devDependencies可以看到這個項目所有的依賴。 對于有經(jīng)驗的開發(fā)者來說, 看完package.json基本就能知道項目的架構(gòu)是怎樣。

2.2 dependencies

express: 基礎(chǔ)框架:

mongodb: 數(shù)據(jù)存儲

mongoose: orm

connect-mongo: session (對于redis, 可以使用connect-redis)

nodemailer:郵件

validator:驗證

passportpassport-github: passport,

loader: ejs-view-helper, 靜態(tài)資源加載處理

其他: event-proxy, node-markdown, ndir

2.3 devDependencies

測試框架:mocha, should

運行: forever

請求模擬: supertest

2.4 nodeclub以express + mongodb + mongoose作為基本框架, 典型的MVC應(yīng)用

Model: 對應(yīng)mongoose orm, models目錄

view: ejs模板, views目錄

controler:express middleware , contollers目錄

2.5 目錄結(jié)構(gòu):

- common/
- controllers/
- libs/
# express中間件, 基本的auth, session 驗證
- middlewares/
- models/
#消息, 郵件服務(wù)
- services/
- plugins/
#可以看做是對model處理的加工庫
- proxy/
- test/
- views/
- app.js
- route.js
- config.js

3. 應(yīng)用入口app.js

神圣的入口文件,幾乎每個項目都會有一個entry,對于了解一個應(yīng)用熟悉入口邏輯很重要。 下面將分步來看看,nodeclub的app.js做了什么:

3.1 require(./config)

3.1.1 應(yīng)用相關(guān)的配置的設(shè)置, 主要分為

1.應(yīng)用全局數(shù)據(jù)配置

2.數(shù)據(jù)庫連接配置

3.session,auth相關(guān)配置

4.rss配置

5.mail配置

6.第三方連接相關(guān)配置, github, weibo

配置文件也是了解應(yīng)用的一個好地方, 在config.default.js中可以看到以下信息, 這些很可能是我們平時做應(yīng)用開發(fā)的時候沒有留意到的地方

  //--應(yīng)用數(shù)據(jù)統(tǒng)計
  google_tracker_id: "UA-41753901-5",

  //--靜態(tài)文件很可能使用cdn來做
  site_static_host: "", // 靜態(tài)文件存儲域名

  //--求解釋
  site_enable_search_preview: false, // 開啟google search preview
  site_google_search_domain:  "cnodejs.org",  // google search preview中要搜索的域名

  //--運營數(shù)據(jù)
  list_topic_count: 20,
  post_interval: 10000,
  admins: { admin: true },
  side_ads:[]
  allow_sign_up: true,

  //--插件模式
  plugins: []

3.1.2 當然這里的配置文件是default的,配置文件可以放在一個config的文件夾下面,多個文件的方式來整理。比如運營數(shù)據(jù)配置和其他數(shù)據(jù)配置分開,因為很有可能需要做一個小的工具來讓非技術(shù)人員配置相關(guān)參數(shù)。這時候可以用一個index.js作為facade,相當于一個大的node module。

3.2 require("./models")

3.2.1 之前已經(jīng)講了models目錄對應(yīng)MVC的M部分。

3.2.2 models目錄下面有index.js, require("./models")相當于require("./models/index")
index相當于一個模型的facade, index.js做得事情分別是

1.connect mongodb

2.require各個model模塊

3.exports 所有的model
簡單而言就是初始化了應(yīng)用model層。

3.2.3 模型使用orm框架mogoose來寫,了解mogoose過后, models部分的代碼也就是秒懂了
, 我說的只是代碼,literaly, 一個項目的核心就是model的設(shè)計,以前做過的任何項目都是一樣, 數(shù)據(jù)庫table的設(shè)計好壞直接影響應(yīng)用的開發(fā)以及性能。 下面來看看各個model的schema設(shè)計(幾乎直接ctr+c, ctr+v加上了一點點注釋) :

3.2.4 user

    var UserSchema = new Schema({
          //--基本用戶信息, index表示在mongodb中會建立索引
          //--unique: true 唯一性設(shè)置
          name: { type: String, index: true },
          loginname: { type: String, unique: true },
          pass: { type: String },
          email: { type: String, unique: true },
          url: { type: String },
          profile_image_url: {type: String},
          location: { type: String },
          signature: { type: String },
          profile: { type: String },
          weibo: { type: String },
          avatar: { type: String },
          githubId: { type: String, index: true },
          githubUsername: {type: String},
          is_block: {type: Boolean, default: false},

          //--用戶產(chǎn)生數(shù)據(jù)meta
          score: { type: Number, default: 0 },
          topic_count: { type: Number, default: 0 },
          reply_count: { type: Number, default: 0 },
          follower_count: { type: Number, default: 0 },
          following_count: { type: Number, default: 0 },
          collect_tag_count: { type: Number, default: 0 },
          collect_topic_count: { type: Number, default: 0 },
          create_at: { type: Date, default: Date.now },
          update_at: { type: Date, default: Date.now },
          is_star: { type: Boolean },
          level: { type: String },
          active: { type: Boolean, default: true },

          //-mail
          receive_reply_mail: {type: Boolean, default: false },
          receive_at_mail: { type: Boolean, default: false },
          from_wp: { type: Boolean },
          retrieve_time : {type: Number},
          retrieve_key : {type: String}
        });

3.2.5 topic 話題


//1 <- 多 //tag <- topic <- collect var TopicSchema = new Schema({ title: { type: String }, content: { type: String }, author_id: { type: ObjectId }, top: { type: Boolean, default: false }, reply_count: { type: Number, default: 0 }, visit_count: { type: Number, default: 0 }, collect_count: { type: Number, default: 0 }, create_at: { type: Date, default: Date.now }, update_at: { type: Date, default: Date.now }, //--這里reply的設(shè)計方式不知道是否合適, 因為mongdb不同于關(guān)系型數(shù)據(jù)庫,這里每次讀取文章都需要重reply集合里邊查找遍歷一邊,文章是讀繁忙的。 //-- 一個document的大小為5Mb, 一本牛津詞典的內(nèi)容, 我覺得將reply放在這里應(yīng)該不會有太大問題。 即便不存放reply 內(nèi)容, 存放一個id數(shù)組也會好很多。 //-- 客官們怎么看? last_reply: { type: ObjectId }, last_reply_at: { type: Date, default: Date.now }, content_is_html: { type: Boolean } }); var ReplySchema = new Schema({ content: { type: String }, topic_id: { type: ObjectId, index: true }, author_id: { type: ObjectId }, reply_id : { type: ObjectId }, create_at: { type: Date, default: Date.now }, update_at: { type: Date, default: Date.now }, content_is_html: { type: Boolean } }); //--話題集合 var TopicCollectSchema = new Schema({ user_id: { type: ObjectId }, topic_id: { type: ObjectId }, create_at: { type: Date, default: Date.now } }); //--話題標簽 var TopicTagSchema = new Schema({ topic_id: { type: ObjectId }, tag_id: { type: ObjectId }, create_at: { type: Date, default: Date.now } });

3.2.6 tag
標簽系統(tǒng)

        //tag <- collect
        var TagSchema = new Schema({
          name: { type: String },
          order: { type: Number, default: 1 },
          description: { type: String },
          background: { type: String },
          topic_count: { type: Number, default: 0 },
          collect_count: { type: Number, default: 0 },
          create_at: { type: Date, default: Date.now }
        });

        var TagCollectSchema = new Schema({
          user_id: { type: ObjectId, index: true },
          tag_id: { type: ObjectId },
          create_at: { type: Date, default: Date.now }
        });

3.2.7 關(guān)系

        var RelationSchema = new Schema({
          user_id: { type: ObjectId },
          follow_id: { type: ObjectId },
          create_at: { type: Date, default: Date.now }
        });

3.2.8 消息
消息model設(shè)計, 對于一個blog來說, 基本的只有回復(fù)消息, 這里加了關(guān)注和@消息。

    /*
     * type:
     * reply: xx 回復(fù)了你的話題
     * reply2: xx 在話題中回復(fù)了你
     * follow: xx 關(guān)注了你
     * at: xx @了你
     */
    var MessageSchema = new Schema({
      type: { type: String },
      master_id: { type: ObjectId, index: true },
      author_id: { type: ObjectId },
      topic_id: { type: ObjectId },
      reply_id: { type: ObjectId },
      has_read: { type: Boolean, default: false },
      create_at: { type: Date, default: Date.now }
    });
3.3 require middlewares

3.3.1 express的基礎(chǔ)是middleware,或者說express的基礎(chǔ)是connect,connect的基礎(chǔ)是middleware。middleware模式在professional nodejs中有一個專門的章節(jié)來講解。何為middleware呢? middleware模式 相當于一個加工流水線(大家叫middleware stack),每一個middleware相當于一個加工步驟,當出現(xiàn)一個http請求的時候,http請求會挨著每個middleware執(zhí)行下去。
express里處理一個請求的過程基本上就是請求通過middleware stack的過程: * -> middlewares -> 路由 -> controllers -> errorhandlering。

3.3.2 middleware 怎樣做到的, 異步的方法呢? middleware使用promise的方式來處理異步,所有每個middleware都有三個參數(shù)req, res, next, 對于異步的情況, 必須要調(diào)用next()方法。不然后續(xù)的middleware就無法執(zhí)行。 ps: debug 的時候沒調(diào)用next()還不會報錯,一定注意

3.3.3 auth.js
auth.js exports出來的函數(shù)全部都是中間件,從變量名就完全清楚的知道到底在做什么了


//-- 需要admin權(quán)限 exports.adminRequired = function (req, res, next) {} //-- 需要有用戶 exports.userRequired = function (req, res, next) {} //-- 需要有用戶并登錄 exports.signinRequired = function (req, res, next) { if (!req.session.user) { res.render("notify/notify", {error: "未登入用戶不能發(fā)布話題。"}); return; } next(); } //-- 屏蔽用戶 -_- exports.blockUser = function (req, res, next) {}

這里其實就可以看到中間件的作用了,我們以前寫php的時候每次都需要判斷用戶是否登錄, 沒登陸redirect到index.php ,只不過這里的方式是通過中間件來處理。
明白這里什么意思,其他的中間件模塊也就秒懂了。

3.4 require("./routes")

3.4.1 express 的世界里另外一個很重要的就是route, nodejs啟動的是服務(wù), 監(jiān)聽了某一端口, 接受http or https or sockt請求, 那url中像"/index.php?blabla"這一串的存在怎么處理呢, express的route功能就可以幫我們解析。

3.4.2 MVC中如何將一個請求和controller聯(lián)系起來呢, route就是這樣的紐帶

  //--get, post 請求
  app.get("/signin", sign.showLogin);
  app.post("/signin", sign.login);
  //--使用中間件
  app.get("/signup", configMiddleware.github, passport.authenticate("github"));
  app.post("/:topic_id/reply", auth.userRequired, limit.postInterval, reply.add);

3.4.3 route是了解一個應(yīng)用最佳的地方,一個請求如何處理, 到相應(yīng)的controller去看就知道了。 相比起在PHP環(huán)境下配置更加靈活。當然你說你通過nginx來配置也很靈活,好吧,我們說的不是一回事。

3.5 initialization

3.5.1 experess initialize: app.js 中其他大多部分就是express的初始化了, 初始化流程如下:

1.配置上傳 upload_dir

2.模板引擎設(shè)置

3.express通用中間件設(shè)置

4.pasport中間件

5.自定義中間件

1.auth_user

2.block_user

3.staticfile: upload

4.staticfile: user_data

6.csrf

7.errorhandler

8.set view cache

@Note:配置的順序很重要, 中間件的執(zhí)行順序是按照定義順序來執(zhí)行的, 如果一個中間件依賴另外的中間件, 而自己先執(zhí)行了, 這種情況就會錯誤。 常見的問題就是session配置, 一定要記得配置session中間件的時候, 要先配置cookieParser。

3.5.2 session設(shè)置
這個步驟在initialize里邊已經(jīng)有了, 不過再多帶帶講一下, nodeclub使用的是connect-mongo來作為session的存儲

    //--cookieParser一定要在前面, 因為session的設(shè)置依賴cookie
    app.use(express.cookieParser());
    app.use(express.session({
      secret: config.session_secret,
      store: new MongoStore({
        db: config.db_name,
      }),
    }));

3.5.3 view helpers
使用過ejs的肯定知道, ejs里邊view helper設(shè)置很簡單, 就像賦值變量一樣。 當對于一些通用的helper可以這樣設(shè)置:

        app.helpers({
          config: config,
          Loader: Loader,
          assets: assets
        });
        app.dynamicHelpers(require("./common/render_helpers"));

3.5.4 github pasport initialize

        // github oauth
        passport.serializeUser(function (user, done) {
          done(null, user);
        });
        passport.deserializeUser(function (user, done) {
          done(null, user);
        });
        passport.use(new GitHubStrategy(config.GITHUB_OAUTH, 
  githubStrategyMiddleware));

3.5.5 start app

4. 用戶注冊

4.1 user 是每個應(yīng)用都會處理的基本, 注冊登錄登出, 看看nodeclub做了哪些事情:

4.2 路由:

  //--設(shè)置能否直接注冊, 不能的話通過github注冊
  if (config.allow_sign_up) {
    app.get("/signup", sign.showSignup);
    app.post("/signup", sign.signup);
  } else {
    app.get("/signup", configMiddleware.github, passport.authenticate("github"));
  }
  app.post("/signout", sign.signout);
  app.get("/signin", sign.showLogin);
  app.post("/signin", sign.login);

4.3 controller & model:sign.signup

    sanitize = validator.sanitize;
    check = validator.check;
    exports.signup = function (req, res, next) {
      //--xss 消毒
      var name = sanitize(req.body.name).trim();
      name = sanitize(name).xss();
      ...
      //--validations
      try {
        check(name, "用戶名只能使用0-9,a-z,A-Z。").isAlphanumeric();
      } catch (e) {
        res.render("sign/signup", {error: e.message, name: name, email: email});
        return;
      }
      ...
      //--用用戶名登錄或者email登錄
      query = {"$or": [{"loginname": loginname}, {"email": email}]}
      User.getUserByQuery(query, {}, function(){
        ...
        pass = md5(pass);
        ...
        User.newAndSave(name, loginname, pass, email, avatar_url, false, function (err) {
          ...
          // 發(fā)送激活郵件
          mail.sendActiveMail(email, md5(email + config.session_secret), name);
          res.render("sign/signup", {
            success: "歡迎加入 " + config.name + "!我們已給您的注冊郵箱發(fā)送了一封郵件,請點擊里面的鏈接來激活您的帳號。"
          });
        })
      })
    }   
5. mongoose 的使用

5.1 使用User.newAndSave,

5.2 異步 callback pyramid
一個應(yīng)用通常會遇到這樣的情景, 一個頁面需要的數(shù)據(jù)包括, 文章列表, 評論列表,用戶數(shù)據(jù),廣告數(shù)據(jù), other stuff... 問題是每個都是異步的, 怎么辦。 user數(shù)據(jù)獲取過后的callback調(diào)用文章列表獲取, 文章列表獲取的callback調(diào)用評論列表的獲取... 這樣就太蛋疼了。 nodeclub使用了eventproxy模塊優(yōu)雅的解決這樣的問題:

    render = function(){}
    var proxy = EventProxy.create("tags", "topics", "hot_topics", "stars", "tops", "no_reply_topics", "pages", render);
    proxy.fail(next);
    Tag.getAllTags(proxy.done("tags"));
    Topic.getTopicsByQuery(query, options, proxy.done("topics"));
    User.getUsersByQuery({ is_star: true }, { limit: 5 }, proxy.done("stars"));

看完代碼不言而喻。。。
當然異步處理的方法有很多:
- 1.基于事件的:eventProxy
- 2.基于promise的:Async.js Q.js, when.js
- 3.基于編譯的:continuation, wind
- 4.基于語言語法的:yield, livescript
文章最后會講一下我我的異步選擇方案

6. 消息

6.1 原先以為有動態(tài)的消息推送, 有隊列處理, 錯了, 木有

6.2 在sublime text里邊全局搜索sendReply2Message會發(fā)現(xiàn)是在controller/reply.js里邊調(diào)用的, 也就是說,消息是直接觸發(fā)的。

6.3 好吧, 這部分大概大家都能秒懂。。

7. 開發(fā) 7.1 測試

7.1.1 一個項目必定離不開測試, nodeclub基于mocha BDD測試框架, 一切的前提假設(shè)至少能看懂jasmine或者mocha或者任何一個BDD風格的測試代碼。

7.1.2 打開即看到app.js

  var app = require("../app");
  describe("app.js", function () {
    //--before, 執(zhí)行it的前面會執(zhí)行
    before(function (done) {
      //--done, 異步方法
      app.listen(3001, done);
    });
    after(function () {
      app.close();
    });
    it("should / status 200", function (done) {
      //--使用 app.request()就可以模擬請求了? 這個api哪里來的, 求解釋?
      app.request().get("/").end(function (res) {
        res.should.status(200);
        done();
      });
    });
  });
  //--按理說應(yīng)該是可以正常運行了但是我一直出現(xiàn)這個錯誤:
  //--connect ADDRNOTAVAIL 知道的求解釋
  //--我嘗試用supertest直接測試, 但是也是一直timeout, mocha
  //--里邊加大timeout時間, 結(jié)果就是一直沒反應(yīng)。 

  //--分析原因, express版本問題, nodeclub中express的版本還是2.x, 所以才會有
  //--app.request(), app.close()這些api
  //--第二個原因, 到supertest官網(wǎng), 發(fā)現(xiàn)人家都已經(jīng)轉(zhuǎn)戰(zhàn)到superagent項目了, 于是我寫了下面這個測試腳本, 可以通過了
  var express = require("express");
  var should = require("should");
  var path = require("path");
  var superagent = require("superagent");
  var app = express()
  app.get("/user", function(req, res, next) {
      res.send(200, {
          name: "tobi"
      })
  })
  describe("myapp.js", function() {
      this.timeout(5000)
      before(function(done) {
          app.listen(21, done);
      })
      after(function() {
          // app.close()
      })
      it("should /status 200", function(done) {
          agent = superagent.agent()
          agent.get("http://localhost:21/user").end(function(err, res) {
            console.log(err, res)
            res.should.have.status(200);
            res.text.should.include("tobi");
            return done();
          });
      })
  })
7.2 運行

nodejs是單線程應(yīng)用, 如果我們用node命令來運行我們的應(yīng)用, 當出現(xiàn)一個小錯誤, 它就掛了。 然后沒有然后了。 避免這種問題的方法有如下工具:

1.forever

2.nodemon

3.supervisor
nodeclub 使用forever來運行項目, 使用這類工具的好處就是, 當有代碼改動過后, 會自動的重啟應(yīng)用。 不必每次自己去運行node *.js

8. 說說自己的經(jīng)驗

待續(xù)...

8.1 消息訂閱設(shè)計 8.2 express + socket 8.3 異步 8.4 Action

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/18710.html

相關(guān)文章

  • [Node + Docker] 聊聊怎么把 nodeclub 構(gòu)建成 Docker 鏡像

    摘要:但是,命名約定為全部大寫。命令可以多次使用,表示會創(chuàng)建多個鏡像。現(xiàn)在可以開始構(gòu)建鏡像了,安裝比較蛋疼,我本地沒有安裝環(huán)境,我用的是時速云的本地客戶端,安裝配置都比較簡單,這里就不說了,大家可以參考官方文檔。 14年畢業(yè)后開始接觸node,15年來帝都找了份工作,一直默默的在cnode社區(qū)晃悠,灌過幾次水,今天就想發(fā)個處女貼,跟大家聊聊怎么把nodeclub項目源碼構(gòu)建成一個鏡像。話說D...

    sanyang 評論0 收藏0
  • node.js中文資料導(dǎo)航

    摘要:中文資料導(dǎo)航官網(wǎng)七牛鏡像深入淺出系列進階必讀中文文檔被誤解的編寫實戰(zhàn)系列熱門模塊排行榜,方便找出你想要的模塊多線程,真正的非阻塞淺析的類利用編寫異步多線程的實例中與的區(qū)別管道拒絕服務(wù)漏洞高級編程業(yè)界新聞看如何評價他們的首次嘗鮮程序員如何說服 node.js中文資料導(dǎo)航 Node.js HomePage Node官網(wǎng)七牛鏡像 Infoq深入淺出Node.js系列(進階必讀) Nod...

    geekidentity 評論0 收藏0
  • 全棧最后一公里 - Node.js 項目的線上服務(wù)器部署與發(fā)布

    摘要:沒有耐心閱讀的同學(xué),可以直接前往學(xué)習(xí)全棧最后一公里。我下面會羅列一些,我自己錄制過的一些項目,或者其他的我覺得可以按照這個路線繼續(xù)深入學(xué)習(xí)的項目資源。 showImg(https://segmentfault.com/img/bVMlke?w=833&h=410); 本文技術(shù)軟文,閱讀需謹慎,長約 7000 字,通讀需 5 分鐘 大家好,我是 Scott,本文通過提供給大家學(xué)習(xí)的方法,...

    Nosee 評論0 收藏0
  • Node.js 入門你需要知道的 10 個問題

    摘要:什么是在中什么時候需要是中的包管理器。允許我們?yōu)榘惭b各種模塊,這個包管理器為我們提供了安裝刪除等其它命令來管理模塊。 showImg(https://user-gold-cdn.xitu.io/2019/7/11/16bde5b2df52a924?w=4000&h=2667&f=jpeg&s=450648); 本文為您分享「Node.js 入門你需要知道的 10 個問題」這些問題可能也...

    szysky 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<