Adonis.js RESTFUL API 解决方法

Adonis.js RESTFUL API Workaround

我最近开始在 adonisjs 框架上开发应用程序。我可以选择使用 expressjs,但我更喜欢 adonisjs,因为我喜欢它的结构方式(主要是 laravel 风格)。

我目前正在尝试构建一个 RESTFUL API 但无法弄清楚基本的路由/中间件/api控制器(我的自定义控制器来处理所有 api 请求)场景。

这是我到目前为止所做的:

routes.js

Route.post('api/v1/login', 'ApiController.login')
Route.post('api/v1/register', 'ApiController.register')

// API Routes
Route.group('api', function() {

  Route.get('users', 'ApiController.getUsers')

}).prefix('/api/v1').middlewares(['auth:api'])

ApiController.js

'use strict'

const User = use('App/Model/User')
const Validator = use('Validator')

const FAIL = 0
const SUCCESS = 1

class ApiController {

  * login (request, response) {

    let jsonResponse = {}

    const email = request.input('email')
    const password = request.input('password')

    // validate form input
    const rules = {
      email: 'required|email',
      password: 'required'
    }

    const messages = {
      'email.required': 'Email field is required.',
      'password.required': 'Password field is required.'
    }

    const validation = yield Validator.validateAll(request.all(), rules, messages)

    if (validation.fails()) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = validation.messages()[0].message

    } else {

      try {

        yield request.auth.attempt(email, password)

        const user = yield User.findBy('email', email)

        const token = yield request.auth.generate(user)

        jsonResponse.status = SUCCESS
        jsonResponse.response = {}
        jsonResponse.response.message = "Logged In Successfully"
        jsonResponse.response.user = user
        jsonResponse.response.token = token

      } catch (e) {

        jsonResponse.status = FAIL
        jsonResponse.response = {}
        jsonResponse.response.message = e.message

      }

    }

    return response.json(jsonResponse)

  }

}

module.exports = ApiController

config/auth.js

'use strict'

const Config = use('Config')

module.exports = {

  /*
  |--------------------------------------------------------------------------
  | Authenticator
  |--------------------------------------------------------------------------
  |
  | Authenticator is a combination of HTTP Authentication scheme and the
  | serializer to be used for retrieving users. Below is the default
  | authenticator to be used for every request.
  |
  | Available Schemes - basic, session, jwt, api
  | Available Serializers - Lucid, Database
  |
  */
  authenticator: 'session',

  /*
  |--------------------------------------------------------------------------
  | Session Authenticator
  |--------------------------------------------------------------------------
  |
  | Session authenticator will make use of sessions to maintain the login
  | state for a given user.
  |
  */
  session: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'session',
    uid: 'email',
    password: 'password'
  },

  /*
  |--------------------------------------------------------------------------
  | Basic Auth Authenticator
  |--------------------------------------------------------------------------
  |
  | Basic Authentication works on Http Basic auth header.
  |
  */
  basic: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'basic',
    uid: 'email',
    password: 'password'
  },

  /*
  |--------------------------------------------------------------------------
  | JWT Authenticator
  |--------------------------------------------------------------------------
  |
  | Jwt authentication works with a payload sent with every request under
  | Http Authorization header.
  |
  */
  jwt: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'jwt',
    uid: 'email',
    password: 'password',
    secret: Config.get('app.appKey')
  },

  /*
  |--------------------------------------------------------------------------
  | API Authenticator
  |--------------------------------------------------------------------------
  |
  | Api authenticator authenticates are requests based on Authorization
  | header.
  |
  | Make sure to define relationships on User and Token model as defined
  | in documentation
  |
  */
  api: {
    serializer: 'Lucid',
    model: 'App/Model/Token',
    scheme: 'api'
  }

}

config/shield.js

'use strict'

module.exports = {
  /*
  |--------------------------------------------------------------------------
  | Content Security Policy
  |--------------------------------------------------------------------------
  |
  | Content security policy filters out the origins not allowed to execute
  | and load resources like scripts, styles and fonts. There are wide
  | variety of options to choose from.
  | @examples
  | directives: {
  |   defaultSrc: ['self', '@nonce', 'cdnjs.cloudflare.com']
  | }
  */
  csp: {
    directives: {
    },
    reportOnly: false,
    setAllHeaders: false,
    disableAndroid: true
  },

  /*
  |--------------------------------------------------------------------------
  | X-XSS-Protection
  |--------------------------------------------------------------------------
  |
  | X-XSS Protection saves from applications from XSS attacks. It is adopted
  | by IE and later followed by some other browsers.
  |
  */
  xss: {
    enabled: true,
    enableOnOldIE: false
  },

  /*
  |--------------------------------------------------------------------------
  | Iframe Options
  |--------------------------------------------------------------------------
  |
  | xframe defines whether or not your website can be embedded inside an
  | iframe. Choose from one of the following options.
  | @available options
  | DENY, SAMEORIGIN, ALLOW-FROM http://example.com
  */
  xframe: 'DENY',

  /*
  |--------------------------------------------------------------------------
  | No Sniff
  |--------------------------------------------------------------------------
  |
  | Browsers have a habit of sniffing content-type of a response. Which means
  | files with .txt extension containing Javascript code will be executed as
  | Javascript. You can disable this behavior by setting nosniff to false.
  |
  */
  nosniff: true,

  /*
  |--------------------------------------------------------------------------
  | No Open
  |--------------------------------------------------------------------------
  |
  | IE users can execute webpages in the context of your website, which is
  | a serious security risk. Below options will manage this for you.
  |
  */
  noopen: true,

  /*
  |--------------------------------------------------------------------------
  | CSRF Protection
  |--------------------------------------------------------------------------
  |
  | CSRF Protection adds another layer of security by making sure, actionable
  | routes does have a valid token to execute an action.
  |
  */
  csrf: {
    enable: true,
    methods: ['POST', 'PUT', 'DELETE'],
    filterUris: ['/api/v1/login', '/api/v1/register'],
    compareHostAndOrigin: true
  }

}

现在当我点击登录网络服务时(使用邮递员)。它验证用户但在 const token = request.auth.generate(user) 处抛出异常并显示 request.auth.generate is not a function.

我不知道这是怎么回事。请帮忙。

谢谢

您需要生成一个 JWT 令牌(当用户调用登录 api 调用时)并将其发回,以便请求登录服务的应用程序可以存储它并使用它来发出未来的请求(使用"Authorization" header,值为 "Bearer [JWT Token String]")。当应用程序发送另一个请求时,即获取业务类别列表(在其 header 中带有 JWT 令牌),我们将在 api 中间件中验证该请求。请求通过验证后,我们将处理请求并以 json 格式发回数据。

这是你的 header 的样子:

这就是您实际需要在代码中执行的操作:

// ROUTES.JS

// API Routes
Route.post('/api/v1/register', 'ApiController.register')
Route.post('/api/v1/login', 'ApiController.login')

Route.group('api', function() {

  Route.get('/business_categories', 'ApiController.business_categories')

}).prefix('/api/v1').middlewares(['api'])

// API.JS(中间件)

'use strict'

class Api {

  * handle (request, response, next) {

    // here goes your middleware logic
    const authenticator = request.auth.authenticator('jwt')
    const isLoggedIn = yield authenticator.check()

    if (!isLoggedIn) {
      return response.json({
        status: 0,
        response: {
          message: 'API Authentication Failed.'
        }
      })
    }

    // yield next to pass the request to next middleware or controller
    yield next

  }

}

module.exports = Api

// APICONTROLLER.JS

'use strict'

// Dependencies
const Env = use('Env')
const Validator = use('Validator')
const Config = use('Config')
const Database = use('Database')
const Helpers = use('Helpers')
const RandomString = use('randomstring')
const Email = use('emailjs')
const View = use('View')

// Models
const User = use('App/Model/User')
const UserProfile = use('App/Model/UserProfile')
const DesignCenter = use('App/Model/DesignCenter')
const Settings = use('App/Model/Setting')

// Properties
const FAIL      = 0
const SUCCESS   = 1
const SITE_URL  = "http://"+Env.get('HOST')+":"+Env.get('PORT')

// Messages
const MSG_API_AUTH_FAILED             = 'Api Authentication Failed.'
const MSG_REGISTERED_SUCCESS          = 'Registered Successfully.'
const MSG_LOGGED_IN_SUCCESS           = 'Logged In Successfully.'
const MSG_LOGGED_IN_CHECK             = 'You Are Logged In.'
const MSG_LOGGED_IN_FAIL              = 'Invalid Credentials.'
const MSG_FORGOT_PASS_EMAIL_SUCCESS   = 'Your password reset email has been sent. Please check your inbox to continue.'

class ApiController {

  * register (request, response) {

    let jsonResponse = {}

    // validate form input
    const validation = yield Validator.validateAll(request.all(), Config.get('validation.api.register.rules'), Config.get('validation.api.register.messages'))

    // show error messages upon validation fail
    if (validation.fails()) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = validation.messages()[0].message

    } else {

      // handle card image
      let card_image = null

      if ( request.file('card_image') ) {

        const image = request.file('card_image', {
          allowedExtensions: ['jpg', 'png', 'jpeg']
        })

        if (image.clientSize() > 0) {
          const filename = RandomString.generate({length: 30, capitalization: 'lowercase'}) + '.' + image.extension()
          yield image.move(Helpers.publicPath(Config.get('constants.user_card_img_upload_path')), filename)

          if (!image.moved()) {
            jsonResponse.status = FAIL
            jsonResponse.response = {}
            jsonResponse.response.message = image.errors()
            return response.json(jsonResponse)
          }

          // set value for DB
          card_image = filename
        }

      }

      // create user
      const user = yield User.create({
        username: new Date().getTime(),
        email: request.input('email'),
        password: request.input('password')
      })

      // create user profile
      const user_profile = yield UserProfile.create({
        user_id: user.id,
        user_type_id: 3, // designer
        first_name: request.input('first_name'),
        last_name: request.input('last_name'),
        business_name: request.input('business_name'),
        business_category_id: request.input('business_category'),
        card_image: card_image,
        phone: request.input('mobile_no'),
        is_active: 1
      })

      jsonResponse.status = SUCCESS
      jsonResponse.response = {}
      jsonResponse.response.message = MSG_REGISTERED_SUCCESS
      jsonResponse.response.user = {
        'id': user.id,
        'first_name': user_profile.first_name,
        'last_name': user_profile.last_name,
        'business_name': user_profile.business_name,
        'business_category_id': user_profile.business_category_id,
        'card_image': user_profile.card_image == null ? "" : SITE_URL + "/" + Config.get('constants.user_card_img_upload_path') + "/" + user_profile.card_image,
        'mobile_no': user_profile.phone
      }

    }

    return response.json(jsonResponse)

  }

  * login (request, response) {

    let jsonResponse = {}

    const email = request.input('email')
    const password = request.input('password')

    // validate form input
    const validation = yield Validator.validateAll(request.all(), Config.get('validation.api.login.rules'), Config.get('validation.api.login.messages'))

    if (validation.fails()) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = validation.messages()[0].message

    } else {

      try {
        const jwt = request.auth.authenticator('jwt')
        const token = yield jwt.attempt(email, password)
        const user = yield User.findBy('email', email)
        const user_profile = yield UserProfile.findBy('user_id', user.id)

        // check if user type is designer
        if ( user_profile.user_type_id == 3 ) {

          jsonResponse.status = SUCCESS
          jsonResponse.response = {}
          jsonResponse.response.message = MSG_LOGGED_IN_SUCCESS

          let card_image = null
          if (user_profile.card_image) {
            card_image = SITE_URL + "/" + Config.get('constants.user_card_img_upload_path') + "/" + user_profile.card_image
          }

          jsonResponse.response.user = {
            'id': user.id,
            'first_name': user_profile.first_name,
            'last_name': user_profile.last_name,
            'business_name': user_profile.business_name,
            'business_category_id': user_profile.business_category_id,
            'card_image': card_image,
            'mobile_no': user_profile.phone
          }
          jsonResponse.response.token = token

        } else {

          jsonResponse.status = FAIL
          jsonResponse.response = {}
          jsonResponse.response.message = MSG_LOGGED_IN_FAIL

        }
      } catch (e) {
        jsonResponse.status = FAIL
        jsonResponse.response = {}
        jsonResponse.response.message = e.message
      }

    }

    return response.json(jsonResponse)

  }

  * business_categories (request, response) {

    let jsonResponse = {}

    try {

      jsonResponse.status = SUCCESS
      const dbRecords = yield Database.select('id', 'name').from('business_categories')
      let records = []

      dbRecords.forEach(function(row) {
        records.push({
          id: row.id,
          name: row.name
        })
      })

      jsonResponse.response = records

    } catch (e) {

      jsonResponse.status = FAIL
      jsonResponse.response = {}
      jsonResponse.response.message = e.message

    }

    response.json(jsonResponse)

}

module.exports = ApiController

// 配置 / AUTH.JS

因为 JWT 令牌保持有效,除非它们过期或删除(从应用程序中,当强制注销用户时)。我们还可以设置一个有效期如下:

jwt: {
    serializer: 'Lucid',
    model: 'App/Model/User',
    scheme: 'jwt',
    uid: 'email',
    password: 'password',
    secret: Config.get('app.appKey'),
    options: {
        // Options to be used while generating token
        expiresIn: Ms('3m') // 3 months
    }
}

// 配置 / SHIELD.JS

由于大多数 api 服务在发送 POST 请求时无法发送 CSRF 令牌,您可以在此处排除那些不需要检查 CSRF 令牌的 api 路径在这个文件中如下:

csrf: {
    enable: true,
    methods: ['POST', 'PUT', 'DELETE'],
    filterUris: [
        '/api/v1/login',
        '/api/v1/register'
    ],
    compareHostAndOrigin: true
}

希望这对您有所帮助:)