我有一个我在Rails中构建的API。它运行我在模块中定义的一些方法,并将它们的返回值呈现为JSON。虽然我一直在开发,但API的整个代码一直是模块本身(内容不相关),单一路线:
controller :cool do
get "cool/query/*args" => :query
end
和此:
class CoolController < ApplicationController
include CoolModule
def query
args = params[:args].split("/")
# convert the API URL to the method name
method_symbol = args[0].tr("-","_").to_sym
if !CoolModule.method_defined?(method_symbol)
return nil
end
# is calling self.method a good idea here, or is there a better way?
render json: self.method(method_symbol).call(args[1], args[2])
end
end
我的API(即模块)包含~30个函数,每个函数接受可变数量的参数,我希望将其保存在模块中的路由逻辑(就像现在一样)。
它将被用作我的酷ajax前端和另一个 API之间的“中端”(可能会说),我无法控制它并且实际上是后端适当的。因此,需要特别关注,因为它既接收用户输入又向第三方发送查询(我对此负责)。
我的具体问题是:
我的悲观主义者说'英里case
- when
,但我会感谢你的投入。
答案 0 :(得分:1)
Module#method_defined?的问题是它可能在间接方法定义(其他包含的模块,如果模块是类的继承方法)以及私有方法上返回true。这意味着你(以及接触代码的其他任何人)必须非常小心你对该模块的处理。
因此,您可以使用此方法,但您需要对未来的维护者非常明确,模块中的任何方法都自动为外部接口。就个人而言,我会选择更明确的内容,例如允许api方法名称的简单白名单,例如:
require 'set'
module CoolModule
ALLOWED_API_METHODS = Set[
:foo,
:bar,
...
]
def self.api_allowed? meth
ALLOWED_API_METHODS.include? meth.to_sym
end
end
是的,你必须维护这个列表,但这并不难看,它是一个显式接口的文档;并且意味着你不会被后来的编码人员判断为了方便他们需要在模块中添加一些实用工具方法,从而意外地将它们导出到你的外部api。
或者对于单个列表,您可以使用define_for_api方法并使用它来代替def来声明api接口方法
module CoolModule
@registered_api_methods = Set.new
def self.define_for_api meth, &block
define method meth, &block
@registered_api_methods << meth
end
def self.api_allowed? meth
@registered_api_methods.include? meth.to_sym
end
def api_dispatch meth, *args
raise ArgumentError unless self.class.api_allowed? meth
send(meth *args)
end
define_for_api :foo do |*args|
do_something_common
...
end
define_for_api :bar do
do_something_common
...
end
# this one is just ordinary method internal to module
private
def do_something_common
end
end