`
woody_420420
  • 浏览: 42057 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

为Rails中的validation error增加error_code

阅读更多

  各位同学对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并不支持该功能,那我们就只能挽起衣袖,自己动手。
  首先,rails中关于model的validation,error。。。相关源代码请参见:activerecord-2.0.2\lib\activerecord\validation.rb。源代码不是很复杂,这里不详细阐述,只给出我目前找到的解决方案。
  由于error这个东西在rails中的实现并不是十分OO(Model#errors是一个hash,rails将validation中出现的错误都塞到里面),因此我的办法颇费周折,可谓很暴力。正是由于errors是hash,不是一个一个error对象,因此,没办法很轻巧的增加一个error_code属性,我采用了增加另外一个hash:error_with_code来表示error信息和error_code信息。这样做的好处是不会影响原有的error逻辑。

  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!修改工作完成!(不要忘了十分暴力的部分)下面来完成的看一下如何使用。
  首先,在我们的Model中所有的validates_***_of 方法中增加:code => 2008之类的error_code,

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 星期二

分享到:
评论
4 楼 woody_420420 2008-08-07  
我的目的是将error信息按照某种xml结构形式返回给客户端,而不仅仅是通过<%= %>将一条message格式化为: Error Message... Code:#code。显示出来。这仍然是通过手工操作字符串的方式,不是我想要的。而且,这种add方式无法为Model中大量的validate_***_of增加error_code。
3 楼 hozaka 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"] ) %>

在辅助方法里进行格式化
2 楼 woody_420420 2008-08-06  
<p>1.比如添加 errors 到 “api” 属性
</p>
<p>没搞懂什么意思。能否详细说明下?</p>
<p> </p>
<p>2.或者使用格式化的 message</p>
<p>
这个是最简单,无侵入的办法。不过太不用户友好了,如果客户端要显示message,还得手工操作这个message字符串。所以一早就没打算这么干。
</p>
<p> </p>
<p>
的确monkey pattch存在很多问题,不过我虽然monkey了,但是原来的逻辑我全部没动。比如增加了@errors_with_code,而不是修改@error,增加了full_messages_with_error_code,而不是修改full_messages。关于方法的调用,使用了默认参数值,即使某个插件按照rails原有方法调用validation,也不会有问题。。。目前就我能看到的情况来看,应该不会有什么问题。
不过确实这种方法不能算好,只是我现在还没找到更好的办法,希望能得到更多的建议!</p>
1 楼 hozaka 2008-08-06  
<p>MonkeyPatching</p>
<p> </p>
<p>非常不推荐这样做,在升级项目版本的时候会遇到无尽的麻烦,修改公用的代码还有可能带来兼容性的问题,比如某个插件调用 validation 产生莫名奇妙的错误。
只是简单的给 Web API 添加 Error Code,有很多取巧的办法,比如添加 errors 到 “api” 属性,或者使用格式化的 message,尽可能把对 Rails 的修改减到最少</p>

相关推荐

    Rails_Recipes_with_Source_Code

    《Rails Recipes with Source Code》是一本专注于Ruby on Rails框架实践技巧和源代码解析的书籍。Rails是基于Ruby语言的Web开发框架,以其“约定优于配置”(Convention over Configuration)的理念和“开发人员的...

    RestFul_Rails_Dev_pdf_v_0.1.zip

    本资料“RestFul_Rails_Dev_pdf_v_0.1.zip”包含了《RESTful Rails Development》的翻译版,将深入探讨如何在Rails中实现RESTful的设计模式。 首先,RESTful设计的核心概念是资源(Resources)。在Rails中,资源...

    rails_admin_acts_as_list:rails_admin插件以对记录进行排序

    在您的config/initializers/rails_admin.rb初始化程序中添加配置: RailsAdmin . config do | config | config . model Post do list do sort_by :position # Add Default sorting sort_reverse false # sort p

    Rails里给文件上传添加progress_bar

    在Ruby on Rails(Rails)框架中,为文件上传添加进度条功能可以显著提升用户体验,让用户在上传大文件时能够清楚地看到进度,增加交互性。本文将深入探讨如何在Rails应用中实现这一功能。 首先,我们需要理解文件...

    用于过滤英文脏话的 Rails 插件 gem_Ruby_代码_相关文件_下载

    这个插件名为`profanity_filter`,它是一个基于Ruby的库,可以集成到Rails应用中。`gem`是Ruby中的一个包管理器,用于安装、管理和分发代码库。通过在Rails项目中引入`profanity_filter` gem,你可以轻松地过滤掉...

    IkaGame示例。使用 Rails5 ActionCable_ruby_代码_下载

    通过研究这个示例,开发者可以掌握ActionCable的基本用法,了解如何在Rails5中实现WebSocket通信,以及如何将实时更新集成到现有的Rails应用程序中。此外,这个示例还展示了如何处理游戏状态的同步,这对于开发多人...

    rails_model_test_hello_world

    自述文件版本和设置$ ruby -vruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin18]$ rails -... $ rails new rails_model_test_hello_world -T -m ~/rtfb_template.rb$ cd rails_model_test_hello_world$ rail

    用于测量 gem 的 Rails 适配器,在 Ruby 和 Rails 中封装测量值及其单位_Ruby_代码_相关文件_下载

    这个 gem 是用于测量gem 的 Rails 集成。 它提供了 ActiveRecord 适配器,用于保存和检索测量值及其单位和模型验证。 用法 活动记录 列应具有_valueand_unit后缀,并且是DECIMALand VARCHAR,并且接受默认值。支持...

    Ruby on Rails Guides v2 - Ruby on Rails 4.2.5

    - **效果**:这将在应用中增加一个新的URL路径,指向指定控制器的动作。 #### 七、渲染视图 - **方法**:在控制器中使用`render`方法来显示特定的视图文件。 - **视图文件**:通常使用ERB模板语言来编写视图文件,...

    使用Rails4Devise和Grape验证API_JavaScript_CSS_下载.zip

    在压缩包中的"authenticating_api_rails_devise-master"可能是一个Git仓库的名称,表明这个项目是用Git版本控制系统管理的,"master"分支通常代表开发的主要分支。这个目录可能包含以下文件和目录结构: - Gemfile...

    Ruby-on-Rails-rails.zip

    Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zipRuby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zip Ruby_on_Rails_rails.zipRuby_on_...

    支持 openSUSE ( TSP ) 的旅行支持计划的 rails 应用程序_ruby_代码_下载

    这是一个基于 Ruby on Rails 的应用程序,用于管理来自诸如 openSUSE Travel Support Program、GNOME 的 Conference Travel Subsidy Program 或 KDE eV Travel Cost Reimbursement 计划等自由软件组织的旅行帮助计划...

    rails_fix_google_bot_accept:针对 Google 机器人请求的 Rails 修复

    gem 'rails_fix_google_bot_accept' 然后执行: $ bundle 就是这样。 来源 gem 基于这个要点: : 它是由 Romain Champourlier 制作的,作为对这个问题的回答: ://stackoverflow....

    rails_get_to_the_fest

    标题 "rails_get_to_the_fest" 暗示着这是一个关于Ruby on Rails的项目,可能是为某种节日或活动创建的Web应用。Rails是基于Ruby语言的开源Web开发框架,它遵循MVC(模型-视图-控制器)架构模式,使得构建数据库驱动...

    03-2 . 安装不同版本的Rails与产生Rails 5.x版专案

    [Ruby_on_Rails][中文][Rails_5.x]__03-2_._安裝不同版本的Rails與產生Rails_5.x版

    Rails101_by_rails4.0

    书中介绍了Ruby on Rails安装的最佳实践,以及如何通过Git、编辑器和Linux命令行等前置技能的学习,为后续的Rails开发打下基础。Git作为版本控制系统,对于团队协作开发项目尤为重要,学习它的使用方法能够帮助...

    rails_react_flights_code_along

    自述文件 该自述文件通常会记录启动和运行应用程序所需的所有步骤。 您可能要讲的内容: Ruby版本 系统依赖 配置 数据库创建 数据库初始化 如何运行测试套件 服务(作业队列,缓存服务器,搜索引擎等) ...

    [Ruby on Rails][API][Rails 5.x] 01. Rails 与 Web API 介绍

    [Ruby_on_Rails][API][Rails_5.x]__01._Rails_與_Web_API_介紹

    rails_apps_composer, 一个 gem,为 Rails 启动应用程序创建 Rails 应用程序模板.zip

    rails_apps_composer, 一个 gem,为 Rails 启动应用程序创建 Rails 应用程序模板 Rails 应用编辑器 Rails 应用程序编辑器 gem 安装一个 命令行 工具来从"食谱"的Collection 组装 Rails 应用程序。"你可以使用 rails_...

Global site tag (gtag.js) - Google Analytics