Sails基礎之Controller層

通過前面的使用,我們可以看出Sails中MVC的結構更傾向於MVP的概念,Presenter在Sails中被稱之爲Actions:

They often act as a middleman between your models and views.

Controller層這個結構上的變化是Sails v1.0中新提出的方案,Action可以使用classic和actions2兩種格式,另外,仍然兼容支持Sails v0.12上的Controller實現方式。相對於腫脹的Controller,將Controller拆分成多個Action可以更清晰的表達業務邏輯,其中actions2格式的Action更是一種半文檔半校驗器的形式,可以從接口描述、接口參數、參數校驗、邏輯、退出方式等方面構建整個業務邏輯。

Actions

Action2 Action

Action2格式的action與Helper基本相似,需要做調整的主要是各種與Controller層職責相關的exit方式。我們來創建一個signup action:

sails generate action user/login

我們可以編寫一個用於登錄驗證的action,從而實現登錄業務邏輯:

  1. Model層
    Model層我們使用前面案例中的User Model:
const crypto = require('crypto'); 
const uuid = require('node-uuid');

module.exports = {
  primaryKey: 'uuid',
  attributes: {
    uuid: {
      type: 'string',
      required: true,
    },
    username: {
      type: 'string',
      required: true,
      allowNull: false,
      unique: true,
    },
    password: {
      type: 'string',
      required: true,
      allowNull: false,
    },

  },
  customToJSON: function() {
    return _.omit(this, ['password']);
  },
  beforeCreate: function (valuesToSet, proceed) {
    let md5 = crypto.createHash('md5');
    valuesToSet.uuid = uuid.v4();
    valuesToSet.password = md5.update(valuesToSet.password).digest('hex');
    return proceed();
  }

};

身份驗證主要通過對username和password的查詢完成。
2. View層
首先在config/routes創建一個view路由:

 '/login': {
    view: 'pages/login',
    locals: {
      layout: 'layouts/users',
      errno: 0,
    },
},

該路由指定了模板爲views/pages/login,layout爲views/layouts/users(只有默認的ejs模板引擎支持此項配置),接下來我們分別創建layouts/users和pages/login:
layouts/users.ejs:

<!DOCTYPE html>
<html>
  <head>
    <title>Sails App</title>
    <!-- Viewport mobile tag for sensible mobile support -->
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <!--STYLES-->
    <!--STYLES END-->
  </head>
  <body>
    <%- body %>
  </body>
</html>

pages/login.ejs:

<div class="container">
    <% if(errno == 1){ %>
      <div class="alert alert-danger" role="alert">
          Username or password is error!
      </div>
    <% } %>
    <form method="POST" action="/user/login">
        <div class="form-group">
          <label for="username">Username</label>
          <input type="text" class="form-control" id="username" name="username" placeholder="Enter username">
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input type="password" class="form-control" id="password" name="password" placeholder="Password">
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
</div>

assets資源:
將bootstrap.min.css拷貝到assets/styles下,啓動時grunt任務會將其自動注入到users.ejs的樣式文件引用中。

  1. Controller層
    api/controllers/user/login:
const crypto = require('crypto'); 

module.exports = {

  friendlyName: 'Login',

  description: 'Login user.',

  inputs: {
    username: {
      description: 'The username',
      type: 'string',
      required: true,
    },
    password: {
      description: 'The password',
      type: 'string',
      required: true,
    }

  },

  exits: {
    success: {
      responseType: 'view',
      viewTemplatePath: 'pages/welcome'
    },
    fail: {
      responseType: 'view',
      viewTemplatePath: 'pages/login'
    },
    err: {
      description: 'Occur error',
      responseType: 'notFound'
    }
  },


  fn: async function (inputs, exits) {
    let md5 = crypto.createHash('md5');
    let {username, password} = inputs;
   
    try{

      let user = await User.findOne({
        username, username
      });
  
      if(!user || user.password != md5.update(password).digest('hex')){
        return exits.fail({
          errno: 1
        });
      }

      return exits.success({
        errno: 0,
        msg: {
          username: username
        }
      });
    } catch(e) {
      sails.log(e.message);
      throw 'err';
    }
  }
};

可以看到與Helper的結構是一致的,唯一需要調整的是exit的方式,而exit方式主要是由responseType決定的:
responseType is one of the following:

  • “” (the standard response: Determine an appropriate response based on context: this might send plain text, download a file, transmit data as JSON, or send no response body at all.)
  • “view” (render and respond with a view; exit output will be provided as view locals)
  • “redirect” (redirect to the URL returned as the exit output)
    爲action創建路由:
  'post /user/login': 'user.login',

從代碼可以看到,登錄成功後,渲染模板pages/welcome,因此,我們創建對應的模板:

<h1>Welcome <%= msg.username %> !</h1>

接下來,我們啓動測試以下:

  1. 創建一個測試user:
    http://127.0.0.1:1337/user/create?username=admin&password=123456&uuid=auto

  2. 登錄錯誤:
    http://127.0.0.1:1337/login

    輸入錯誤的username或password:

  3. 登錄成功:
    輸入正確的username和password:

Classic Action

Classic action與v0.12版本中傳統的Controller實現方式基本一致,將傳統寫在一個Controller中的業務邏輯拆分爲單個的一個個action,更有利於業務的實現與維護。

sails generate action user/login--no-actions2

Controller

另外,你仍然可以使用傳統的Controller方式實現所有業務邏輯:

module.exports = {
  login: function (req, res) { ... },
  logout: function (req, res) { ... },
  signup: function (req, res) { ... },
};

machine-as-action

你也可以將Controller的編寫方式轉換到action2的編寫風格上來,請參考以下鏈接:
https://github.com/sailshq/machine-as-action

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章