Grape API validation
Bài đăng này đã không được cập nhật trong 9 năm
Việc sử dụng gem Grape trong rails đôi khi cần đến việc validation params, một vài tip nhỏ hi vọng giúp bạn chủ động hơn trong việc xử lý validation.
Link về gem Grape: https://github.com/ruby-grape/grape
Validation trong Grape có nhiều phần khác nhau, validation params là việc bắt validation tại đầu vào params của API.
Ta thường có:
resources :order do
params do
requires :mode, type: Integer, values: [0, 1]
optional :okan_info, type: Hash do
optional :okan_id, type: String, max_length: 255, allow_blank: false
optional :nominate_flg, type: Boolean
end
requires :start_time, type: DateTime, coerce_with: ->(val){Time.zone.parse(val).to_datetime.utc}
requires :job_kind, type: Array[String]
requires :absence_flg, type: Boolean
optional :repeat_pattern, type: Integer
optional :coupon_id, type: Integer
optional :notice_time, type: Integer, values: Settings.orders.notice_time
requires :work_time, type: Integer,
values: [*Settings.orders.min_work_time..Settings.orders.max_work_time]
end
...
end
Muốn hạn chế gía trị của params ta dùng values. (https://github.com/ruby-grape/grape#parameter-validation-and-coercion)
Muốn convert gía trị của params ta dùng coerce_with. (https://github.com/ruby-grape/grape#custom-types-and-coercions)
Tại ví dụ ta thấy có requires :start_time, type: DateTime, coerce_with: ->(val){Time.zone.parse(val).to_datetime.utc}, ý nghĩa của đọa code này là parse gía trị của start_time về gía trị utc.
Việc sử dụng coerce_with cần được catch bằng cách check message_key của error
rescue_from Grape::Exceptions::ValidationErrors do |e|
e.each do |_param, errors|
case errors[:message_key]
...
when :coerce
...
else
...
end
end
end
Muốn tạo thêm 1 dạng validation mới, chỉ cần địng nghĩa thêm 1 class
module APIValidation
class MaxLength < Grape::Validations::Base
def validate_param! attr_name, params
return if params[attr_name].nil? || params[attr_name].try(:length).try(:<=, (@option))
raise Grape::Exceptions::Validation,
params: [@scope.full_name(attr_name)],
headers: Settings.api_validation.bad_digit_params
end
end
end
Đây là validation max_length cho độ dài params là string, sử dụng như những validation khác: optional :okan_id, type: String, max_length: 255, allow_blank: false.
validate_param! là method của Grape::Validations::Base:
module Grape
...
module Validations
class CoerceValidator < Base
def initialize(*_args)
super
(@converter = Types.build_coercer(type, (@option[:method])))
end
def validate_param!(attr_name, params)
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
new_value = coerce_value(params[attr_name])
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless valid_type?(new_value)
params[attr_name] = new_value
end
end
end
end
Ở đây ta kế thừa lại class Grape::Validations::Base và override lại hàm validate_param!.
Đoạn code headers: Settings.api_validation.bad_digit_params dùng để mark lại dạng error và lưu tại headers, khi catch lại thì lấy ra như đối với message_key.
Muốn thêm validation có liên hệ giữa các params với nhau, cũng làm tương tự cách trên, gỉa sử params đầu vào có search_start_time, search_start_time, và điều kiện là search_end_time - search_start_time >= 100.days.
params do
optional :search_start_time, type: Date
optional :search_end_time, type: Date, greater_than: [:search_start_time, 100]
all_or_none_of :search_start_time, :search_end_time
end
Tại module định nghĩa error
class GreaterThan < Grape::Validations::Base
def validate_param! attr_name, params
return if params[attr_name].nil? ||
params[attr_name].try( :>=, params[@option[0]] + (@option[1].days))
raise Grape::Exceptions::Validation,
params: [@scope.full_name(attr_name)],
headers: Settings.api_validation.bad_format_params
end
end
Đoạn code greater_than: [:search_start_time, 100] truyền các gía trị cần so sánh vào @option khi kiểm tra validate tại hàm validate_params: return if params[attr_name].nil? || params[attr_name].try( :>=, params[@option[0]] + (@option[1].days))
Ta có thể custom lại các validation như all_or_none_of, at_least_one_of ... bằng cách định nghĩa lại:
class AllOrNoneOfValidator < Grape::Validations::MultipleParamsBase
def validate! params
...
end
end
Chú ý Grape::Validations::MultipleParamsBase là class base cho việc validate multiple params, tuy nhiên cách này không nên sử dụng tùy tiện vì ảnh hưởng đến hàm chuẩn trong Grape.
Cảm ơn và hi vọng bài viết gíup ích phần nào trong công việc của bạn.
All rights reserved