<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 第十四步:Express API 路由(2/2) 讓我們回到server.js。我希望現在你已經明白下面這些路由該放在哪里——在Express中間件后面和React中間件前面。 > 注意:請理解我們這里將所有的路由都放在server.js,是為了這個教程的方便。在我工作期間所構建的儀表盤項目里,所有的路由都被拆開分散到不同的文件,并放在routes目錄下面,并且,所有的路由處理程序也都被打散,分成不同的文件放到controllers目錄下。 讓我們以獲取Home組件中兩個角色的路由作為開始。 ### GET /api/characters ~~~ /** * GET /api/characters * Returns 2 random characters of the same gender that have not been voted yet. */ app.get('/api/characters', function(req, res, next) { var choices = ['Female', 'Male']; var randomGender = _.sample(choices); Character.find({ random: { $near: [Math.random(), 0] } }) .where('voted', false) .where('gender', randomGender) .limit(2) .exec(function(err, characters) { if (err) return next(err); if (characters.length === 2) { return res.send(characters); } var oppositeGender = _.first(_.without(choices, randomGender)); Character .find({ random: { $near: [Math.random(), 0] } }) .where('voted', false) .where('gender', oppositeGender) .limit(2) .exec(function(err, characters) { if (err) return next(err); if (characters.length === 2) { return res.send(characters); } Character.update({}, { $set: { voted: false } }, { multi: true }, function(err) { if (err) return next(err); res.send([]); }); }); }); }); ~~~ 別忘了在最頂部添加[Underscore.js](http://underscorejs.org/)模塊,因為我們需要使用它的幾個函數`_.sample()`、`_.first()`和`_.without()`。 ~~~ var _ = require('underscore'); ~~~ 我已經盡力讓這段代碼易于理解,所以你應該很清楚如何獲取兩個隨機角色。它將隨機選擇Male或Female性別并查詢數據庫以獲取兩個角色,如果獲得的角色少于2個,它將嘗試用另一個性別進行查詢。比如,如果我們有10個男性角色但其中9個已經被投票過了,只顯示一個角色沒有意義。如果無論是男性還是女性角色查詢返回都不足兩個角色,說明我們已經耗盡了所有未投票的角色,應該重置投票計數,通過設置所有角色的`voted:false`即可辦到。 ### PUT /api/characters 這個路由和前一個相關,它會分別更新獲勝的`wins`字段和失敗角色的`losses`字段。 ~~~ /** * PUT /api/characters * Update winning and losing count for both characters. */ app.put('/api/characters', function(req, res, next) { var winner = req.body.winner; var loser = req.body.loser; if (!winner || !loser) { return res.status(400).send({ message: 'Voting requires two characters.' }); } if (winner === loser) { return res.status(400).send({ message: 'Cannot vote for and against the same character.' }); } async.parallel([ function(callback) { Character.findOne({ characterId: winner }, function(err, winner) { callback(err, winner); }); }, function(callback) { Character.findOne({ characterId: loser }, function(err, loser) { callback(err, loser); }); } ], function(err, results) { if (err) return next(err); var winner = results[0]; var loser = results[1]; if (!winner || !loser) { return res.status(404).send({ message: 'One of the characters no longer exists.' }); } if (winner.voted || loser.voted) { return res.status(200).end(); } async.parallel([ function(callback) { winner.wins++; winner.voted = true; winner.random = [Math.random(), 0]; winner.save(function(err) { callback(err); }); }, function(callback) { loser.losses++; loser.voted = true; loser.random = [Math.random(), 0]; loser.save(function(err) { callback(err); }); } ], function(err) { if (err) return next(err); res.status(200).end(); }); }); }); ~~~ 這里我們使用[`async.parallel`](https://github.com/caolan/async#paralleltasks-callback)來同時進行兩個數據庫查詢,因為這兩個查詢并不相互依賴。不過,因為我們有兩個獨立的MongoDB文檔,還要進行兩個獨立的異步操作,因此我們還需要另一個`async.parallel`。一般來說,我們僅在兩個角色都完成更新并沒有錯誤后給出一個success的響應。 ### GET /api/characters/count MOngoDB有一個內建的`count()`方法,可以返回所匹配的查詢結果的數量。 ~~~ /** * GET /api/characters/count * Returns the total number of characters. */ app.get('/api/characters/count', function(req, res, next) { Character.count({}, function(err, count) { if (err) return next(err); res.send({ count: count }); }); }); ~~~ > 注意:從這個返回總數量的一次性路由上,你可能注意到我們開始與RESTful API設計模式背道而馳。很不幸這就是現實。我還沒有在一個能完美實現RESTful API的項目中工作過,你可以參看Apigee寫的[這篇文章](https://blog.apigee.com/detail/restful_api_design_what_about_counts)來進一步了解為什么會這樣。 ### GET /api/characters/search 我上次檢查時MongoDB還不支持大小寫不敏感的查詢,所以這里我們需要使用正則表達式,不過還好MongoDB提供了[`$regex`](http://docs.mongodb.org/manual/reference/operator/query/regex/)操作符。 ~~~ /** * GET /api/characters/search * Looks up a character by name. (case-insensitive) */ app.get('/api/characters/search', function(req, res, next) { var characterName = new RegExp(req.query.name, 'i'); Character.findOne({ name: characterName }, function(err, character) { if (err) return next(err); if (!character) { return res.status(404).send({ message: 'Character not found.' }); } res.send(character); }); }); ~~~ ### GET /api/characters/:id 這個路由是供角色資料頁面使用的(我們將在下一節創建角色組件),教程最開始的圖片就是這個頁面。 ~~~ /** * GET /api/characters/:id * Returns detailed character information. */ app.get('/api/characters/:id', function(req, res, next) { var id = req.params.id; Character.findOne({ characterId: id }, function(err, character) { if (err) return next(err); if (!character) { return res.status(404).send({ message: 'Character not found.' }); } res.send(character); }); }); ~~~ 當我開始構建這個項目時,我大概有7-9個幾乎相同的路由來檢索Top 100的角色。在經過一些代碼重構后我僅留下了下面這一個: ~~~ /** * GET /api/characters/top * Return 100 highest ranked characters. Filter by gender, race and bloodline. */ app.get('/api/characters/top', function(req, res, next) { var params = req.query; var conditions = {}; _.each(params, function(value, key) { conditions[key] = new RegExp('^' + value + '$', 'i'); }); Character .find(conditions) .sort('-wins') // Sort in descending order (highest wins on top) .limit(100) .exec(function(err, characters) { if (err) return next(err); // Sort by winning percentage characters.sort(function(a, b) { if (a.wins / (a.wins + a.losses) < b.wins / (b.wins + b.losses)) { return 1; } if (a.wins / (a.wins + a.losses) > b.wins / (b.wins + b.losses)) { return -1; } return 0; }); res.send(characters); }); }); ~~~ 比如,如果我們對男性、種族為Caldari、血統為Civire的Top 100角色感興趣,你可以構造這樣的URL路徑: ~~~ GET /api/characters/top?race=caldari&bloodline=civire&gender=male ~~~ ![](https://box.kancloud.cn/2015-09-14_55f6442ee5332.jpg) 如果你還不清楚如何構造`conditions`對象,這段經過注釋的代碼應該可以解釋: ~~~ // Query params object req.query = { race: 'caldari', bloodline: 'civire', gender: 'male' }; var params = req.query; var conditions = {}; // This each loop is equivalent... _.each(params, function(value, key) { conditions[key] = new RegExp('^' + value + '$', 'i'); }); // To this code conditions.race = new RegExp('^' + params.race + '$', 'i'); // /caldari$/i conditions.bloodline = new RegExp('^' + params.bloodline + '$', 'i'); // /civire$/i conditions.gender = new RegExp('^' + params.gender + '$', 'i'); // /male$/i // Which ultimately becomes this... Character .find({ race: /caldari$/i, bloodline: /civire$/i, gender: /male$/i }) ~~~ 在我們取回獲勝數最多的角色后,我們會對勝率進行一個排序,不讓最老的角色始終顯示在前面。 ### GET /api/characters/shame 和前一個路由差不多,這個路由會取回失敗最多的100個角色: ~~~ /** * GET /api/characters/shame * Returns 100 lowest ranked characters. */ app.get('/api/characters/shame', function(req, res, next) { Character .find() .sort('-losses') .limit(100) .exec(function(err, characters) { if (err) return next(err); res.send(characters); }); }); ~~~ ### POST /api/report 有些角色沒有一個有效的avatar(一般是灰色輪廓),另有些角色的avatar是漆黑一片,它們在一開始就不應該添加到數據庫中。但因為任何人都能添加任何角色,因此有些時候你需要從數據庫移除一些異常角色。這里設置當一個角色被訪問者舉報4次后將被刪除。 ~~~ /** * POST /api/report * Reports a character. Character is removed after 4 reports. */ app.post('/api/report', function(req, res, next) { var characterId = req.body.characterId; Character.findOne({ characterId: characterId }, function(err, character) { if (err) return next(err); if (!character) { return res.status(404).send({ message: 'Character not found.' }); } character.reports++; if (character.reports > 4) { character.remove(); return res.send({ message: character.name + ' has been deleted.' }); } character.save(function(err) { if (err) return next(err); res.send({ message: character.name + ' has been reported.' }); }); }); }); ~~~ ### GET /api/stats 最后,為角色的統計創建一個路由。是的,下面的代碼可以用[`async.each`](https://github.com/caolan/async#each)或[`promises`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)來簡化,不過記住,我在兩年前開始創建New Eden Faces時對這些方案還不熟悉,到現在絕大部分的后端代碼沒怎么動過。不過即使這樣,這些代碼還是足夠魯棒,最少它很明確并且易讀。 ~~~ /** * GET /api/stats * Returns characters statistics. */ app.get('/api/stats', function(req, res, next) { async.parallel([ function(callback) { Character.count({}, function(err, count) { callback(err, count); }); }, function(callback) { Character.count({ race: 'Amarr' }, function(err, amarrCount) { callback(err, amarrCount); }); }, function(callback) { Character.count({ race: 'Caldari' }, function(err, caldariCount) { callback(err, caldariCount); }); }, function(callback) { Character.count({ race: 'Gallente' }, function(err, gallenteCount) { callback(err, gallenteCount); }); }, function(callback) { Character.count({ race: 'Minmatar' }, function(err, minmatarCount) { callback(err, minmatarCount); }); }, function(callback) { Character.count({ gender: 'Male' }, function(err, maleCount) { callback(err, maleCount); }); }, function(callback) { Character.count({ gender: 'Female' }, function(err, femaleCount) { callback(err, femaleCount); }); }, function(callback) { Character.aggregate({ $group: { _id: null, total: { $sum: '$wins' } } }, function(err, totalVotes) { var total = totalVotes.length ? totalVotes[0].total : 0; callback(err, total); } ); }, function(callback) { Character .find() .sort('-wins') .limit(100) .select('race') .exec(function(err, characters) { if (err) return next(err); var raceCount = _.countBy(characters, function(character) { return character.race; }); var max = _.max(raceCount, function(race) { return race }); var inverted = _.invert(raceCount); var topRace = inverted[max]; var topCount = raceCount[topRace]; callback(err, { race: topRace, count: topCount }); }); }, function(callback) { Character .find() .sort('-wins') .limit(100) .select('bloodline') .exec(function(err, characters) { if (err) return next(err); var bloodlineCount = _.countBy(characters, function(character) { return character.bloodline; }); var max = _.max(bloodlineCount, function(bloodline) { return bloodline }); var inverted = _.invert(bloodlineCount); var topBloodline = inverted[max]; var topCount = bloodlineCount[topBloodline]; callback(err, { bloodline: topBloodline, count: topCount }); }); } ], function(err, results) { if (err) return next(err); res.send({ totalCount: results[0], amarrCount: results[1], caldariCount: results[2], gallenteCount: results[3], minmatarCount: results[4], maleCount: results[5], femaleCount: results[6], totalVotes: results[7], leadingRace: results[8], leadingBloodline: results[9] }); }); }); ~~~ 最后使用`aggregate()`方法的操作比較令人費解。必須承認,到這一步我也曾去尋求過幫助。在MongoDB里,聚合(aggregation)操作處理數據記錄并且返回計算后的結果。在這里它通過將所有`wins`數量相加,來計算所有投票的總數。因為投票是一個零和游戲,獲勝總數總是和失敗總數相同,所以我們同樣也可以使用`losses`數量來計算。 項目到這里基本就完成了。在教程的最后我還將給項目添加更多特性,給它稍稍擴展一下。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看