【问题标题】:create models in expressjs在 expressjs 中创建模型
【发布时间】:2016-10-19 04:19:00
【问题描述】:

我有一个从外部 API 获取数据的快速应用

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

我想创建一个模型以使代码更易于维护,如您所见,我在这里重复了很多代码。

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});
});

我该怎么做?

【问题讨论】:

    标签: javascript node.js express models


    【解决方案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(/*...*/)
    });
    

    【讨论】:

      【解决方案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 语句,所以它的意思是,if id != null, false, or undefined,将 id 添加到 companies/ 字符串和 else 什么都不添加。

      编辑

      关于 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});
          });
      
      });
      

      为了简单起见,我在这里添加了回调,但我建议使用 Promise: https://developers.google.com/web/fundamentals/getting-started/primers/promises

      【讨论】:

      • 是的,你是对的,这可以用你的方法简化很多,我正在寻找类似var data = new companies()var data = new companies(1) 的东西,这个公司()会神奇地发出 http 请求和获取数据。所以我可以在更多地方重复使用它。谢谢!!
      • 别担心,祝你好运:) 是的,你可以很容易地将这个逻辑实现到一个类中!我会尝试添加一个小例子。如果有帮助,请记住 +1/接受:D @handsome
      • @handsome 看看我的更新。它使用节点 es6 类:)
      • 好主意,但您的编辑存在两个问题 :) 首先是 company.get 异步发生,因此 company.body 在调用 company.get 后不会立即获得预期值。实际上,它总是"",因为您在常规函数中使用this.body,而不是类上的方法,而JS中常规函数中的this要么是全局对象,要么是严格意义上的undefined模式或response.on 可以使用call/apply,将this 更改为其他内容。无论哪种方式,属于该类的body 永远不会改变。
      • 这不是复制粘贴的工作,我只是快速完成了!无论如何,感谢您指出这一点,我添加了回调来解决 http 为我们提供的异步功能。 @nem035 类的东西还有更多的指南:D
      【解决方案3】:

      进行重构的一般方法是识别代码中的不同之处,并将其提取为动态部分,然后传递给包含公共代码的函数。

      例如,这里的两个不同之处是请求发生的路径

      '/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
              });
            });
          });
        });
      }
      

      【讨论】:

      • 好点。我仍然习惯于这种异步的东西,我将无法创建一个模型,所以我可以在多个页面中调用 /companies?我问是因为异步需要完成请求响应才能获取数据。每次我需要显示所有公司时,我都需要发出一个 http 请求并在回调中发送数据?
      • 好吧,如果公司没有改变,你可以将它们存储在内存中的某个地方并第一次发出请求,保存公司,然后每次都返回保存的值。你想让我编辑我的问题并给你看一个例子吗?
      • 这是有道理的,但我正在寻找更面向模型的东西,比如好的旧 PHP,这样我就可以重用逻辑,而不是到处进行所有这些 http 调用。谢谢大哥!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-28
      • 2018-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多