javascript - Modelle in expressjs erstellen
Ich habe eine Express-App, die ihre Daten von einer externen API erhält
api.com/companies (GET, POST)
api.com/companies/id (GET, PUT)
Ich möchte ein Modell erstellen, um den Code leichter wartbar zu machen, wie Sie sehen können, wiederhole ich hier viel Code.
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});
});
Wie kann ich das machen?
Antwort #1
Zuerst einmal: http.get ist asynchron. Faustregel: Wenn Sie einen Callback sehen, haben Sie es mit einer asynchronen Funktion zu tun. Sie können nicht sagen, ob das http.get() und sein Callback abgeschlossen sein werden, wenn Sie die Anfrage mit res.render beenden. Das bedeutet, dass res.render immer innerhalb des Callbacks stattfinden muss.
Ich verwende in diesem Beispiel die ES6-Syntax.
// 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})
});
});
Bezüglich Ihrer 'Modell'-Frage. Ich würde sie eher 'Dienste' nennen, da ein Modell eine Sammlung von Daten ist. Ein Dienst ist eine Sammlung von Logik.
Um ein Unternehmensservicemodul zu erstellen, könnte man Folgendes tun:
// 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(/*...*/)
});
Antwort #2
In diesem Fall ist es nicht nötig, mehrere Routen zu haben! Sie können einen optionalen Parameter mit ? verwenden. Schauen Sie sich folgendes Beispiel an:
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});
});
Der Codeausschnitt hier:
path: '/companies/' + id ? id : ""
Es wird eine Inline-if-Anweisung verwendet, also sagt es im Wesentlichen: wenn id != null, false oder undefined, füge id zur companies/-Zeichenkette hinzu und ansonsten füge einfach nichts hinzu.
Bearbeiten
Bezüglich JS-Klassen könnten Sie so etwas machen:
// 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
});
});
}
}
Ihre Router-Klasse könnte dann diese Klasse wie folgt verwenden:
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});
});
});
Ich habe hier der Einfachheit halber Callbacks hinzugefügt, aber ich empfehle die Verwendung von Promises: https://developers.google.com/web/fundamentals/getting-started/primers/promises
