Rails API 项目里,如果请求的参数个数较多,需要验证的类型也比较复杂且各个请求之间差异也比较大,在 server 里用什么架构去验证比较好?

realwol · December 14, 2016 · Last by yingce replied at December 19, 2016 · 4310 hits

一般的,可以用 routes,callback 或者在方法中来简单验证。如果是碰到如题的这种环境,怎么做比较好?
像 grape 一样的参数校验应该也算是额外封装了一个步骤去验证每个 api 的请求是否合理吧。
或者还有什么比较好的办法实现?gem 也可以,说出来相互拓展一下思路。

可以看下 ruby-china api 下的 base_controller 非常漂亮易懂

或者看下rails_param

很多人批评 hanami 将一个 action 独立成一个类的方式,但这其实赋予了 action 更大的灵活性。使用 rails_param,你的 action 很快就会充满各种参数过滤清洗逻辑,当然,你可以对其进行抽取分离,但是效果也并不是很好。反观 hanami-validation,一方面参数过滤清晰表达能力更强,另一方面能够跟 action 的逻辑进行有效分离。

推荐 hanami-validation 或者 dry-validation (其实他们是一伙的),至于怎样整合进入 Rails,我目前是在 application_controller 里面加入一个 before_action, 根据对应的 controller+action 按照某种约定查找对应的 validation,暴露一个 sanitized_params helper 方法给后续的 action 逻辑

#2 楼 @novtopro 见过所有的 api 请求全部先进同一个 controller 进行一些处理,然后再 redirect_to 到相应的 controller,感觉没有你这个办法好。

接口还是以资源 (model) 为单位会好管理一些吧

rails_param 这个 gem 有些小 bug,而且已经没人维护了

#3 楼 @adamshen 这个有点像 taobao 或者百度的那种微服务接口吧

#3 楼 @adamshen 之前有过将参数存入表中,然后根据方法和参数关系,调用起来一次检查,等于多一层服务出来专门做这个事情。这样的做可以将方法的维护和文档关联起来,文档先行,对于大型的复杂应用还是挺有用的。不过一般情况下,不是很建议这样做,因为项目达到这种程度,应该是可以通过分割或者抽取来进行简化。

#6 楼 @yingce 没有实际用过微服务,不是很了解。我想应该就是像楼上说的,当项目需要的动态路由、参数检查、权限控制复杂度很高的情况下,再抽象出一层服务来单独做这些事情比较合适。

#2 楼 @novtopro 给点参考链接,光这么说很难领会。

@realwol

ApplicationController

class Api::BaseController < ActionController::API
  before_action :sanitize

  helper_method :sanitized_params

  private

  def sanitized_params
    @sanitized_params ||= begin
      sanitizer ? sanitization.output : request.parameters
    end
  end

  def sanitize
    render_errors(sanitization.messages, :bad_request) && return if sanitization && sanitization.failure?
  end

  def sanitization
    @sanitization ||= sanitizer ? sanitizer.call(request.parameters.to_h) : nil
  end

  def sanitizer
    @sanitizer ||= begin
      controller_without_suffix = request.controller_class.to_s.match(/^(.*)Controller$/)[1]
      action                    = params[:action].titleize
      "#{controller_without_suffix}::#{action}Validation".safe_constantize
    end
  end
end

A simple sanitizer: Posts::IndexValidation

class Api::V1::Posts::IndexValidation < Api::V1::Validation
  validations do
    optional(:sort).filled
    optional(:filter).filled
    optional(:page).schema do
      optional(:number).filled
      optional(:size).filled
    end
  end
end

PostsController

class Api::V1::PostsController < Api::BaseController
  def index
    # Use sanitized_params here
  end
end

#12 楼 @novtopro 可行,不过 validation 似乎需要单独文件,对文件名有特殊要求吗?维护起来代价大吗?

  1. 需要单独的文件
  2. 文件名按照约定来,Rails 强调的不也是 Convention over Configuration 嘛
  3. 当然需要维护,也不算大。

因为我目前也是按照 JSON API 规范去构建 API,我 Explore 了一下其它的参数校验清洗 gem,包括 strong_parameters, rails_param 感觉都不是很满意,hanami-validation 和 dry-validation 的逻辑表达能力实在太好了,于是就用了。

其实我也不知道这种方式好不好,不过就目前来看也还 OK

如果只要求简单的校验清洗,我觉得就没有必要搞那么复杂。

#15 楼 @novtopro 是,简单的可以不去讨论,所以就当我题目中说的那种请求类型是个讨论前提。
rails_params 这种实现方式你是有什么顾虑还是觉得哪儿的设计不满意?我倒是感觉挺好哈。各有所爱吧。

#1 楼 @yingce 我在我之前 fork 的 ruby china 项目里没找到 base controller 啊,是更新了还是有单独 api 项目?

#17 楼 @realwol 记错了 是 api/v3/application_controller

https://github.com/ruby-china/homeland/blob/master/app/controllers/api/v3/application_controller.rb

在 controller 使用

optional! :offset, default: 0
optional! :limit, default: 20, values: 1..150

#18 楼 @yingce 能麻烦你发我一下项目地址吗?👄

Rails 写接口的话,可以试一下 apipie 呢

和 strong parameter 一样,为不同的验证需求设计 private method。controller 臃肿的话作为 concern 独立出去。或者扔 model 里面也 ok?

我第一个想到的是 phoenix 的 changeset。

#11 楼 @Rei 说实话,光这网站,看着就比其他的高端。

#21 楼 @42thcoder 这个更偏向于文档生成和管理吧。

#20 楼 @yingce #25 楼 @42thcoder 还有个我认为比较重要的问题就是 错误信息的定制,你们说的这两个我没有深入的用过,不太清楚如何定制,不过据我所知,grape 也是最近的版本里才加入的。不知道两位对这方面有了解吗?

api-pipe 可以验证类型和时候缺失。

#27 楼 @bastengao 支持错误信息定制不

#28 楼 @realwol 支持么,有不同的错误码和消息。我跑个砖,剩下你自己看吧。

#29 楼 @bastengao 那还不错 有啥不好的地方没

#30 楼 @realwol 用的人相对较少。我用的比较浅,我们的 API 也比较简单。

#31 楼 @bastengao 楼上有人说了 我看了下 有机会试试

#26 楼 @realwol 你可以花 5 分钟看看源码,传参数的错误就那几种,每个定义一下就可以了

You need to Sign in before reply, if you don't have an account, please Sign up first.