我正在Rails 4中编写一个非常标准的CRUD RESTful API。虽然我的错误处理很简单。
想象一下,我有以下模型:
class Book < ActiveRecord::Base
validates :title, presence: true
end
如果我尝试创建没有标题的图书对象,我将收到以下错误:
{
"title": [
"can't be blank"
]
}
ActiveRecord验证旨在与Forms一起使用。理想情况下,我希望将每个人类可读验证错误与API使用者可以使用的常量相匹配。如下所示:
{
"title": [
"can't be blank"
],
"error_code": "TITLE_ERROR"
}
这既可用于显示面向用户的错误(“标题不能为空白”),也可用于其他代码(if response.error_code === TITLE_ERROR
...)。在Rails中是否有任何工具?
答案 0 :(得分:9)
在 error_codes.yml 上定义您的标准API错误,包括status_code
,title
,details
以及您可以使用的内部code
提供有关API文档错误的更多信息。
这是一个基本的例子:
api:
invalid_resource:
code: '1'
status: '400'
title: 'Bad Request'
not_found:
code: '2'
status: '404'
title: 'Not Found'
details: 'Resource not found.'
在 config / initializers / api_errors.rb 上将YAML文件加载到常量中。
API_ERRORS = YAML.load_file(Rails.root.join('doc','error-codes.yml'))['api']
在 app / controllers / concerns / error_handling.rb 上定义一个可重用的方法来呈现JSON格式的API错误:
module ErrorHandling
def respond_with_error(error, invalid_resource = nil)
error = API_ERRORS[error]
error['details'] = invalid_resource.errors.full_messages if invalid_resource
render json: error, status: error['status']
end
end
在您的API基础控制器上包含关注点,以便它可以从继承它的所有控制器上获得:
include ErrorHandling
然后,您就可以在任何控制器上使用您的方法:
respond_with_error('not_found') # For standard API errors
respond_with_error('invalid_resource', @user) # For invalid resources
例如,在您的用户控制器上,您可能具有以下内容:
def create
if @user.save(your_api_params)
# Do whatever your API needs to do
else
respond_with_error('invalid_resource', @user)
end
end
您的API将输出的错误如下所示:
# For invalid resources
{
"code": "1",
"status": "400",
"title": "Bad Request",
"details": [
"Email format is incorrect"
]
}
# For standard API errors
{
"code": "2",
"status": "404",
"title": "Not Found",
"details": "Route not found."
}
随着API的增长,您可以轻松地在YAML文件中添加新的错误代码,并使用此方法避免重复,并使您的错误代码在您的API中保持一致。
答案 1 :(得分:1)
你的create方法看起来应该是这样的:
def create
book = Book.new(book_params)
if user.save
render json: book, status: 201
else
render json: { errors: book.errors, error_code: "TITLE_ERROR" }, status: 422
end
end
那会返回看起来像你问的json,除了“title”和“error_code”嵌套在“错误”中。我希望不要处理大问题。
答案 2 :(得分:1)
您只有两种方法可以实现此目的:要么为验证程序编写代码(在验证期间将测试错误的组件),要么编写渲染器。
我假设您知道如何编写渲染器,因为@ baron816的回答是建议的,并且做了一些DRY以以某种方式概括它。
让我引导您完成验证器技术:
1 - 让我们为您的错误代码创建一个存储空间,我称之为custom_error_codes
,我假设您可以一次设置多个错误代码,所以我会这样做使用Array
(否则会更改)。
创建模型问题
module ErrorCodesConcern
extend ActiveSupport::Concern
included do
# storage for the error codes
attr_reader :custom_error_codes
# reset error codes storage when validation process starts
before_validation :clear_error_codes
end
# default value so the variable is not empty when accessed improperly
def custom_error_codes
@custom_error_codes ||= []
end
private
def clear_error_codes
@custom_error_codes = []
end
end
然后将关注点添加到模型中
class MyModel < ActiveRecord::Base
include ErrorCodesConcern
...
end
2 - 让黑客验证程序添加错误代码的标记。首先,我们需要查看验证器源代码,它们位于(activemodel-gem-path)/ lib / active_model / validations / 中。
在 app 目录中创建验证器目录,然后创建以下验证器
class CustomPresenceValidator < ActiveModel::Validations::PresenceValidator
# this method is copied from the original validator
def validate_each(record, attr_name, value)
if value.blank?
record.errors.add(attr_name, :blank, options)
# Those lines are our customization where we add the error code to the model
error_code = "#{attr_name.upcase}_ERROR"
record.custom_error_codes << error_code unless record.custom_error_codes.include? error_code
end
end
end
然后在我们的模型中使用我们的自定义验证器
class Book < ActiveRecord::Base
validates :title, custom_presence: true
end
3 - 因此,您必须修改代码正在使用的所有rails验证器并创建渲染器(请参阅@ baron816&#39; s答案)并使用模型{{1进行响应价值。
答案 3 :(得分:1)
看起来您似乎没有考虑多个验证错误。
在您的示例中,Book
模型只有一个验证,但其他模型可能有更多验证。
我的回答包含第一个解决多个验证的解决方案和另一个仅使用模型上找到的第一个验证错误的解决方案
在ApplicationController中添加它
# Handle validation errors
rescue_from ActiveRecord::RecordInvalid do |exception|
messages = exception.record.errors.messages
messages[:error_codes] = messages.map {|k,v| k.to_s.upcase << "_ERROR" }
render json: messages, status: 422
end
请注意,在这种情况下,error_codes
是一个允许多个错误代码的数组。例如:
{
"title": [
"can't be blank"
],
"author": [
"can't be blank"
],
"error_codes": ["TITLE_ERROR", "AUTHOR_ERROR"]
}
如果您确实只想保留一个验证错误,请改用
# Handle validation errors
rescue_from ActiveRecord::RecordInvalid do |exception|
key = exception.record.errors.messages.keys[0]
msg = exception.record.errors.messages[key]
render json: { key => msg, :error_code => key.to_s.upcase << "_ERROR" }, status: 422
end
会给你一个像
这样的回复{
"title": [
"can't be blank"
],
"error_code": "TITLE_ERROR"
}
即使您有多个错误
答案 4 :(得分:1)
试试这个:
book = Book.new(book_params)
if user.save
render json: book, status: 201
else
render json: {
errors: book.errors,
error_codes: book.errors.keys.map { |f| f.upcase + "_ERROR" }
},
status: 422
end
error_codes
将返回多个错误代码。