分享 像 PHP 的 die 方法一样终结 Rails 的 render

wwwicbd · 2017年08月09日 · 最后由 5long 回复于 2017年08月10日 · 1783 次阅读

场景是这样的:

Redis 中用 String 型存了用户登录令牌,key 是令牌,value 是该用户的 ID. 定义一个 auth 方法,如果根据令牌能读到合法的用户 ID, 就意味着该令牌有效而且能获取 ID.(敏感权限再去数据库验证身份)

在一部分接口中需要登录才能操作,所以希望一旦令牌非法,auth 方法直接 render 错误码然后终结后续操作. 这个场景在 PHP 中可以用 die("err_info"); 来处理。

4-ways-to-early-return-from-a-rails-controller 这篇文章提到了几个实用方法,利用返回值和 return 的组合. 但我想要的是一旦触发某个条件,无论是在 Controller 还是在某个方法里,立即 render 结果并终结后续操作。

方案

利用异常。

module DiyExceptions
  class RenderAndDie < StandardError
  end
end
class ApplicationController < ActionController::API
  # sth...

    # 类比PHP die()
  rescue_from DiyExceptions::RenderAndDie do |ex|
    msg = JSON(ex.message) rescue [1, "RenderAndDie"]
    resp = {
        code: msg[0],
        info: msg[1]
    }
    render json: resp
    logger.debug ">>>\n#{resp.to_json}<<<\n"
  end


  # 完全成功的请求
  # 直接结束请求
  def success(info)
    info = JSON.parse(info) rescue info if info.instance_of?(String)
    raise DiyExceptions::RenderAndDie, [0, info].to_json
  end


  # 业务上失败的请求, code > 0
  # 直接结束请求
  def failed(code, info)
    info = JSON.parse(info) rescue info if info.instance_of?(String)
    raise DiyExceptions::RenderAndDie, [code, info].to_json
  end

  # sth...
end

如此,使用 failed 或 success 方法就可以啦 ;)

success "操作成功"

success [1,2,3]

success render_to_string json: {list: posts_list, total: posts_total}, include: {:user => {only: [:id, :name, :avatar]}}

其实就是 raise 然后在 rescue_from 回这个异常并且做 response 的处理 和 PHP die 的关系是?

if Rails.env.development?
      p ">>>"

用 logger 会好一些

logger.debug ">>>"

可以避免开发环境和其他环境行为不一样。

我学 ruby 的原因就是不想再碰 php,你居然让我噩梦重演

我也是新手,想问下 raise 这个和这个一样吗

raise ::Unauthorized  XXX
rescue ActiveRecord::RecordNotFound
    render_404
end

对于这个应用场景我的意见是用现有框架里提供的 (before_action)[http://guides.rubyonrails.org/action_controller_overview.html] 来解决问题,而不是把其他语言 / 框架里你所熟悉的实现方法照搬过来。除非你有很好的理由。

@sefier 主楼的实现并没有完全还原 PHP die() 的语义,只是用异常做了个 goto 而已。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号