该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-08-05
各位同学对model中一坨坨的 validates_presence_of :name, :link 之类种种的代码不会觉得陌生。在执行save,update操作,rails会自动执行validation操作,并将错误信息存放在Model#errors中。通常,对于一般web程序来将,这就够了。我们可以将validation过程中的所有错误信息显示给用户,以进行修改。但是,在web api中,则没这么简单。一般来说,api通过xml返回结果,如果用户调用一个api的时候(比如通过调用api保存一个blog),如果这个时候validation出错了怎么办呢?如果只是简单的调用Model#errors#to_xml,返回给用户,那么其结果类似: <errors> <error>name can't be blank</error> </errors> 当然,如果api用户只是发现有错误,将message简单显示,那没问题。如果客户端要针对错误进行更加灵活的操作应该怎么办呢(类似于我们在程序中自定义很多应用程序逻辑相关的Exception)?不可能针对每一个error的message来进行针对性处理,这个时候,我们就需要向error中添加和应用程序逻辑相关的error_code。rails并不支持该功能,那我们就只能挽起衣袖,自己动手。 class Errors @@default_error_codes = { :default => 101 } def initialize(base) # :nodoc: @base, @errors,@errors_with_code = base, {},{} end end 在Errors类中,我还定义了一个@@default_error_codes Hash,用来存放默认的error_codes,这样方便以后的扩展(先不详细讲述,先完成主干部分)。这里,你完全可以把@errors_with_code看成和@errors一样的东西,只不过,他多了一个error_code。那具体是怎么加入这个error_code的呢?我们继续 def add(attribute, msg = @@default_error_messages[:invalid],error_code = @@default_error_codes[:default]) @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil? @errors[attribute.to_s] << msg @errors_with_code[attribute.to_s] = [] if @errors_with_code[attribute.to_s].nil? @errors_with_code[attribute.to_s] << [ msg, error_code] end 上面的add函数的前两行是rails的默认行为,他只是将error message存放在errors中,后面两行是我为rails增加的行为,将error_code和message封装成一个数组,存放在errors_with_code中。如果你查看rails validation的源代码,会发现,诸如validates_confirmation_of,validates_acceptance_of。。。之类我们熟悉的validation代码会调用这个add方法,为了使得可以自定义error_code,下面最最暴力的时候来了,在所有的validates_***_of的方法中,我们必须重写对add方法的调用,以将error_code塞进去,比如,针对validates_format_of方法,我们需要做如下重写: def validates_format_of(*attr_names) configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil } configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp) validates_each(attr_names, configuration) do |record, attr_name, value| record.errors.add(attr_name, configuration[:message],configuration[:code]) unless value.to_s =~ configuration[:with] end end 请注意"configuration[:code]",这是和rails实现唯一不同的地方。有了他,我们在写validates_format_of的时候,就可以这样用: validates_format_of :link, :with => /.../,:message => "error format of link",:code => 3001 这里的:code就是和应用程序相关error_code。至此,error_code的算是增加到了rails中。下面来看看我们怎么将结果(api返回错误信息的结果)返回。同样,这里,我不打算重写rails的默认to_xml行为,因为可能会影响其他程序的运作,所以,我在Error类中增加了一个to_xml_with_error_code方法: def to_xml_with_error_code(options={}) options[:root] ||= "errors" options[:indent] ||= 2 options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) options[:builder].instruct! unless options.delete(:skip_instruct) options[:builder].errors do |e| full_messages_with_error_code.each { |msg,code| e.error(msg,"error_code" => code) } end end 和rails内部实现不同之处只是倒数第三行,我使用的是full_messages_with_error_code,而不是rails本身的full_messages,并且将error_code生成在了xml中("error_code" => code),下面是具体实现: def full_messages_with_error_code # :nodoc: full_messages = {} @errors_with_code.each_key do |attr| @errors_with_code[attr].each do |msg| next if msg.nil? msg = [ msg ].flatten msg_text , msg_error_code = msg if attr == "base" full_messages[msg_text] = msg else full_messages[@base.class.human_attribute_name(attr) + " " + msg_text] = msg_error_code end end end return full_messages end rails内部的full_messages是一个数组,这里我使用的是一个Hash,为了方便message和error_code的对用。OK!修改工作完成!(不要忘了十分暴力的部分)下面来完成的看一下如何使用。 validates_format_of :link, :with => /..../,:message => "error format of link",:code => 3001 如果validation过程中有错误,则调用: model.errors.to_xml_with_error_code 将错误信息返回给客户,这样,客户端可以得到如下的错误结果: <errors> <error error_code="3001">Link error format of link</error> </errors> OK!现在客户端就可以针对error_code执行相应的逻辑处理!
2008.8.5 22:48 星期二 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-08-06
MonkeyPatching
非常不推荐这样做,在升级项目版本的时候会遇到无尽的麻烦,修改公用的代码还有可能带来兼容性的问题,比如某个插件调用 validation 产生莫名奇妙的错误。 只是简单的给 Web API 添加 Error Code,有很多取巧的办法,比如添加 errors 到 “api” 属性,或者使用格式化的 message,尽可能把对 Rails 的修改减到最少 |
|
返回顶楼 | |
发表时间:2008-08-06
1.比如添加 errors 到 “api” 属性 没搞懂什么意思。能否详细说明下?
2.或者使用格式化的 message 这个是最简单,无侵入的办法。不过太不用户友好了,如果客户端要显示message,还得手工操作这个message字符串。所以一早就没打算这么干。
的确monkey pattch存在很多问题,不过我虽然monkey了,但是原来的逻辑我全部没动。比如增加了@errors_with_code,而不是修改@error,增加了full_messages_with_error_code,而不是修改full_messages。关于方法的调用,使用了默认参数值,即使某个插件按照rails原有方法调用validation,也不会有问题。。。目前就我能看到的情况来看,应该不会有什么问题。 不过确实这种方法不能算好,只是我现在还没找到更好的办法,希望能得到更多的建议! |
|
返回顶楼 | |
发表时间:2008-08-07
1. article.errors.add( "api", "Error Message #code" )
2. Helper Method: error_messages_with_code( errors ) 3. <%= error_messages_with_code( article.errors["api"] ) %> 在辅助方法里进行格式化 |
|
返回顶楼 | |
发表时间:2008-08-07
我的目的是将error信息按照某种xml结构形式返回给客户端,而不仅仅是通过<%= %>将一条message格式化为: Error Message... Code:#code。显示出来。这仍然是通过手工操作字符串的方式,不是我想要的。而且,这种add方式无法为Model中大量的validate_***_of增加error_code。
|
|
返回顶楼 | |
浏览 3663 次