ruby-on-rails – 使用devise_token_auth和active_model_seriali
在为devise sign_up方法使用devise_token_auth和active_model_serializer时,我无法覆盖Rails序列化程序.
我想在查询我的API时自定义Devise sign_up控制器返回的字段. devise_token_auth gem文档指出: 要自定义json呈现,请实现以下受保护的控制器方法 注册管理员 … render_create_success … 注意:控制器覆盖必须实现它们替换的控制器的预期操作. 这一切都很好,但我该怎么做? 我已经尝试生成如下的UserController序列化程序: class UsersController < ApplicationController def default_serializer_options { serializer: UserSerializer } end # GET /users def index @users = User.all render json: @users end end 但它只用于自定义方法,例如上面的索引方法:它没有像sign_up这样的设计方法被拾取 我很欣赏一个详细的回复,因为我到处寻找,但我一次只得到一块拼图. 解决方法
Devise sign_up对应devise_token_auth注册控制器,Devise sign_in对应devise_token_auth会话控制器.因此,在使用此gem时,自定义Devise sign_in和sign_up方法需要自定义这两个devise_token_auth控制器.
根据您需要完成的任务,有两种方法可以解决这个问题. 方法#1 如果要在控制器中完全自定义方法,请按照文档覆盖devise_token_auth控制器方法:https://github.com/lynndylanhurley/devise_token_auth#custom-controller-overrides 这就是我做的,它的工作正常: #config/routes.rb ... mount_devise_token_auth_for 'User',at: 'auth',controllers: { sessions: 'overrides/sessions',registrations: 'overrides/registrations' } ... 如果本地控制器覆盖中存在方法,则会将所有devise_token_auth会话和注册路由到LOCAL版本的控制器.如果您的本地覆盖中不存在该方法,那么它将从gem运行该方法.您基本上必须将控制器从gem复制到’app / controllers / overrides’,并对您需要自定义的任何方法进行任何更改.从您未自定义的本地副本中删除方法.您也可以通过这种方式添加回调.如果要修改响应,请在方法末尾自定义呈现,该呈现将通过active_model_serializer将响应作为json返回. 这是我的会话控制器的一个示例,它添加了几个自定义before_actions来添加自定义功能: #app/controllers/overrides/sessions_controller.rb module Overrides class SessionsController < DeviseTokenAuth::SessionsController skip_before_action :authenticate_user_with_filter before_action :set_country_by_ip,:only => [:create] before_action :create_facebook_user,:only => [:create] def create # Check field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first @resource = nil if field q_value = resource_params[field] if resource_class.case_insensitive_keys.include?(field) q_value.downcase! end #q = "#{field.to_s} = ? AND provider='email'" q = "#{field.to_s} = ? AND provider='#{params[:provider]}'" #if ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'mysql' # q = "BINARY " + q #end @resource = resource_class.where(q,q_value).first end #sign in will be successful if @resource exists (matching user was found) and is a facebook login OR (email login and password matches) if @resource and (params[:provider] == 'facebook' || (valid_params?(field,q_value) and @resource.valid_password?(resource_params[:password]) and (!@resource.respond_to?(:active_for_authentication?) or @resource.active_for_authentication?))) # create client id @client_id = SecureRandom.urlsafe_base64(nil,false) @token = SecureRandom.urlsafe_base64(nil,false) @resource.tokens[@client_id] = { token: BCrypt::Password.create(@token),expiry: (Time.now + DeviseTokenAuth.token_lifespan).to_i } @resource.save sign_in(:user,@resource,store: false,bypass: false) yield @resource if block_given? #render_create_success render json: { data: resource_data(resource_json: @resource.token_validation_response) } elsif @resource and not (!@resource.respond_to?(:active_for_authentication?) or @resource.active_for_authentication?) render_create_error_not_confirmed else render_create_error_bad_credentials end end def set_country_by_ip if !params['fb_code'].blank? if !params['user_ip'].blank? #checks if IP sent is valid,otherwise raise an error raise 'Invalid IP' unless (params['user_ip'] =~ Resolv::IPv4::Regex ? true : false) country_code = Custom::FacesLibrary.get_country_by_ip(params['user_ip']) country_id = Country.find_by(country_code: country_code) if country_id params.merge!(country_id: country_id.id,country_name: country_id.name,test: 'Test') I18n.locale = country_id.language_code else params.merge!(country_id: 1,country_name: 'International') end else params.merge!(country_id: 1,country_name: 'International') end end end def create_facebook_user if !params['fb_code'].blank? # TODO capture errors for invalid,expired or already used codes to return beter errors in API user_info,access_token = Omniauth::Facebook.authenticate(params['fb_code']) if user_info['email'].blank? Omniauth::Facebook.deauthorize(access_token) end #if Facebook user does not exist create it @user = User.find_by('uid = ? and provider = ?',user_info['id'],'facebook') if !@user @graph = Koala::Facebook::API.new(access_token,ENV['FACEBOOK_APP_SECRET']) Koala.config.api_version = "v2.6" new_user_picture = @graph.get_picture_data(user_info['id'],type: :normal) new_user_info = { uid: user_info['id'],provider: 'facebook',email: user_info['email'],name: user_info['name'],first_name: user_info['first_name'],last_name: user_info['last_name'],image: new_user_picture['data']['url'],gender: user_info['gender'],fb_auth_token: access_token,friend_count: user_info['friends']['summary']['total_count'],friends: user_info['friends']['data'] } @user = User.new(new_user_info) @user.password = Devise.friendly_token.first(8) @user.country_id = params['country_id'] @user.country_name = params['country_name'] if !@user.save render json: @user.errors,status: :unprocessable_entity end end #regardless of user creation,merge facebook parameters for proper sign_in in standard action params.merge!(provider: 'facebook',email: @user.email) else params.merge!(provider: 'email') end end end end 注意params.merge的使用!在回调中将自定义参数添加到主控制器方法中.这是一个非常好的技巧,遗憾的是在Rails 5.1中将不推荐使用,因为params将不再继承hash. 方法#2 如果您只想在自定义控制器中为方法添加功能,则可以继承子控制器,继承原始控制器并将块传递给super,如下所述: https://github.com/lynndylanhurley/devise_token_auth#passing-blocks-to-controllers 我已经在我的自定义注册控制器中对create方法执行了此操作. 按方法#1修改路由 #config/routes.rb ... mount_devise_token_auth_for 'User',registrations: 'overrides/registrations' } ... 并自定义自定义控制器中的create方法: #app/controllers/overrides/registrations_controller.rb module Overrides class RegistrationsController < DeviseTokenAuth::RegistrationsController skip_before_action :authenticate_user_with_filter #will run upon creating a new registration and will set the country_id and locale parameters #based on whether or not a user_ip param is sent with the request #will default to country_id=1 and locale='en' (International) if it's not sent. before_action :set_country_and_locale_by_ip,:only => [:create] def set_country_and_locale_by_ip if !params['user_ip'].blank? #checks if IP sent is valid,otherwise raise an error raise 'Invalid IP' unless (params['user_ip'] =~ Resolv::IPv4::Regex ? true : false) country_code = Custom::FacesLibrary.get_country_by_ip(params['user_ip']) #TODO check if there's an internet connection here or inside the library function #params.merge!(country_id: 1,country_name: 'International',locale: 'en') country_id = Country.find_by(country_code: country_code) if country_id params.merge!(country_id: country_id.id,locale: country_id.language_code,country_name: country_id.name) else params.merge!(country_id: 1,locale: 'en') end else params.merge!(country_id: 1,locale: 'en') end end #this will add behaviour to the registrations controller create method def create super do |resource| create_assets(@resource) end end def create_assets(user) begin Asset.create(user_id: user.id,name: "stars",qty: 50) Asset.create(user_id: user.id,name: "lives",qty: 5) Asset.create(user_id: user.id,name: "trophies",qty: 0) end end end end (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |