2016-10-19 57 views
5

我有一個明確的應用程序,從外部API獲取數據創建expressjs模型

api.com/companies (GET, POST) 
api.com/companies/id (GET, PUT) 

我想創建一個模型,以使代碼更易於維護,你可以看到I'm重複了很多的代碼在這裏。

router.get('/companies', function(req, res, next) { 

    http.get({ 
     host: 'http://api.com', 
     path: '/companies' 
    }, function(response) { 
     var body = ''; 
     response.on('data', function(d) { 
      body += d; 
     }); 
    }); 

    res.render('companies', {data: body}); 
}); 

router.get('/companies/:id', function(req, res, next) { 

    http.get({ 
     host: 'http://api.com', 
     path: '/companies/' + req.params.id 
    }, function(response) { 
     var body = ''; 
     response.on('data', function(d) { 
      body += d; 
     }); 
    }); 

    res.render('company', {data: body}); 
}); 

我該怎麼做?

回答

1

首先: http.get是異步的。經驗法則:當你看到一個回調函數時,你正在處理一個異步函數。如果使用res.render終止請求時,http.get()及其回調將完成,則無法判斷。 這意味着res.render總是需要在回調中發生。

我在本例中使用ES6語法。

// request (https://github.com/request/request) is a module worthwhile installing. 
const request = require('request'); 
// Note the ? after id - this is a conditional parameter 
router.get('/companies/:id?', (req, res, next) => { 

    // Init some variables 
    let url = ''; 
    let template = '' 

    // Distinguish between the two types of requests we want to handle 
    if(req.params.id) { 
     url = 'http://api.com/companies/' + req.params.id; 
     template = 'company'; 
    } else { 
     url = 'http://api.com/companies'; 
     template = 'companies'; 
    } 

    request.get(url, (err, response, body) => { 

     // Terminate the request and pass the error on 
     // it will be handled by express error hander then 
     if(err) return next(err); 
     // Maybe also check for response.statusCode === 200 

     // Finally terminate the request 
     res.render(template, {data: body}) 
    }); 

}); 

關於你的'模型'問題。 我寧願稱他們爲'服務',因爲模型是一些數據集合。服務是一個邏輯集合。

要創建一個公司服務模塊做這樣的事情:

// File companyService.js 
const request = require('request'); 

// This is just one of many ways to encapsulate logic in JavaScript (e.g. classes) 
// Pass in a config that contains your service base URIs 
module.exports = function companyService(config) { 
    return { 
     getCompanies: (cb) => { 
      request.get(config.endpoints.company.many, (err, response, body) => { 
       return cb(err, body); 
      }); 
     }, 
     getCompany: (cb) => { 
      request.get(config.endpoints.company.one, (err, response, body) => { 
       return cb(err, body); 
      }); 
     }, 
    } 
}; 


// Use this module like 
const config = require('./path/to/config'); 
const companyService = require('./companyService')(config); 

// In a route 
companyService.getCompanies((err, body) => { 
    if(err) return next(err); 

    res.render(/*...*/) 
}); 
1

重構方法的一般方法是確定代碼中的不同之處,並將其提取爲傳遞到包含公共代碼的函數的動態部分。

舉例來說,兩件事情,這裏有不同的是在其上請求發生

'/companies' vs '/companies/:id' 

路徑以及傳遞到http.get

'/companies' vs '/companies/' + req.params.id 

您可以提取這些和相關的路徑將它們傳遞給將爲您分配處理程序的函數。

這裏有一個通用的方法:

// props contains the route and 
// a function that extracts the path from the request 
function setupGet(router, props) { 
    router.get('/' + props.route, function(req, res, next) { 

    http.get({ 
     host: 'http://api.com', 
     path: props.getPath(req) 
    }, function(response) { 
     var body = ''; 
     response.on('data', function(d) { 
     body += d; 
     }); 
    }); 

    res.render('company', { 
     data: body 
    }); 
    }); 
} 

然後調用它與兩種選擇:

setupGet(router, { 
    route: 'companies', 
    getPath: function(req) { 
    return 'companies'; 
    } 
}); 

setupGet(router, { 
    route: 'companies/:id', 
    getPath: function(req) { 
    return 'companies' + req.params.id; 
    } 
}); 

這樣做的好處是,你可以使用路線和路徑的任意組合,以及使用其他req屬性來確定路徑。

你需要認識到的另一件事是,你res.render通話會發生這樣做body += d之前,因爲前者在調用http.get後立即同步發生,後者異步發生(晚些時候)。

您可能想要將render方法放入回調本身。

// props contains the route and 
// a function that extracts the path from the request 
function setupGet(router, props) { 
    router.get('/' + props.route, function(req, res, next) { 

    http.get({ 
     host: 'http://api.com', 
     path: props.getPath(req) 
    }, function(response) { 
     var body = ''; 
     response.on('data', function(d) { 
     body += d; 

     // probably want to render here 
     res.render('company', { 
      data: body 
     }); 
     }); 
    }); 
    }); 
} 
+0

好點。我仍然習慣了這個異步的東西 我將無法創建一個模型,因此我可以在多個頁面中調用/ companies?我在問,因爲異步需要完成請求 - 響應才能獲取數據。每次我需要顯示所有公司時,我需要發出http請求並在回調中發送數據? – handsome

+0

那麼,如果公司沒有改變,你可以將它們存儲在某個地方的存儲器中,並首次提出請求,然後保存公司,然後每次只需返回保存的值。你想讓我編輯我的問題並向你展示一個例子嗎? – nem035

+0

這是有道理的,但我正在尋找更多面向模型的東西,比如好的舊PHP,所以我可以重複使用邏輯,而不是在所有地方都進行http調用。謝啦! – handsome

2

在這種情況下沒有必要有多條路線!您可以使用的可選參數?。看看下面的例子:

router.get('/companies/:id?', function(req, res, next) { 
    var id = req.params.id; 

    http.get({ 
     host: 'http://api.com', 
     path: '/companies/' + id ? id : "" 
    }, function(response) { 
     var body = ''; 
     response.on('data', function(d) { 
      body += d; 
     }); 
    }); 

    res.render('companies', {data: body}); 
}); 

的碼位的位置:

path: '/companies/' + id ? id : "" 

是使用內嵌if語句,所以什麼它說的是,如果id != null, false, or undefined,添加ID到companies/字符串和其他只需簡單地添加。

編輯

關於JS類,你可以做這樣的事情:

// Seperate into a different file and export it 
class Companies { 
    constructor (id) { 
     this.id= id; 
     this.body = ""; 
     // You can add more values for this particular object 
     // Or you can dynamically create them without declaring here 
     // e.g company.new_value = "value" 
    } 

    get (cb) { 
     http.get({ 
      host: 'http://api.com', 
      path: '/companies/' + this.id ? this.id : "" 
     }, function(response) { 
      response.on('data',(d) => { 
       this.body += d; 
       cb(); // callback 
      }); 
     }); 
    } 

    post() { 
     // You can add more methods ... E.g a POST method. 
    } 
    put (cb) { 
     http.put({ 
      host: 'http://api.com', 
      path: '/companies/' + this.id ? this.id : "", 
      ... Other object values here ... 
     }, function(response) { 
      response.on('data',(d) => { 
       ... do something here with the response ... 
       cb(); //callback 
      }); 
     }); 
    } 
} 

您的路由器類可以再使用這個類,像這樣:

router.get('/companies/:id?', function(req, res, next) { 
    var id = req.params.id; 
    var company = new Companies(id); 
    company.get(() => { 
     // We now have the response from our http.get 
     // So lets access it now! 
     // Or run company.put(function() {...}) 
     console.log (company.body); 
     res.render('companies', {data: company.body}); 
    }); 

}); 

我添加回調這裏爲簡單起見,但我推薦使用承諾: https://developers.google.com/web/fundamentals/getting-started/primers/promises

+0

你是對的,這可以通過你的方法簡化很多 我正在尋找'var data = new companies()'或'var data = new companies(1)''而這家公司()將會發揮神奇的功能來製作http請求並獲取數據。所以我可以在更多地方重複使用它。謝謝!! – handsome

+0

不用擔心,祝你好運:)是的,你可以很容易地實現這個邏輯到一個類!我會嘗試添加一個小例子。 記住+ 1 /接受,如果它有幫助:D @handsome – James111

+0

@handsome看看我的更新。它使用節點es6類:) – James111