使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)
前篇
使用React、Node.js、MongoDB、Socket.IO開發一個角色投票應用的學習過程(一)
使用React、Node.js、MongoDB、Socket.IO開發一個角色投票應用的學習過程(二)
原文第十三步,Express API路由
第一個路由是用來創建角色的
app.post('/api/characters',(req,res,next) => {let gender = req.body.gender;let characterName = req.body.name;let characterIdLookupUrl = 'https://api.eveonline.com/eve/CharacterId.xml.aspx?names=' + characterName;const parser = new xml2js.Parser();async.waterfall([function(callback) {request.get(characterIdLookupUrl,(err,request,xml) => {if(err) return next(err);parser.parseString(xml,(err,parsedXml) => {try {let characterId = parsedXml.eveapi.result[0].rowset[0].row[0].$.characterID;app.models.character.findOne({ characterId: characterId},(err,model) => {if(err) return next(err);if(model) {return res.status(400).send({ message: model.name + ' is alread in the database'});}callback(err,characterId);});} catch(e) {return res.status(400).send({ message: ' xml Parse Error'});}});});},function(characterId) {let characterInfoUrl = 'https://api.eveonline.com/eve/CharacterInfo.xml.aspx?characterID=' + characterId;console.log(characterInfoUrl);request.get({ url: characterInfoUrl },(err,request,xml) => {if(err) return next(err);parser.parseString(xml, (err,parsedXml) => {if (err) return res.send(err);try{let name = parsedXml.eveapi.result[0].characterName[0];let race = parsedXml.eveapi.result[0].race[0];let bloodline = parsedXml.eveapi.result[0].bloodline[0];app.models.character.create({characterId: characterId,name: name,race: race,bloodline: bloodline,gender: gender},(err,model) => {if(err) return next(err);res.send({ message: characterName + ' has been added successfully!'});});} catch (e) {res.status(404).send({ message: characterName + ' is not a registered citizen of New Eden',error: e.message });}});});}]); });是不是看起來和原文的基本一模一樣,只不過把var 變成了let 匿名函數變成了ES6的'=>'箭頭函數,雖然我用的是warterline而原文中用的是mongoose但是包括方法名基本都一樣,所以我感覺waterline是在API上最接近mongoose
順便說一下,我為什么不喜歡mongodb,僅僅是因為有一次我安裝了,只往里面寫了幾條測試數據,按文本算最多幾kb,但第二天重啟機器的時候,系統提示我,我的/home分區空間不足了(雙系統分區分給linux分小了本來就不大),結果一查mongodb 的data文件 有2G多,我不知道什么原因,可能是配置不對還是別的什么原因,反正,當天我就把它刪除了,
完成了這個API我們就可以往數據庫里添加東西了,不知道哪些用戶名可以用?相當簡單,反正我用的全是一名人的名字(英文名),外國人也喜歡搶注名字,嘿嘿嘿
原文第十三步,Home組件
基本保持和原文一樣,只是用lodash 替換了 underscore
一開始我看到網上介紹lodash是可以無縫替換underscore,中要修改引用就可以,但是我用的版本是4.11.2已經有很多方法不一樣了,還去掉了不少方法(沒有去關注underscore是不是也在最新版本中有同樣的改動)
原文中:
...... import {first, without, findWhere} from 'underscore'; ......var loser = first(without(this.state.characters, findWhere(this.state.characters, { characterId: winner }))).characterId;......修改為:
...... import {first, filter} from 'lodash'; ......let loser = first(filter(this.state.characters,item => item.characterId != winner )).characterId;findWhere 在最新版本的lodash中已經不存正,我用了filter來實現相同功能。
第十四步:Express API 路由(2/2)
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([]);});});}); });可以看到原文中用{ random: { $near: [Math.random(), 0] } }做為查詢條件從而在數據庫里取出兩條隨機的記錄返回給頁面進行PK,前文說過random的類型在mysql沒有類似的,所以我把這個字段刪除了。本來mysql,可以用order by rand() 之類的方法但是,waterline的sort(order by rand())不被支持,所以我是把所有符合條件的記錄取出來,能過lodash的sampleSize方法從所有記錄中獲取兩天隨機記錄。
app.get('/api/characters', (req,res,next) => {let choice = ['Female', 'Male'];let randomGender = _.sample(choice);//原文中是通過nearby字段來實現隨機取值,waterline沒有實現mysql order by rand()返回隨機記錄,所以返回所有結果,用lodash來處理app.models.character.find().where({'voted': false}).exec((err,characters) => {if(err) return next(err);//用lodash來取兩個隨機值let randomCharacters = _.sampleSize(_.filter(characters,{'gender': randomGender}),2); if(randomCharacters.length === 2){//console.log(randomCharacters);return res.send(randomCharacters);}//換個性別再試試let oppsiteGender = _.first(_.without(choice, randomGender));let oppsiteCharacters = _.sampleSize(_.filter(characters,{'gender': oppsiteGender}),2); if(oppsiteCharacters === 2) {return res.send(oppsiteCharacters);}//沒有符合條件的character,就更新voted字段,開始新一輪PKapp.models.character.update({},{'voted': false}).exec((err,characters) => {if(err) return next(err);return res.send([]);});});});在數據量大的情況下,這個的方法性能上肯定會有問題,好在我們只是學習過程,數據量也不大。將就用一下,能實現相同的功能就可以了。
GET /api/characters/search
這個API之前還有兩個API,和原文基本一樣,所做的修改只是用了ES6的語法,就不浪費篇幅了,可以去我的github看
這一個也只是一點mongoose和waterline的一點點小區別
原文中mongoose的模糊查找是用正則來做的,mysql好像也可以,但是warterline中沒有找到相關方法(它的文檔太簡陋了)
所以原文中
我改成了
app.get('/api/characters/search', (req,res,next) => {app.models.character.findOne({name:{'contains':req.query.name}}, (err,character) => {.....通過contains來查找,其實就是like %sometext%的方法來實現
下面還有兩個方法修改的地方也大同小異,就不仔細講了,看代碼吧
GET /api/stats
這個是原文最后一個路由了,
原文中用了一串的函數來獲取各種統計信息,原作者也講了可以優化,哪我們就把它優化一下吧
我把要統計數據的字段放入一個數組countColumn通過forEach把push到asyncTask,最后兩個統計方法不一樣的函數,單獨push,最后用async.parallel方法執行并獲得結果。
underscore的max方法可以從{a:1,b:6,d:2,e:3}返回最大值,但是lodash新版中的不行,只能通過_.max(_.values(bloodlineCount))這樣的方式返回最大值。
總結
以上是生活随笔為你收集整理的使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怀孕梦到很多鸡蛋是什么意思
- 下一篇: 梦到妈妈开车我坐车好不好