2016-10-19 4 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 является асинхронным. Правило большого пальца: когда вы видите обратный вызов, вы имеете дело с асинхронной функцией. Вы не можете сказать, будет ли http.get() и его обратный вызов завершен при завершении запроса с помощью res.render. Это означает, что 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

хороший момент. Я все еще привык к этому асинхронному материалу Я не смогу создать одну модель, чтобы я мог позвонить/компании на более чем одной странице? Я спрашиваю, потому что асинхронный запрос должен будет заполнить запрос-ответ, чтобы получить данные. каждый раз, когда мне нужно будет отображать все компании, мне нужно будет сделать 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 : "" 

ли, используя встроенный, если заявление, так, что он говорит, еслиid != null, false, or undefined, добавить идентификатор к 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

yup вы правы, это может быть упрощено с вашим подходом Я искал что-то вроде 'var data = new companies()' или 'var data = new companies() и эти компании() будут делать магию, чтобы сделать http-запрос и получить данные. поэтому я могу использовать его в других местах. благодаря!! – handsome

+0

Не беспокойтесь и удачи :) Да, вы можете легко реализовать эту логику в классе! Попробую добавить небольшой пример. Запомнить + 1/принять, если это помогло: D @handsome – James111

+0

@handsome Взгляните на мое обновление. Он использует узлы es6 классов :) – James111

Смежные вопросы