javascript - skapa modeller i expressjs
Jag har en express-app som får sina data från ett externt API
api.com/companies (GET, POST)
api.com/companies/id (GET, PUT)
Jag vill skapa en modell för att göra koden lättare att underhålla eftersom du kan se att jag upprepar mycket kod här.
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});
});
Hur kan jag göra det?
Svar #1
Först och främst: http.get är asynkront. Tumregel: När du ser en callback, hanterar du en asynkron funktion. Du kan inte veta om http.get() och dess callback kommer att vara färdig när du avslutar förfrågan med res.render. Detta betyder att res.render alltid behöver ske inom callbacken.
Jag använder ES6-syntax i detta exempel.
// 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})
});
});
Angående din 'modell'-fråga. Jag skulle hellre kalla dem 'tjänster' eftersom en modell är någon samling av data. En tjänst är en samling av logik.
För att skapa en företagstjänstmodul kan man göra så här:
// 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(/*...*/)
});
Svar #2
Det finns inget behov av att ha flera rutter i det här fallet! Du kan använda en valfri parameter med ?. Ta en titt på följande exempel:
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});
});
Kodsnutten här:
path: '/companies/' + id ? id : ""
Använder ett inline-om-uttryck, så vad det säger är, om id != null, false, eller undefined, lägg till id i companies/-strängen och annars lägg helt enkelt till ingenting.
Redigera
När det gäller js-klasser, skulle du kunna göra något som detta:
// 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
});
});
}
}
Din routerklass skulle då kunna använda denna klass på följande sätt:
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});
});
});
Jag har lagt till callback-funktioner här för enkelhetens skull, men jag rekommenderar att använda promises: https://developers.google.com/web/fundamentals/getting-started/primers/promises
