Grape API validation
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 ...
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.