我们想要在使用devise登录的时候实现用在form提交时候使用remote: true
来提交表单。表单提交通过之后使用Turbolinks跳转到其他页面。
如果有验证错误则在当前页面显示验证错误。
为什么要这样做? 因为在Turbolinks-ios中无法用html方法提交表单。提交表单之后的跳转也必须由turbolinks来完成。
首先准备好需要render的js模版。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <% if resource && resource.errors.empty? %> <% flash[:success] = "登录成功"; %> Turbolinks.visit('<%=@redirect_to %>',{ action: "replace" }); <% else %> $('#headFlash').html(` <div class="alert alert-warning alert-dismissible fade in" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> <span><%= flash[:alert] %></span> </div> `); <% end %>
|
我们需要overwrite devise的sessionsController中的create方法。
1 2 3 4 5 6 7 8 9
| class Users::SessionsController < Devise::SessionsController def create self.resource = warden.authenticate!(auth_options) sign_in(resource_name, resource) yield resource if block_given? @redirect_to = after_sign_in_path_for(resource) end end
|
注意需要在router配置中标明我们进行了Devise::SessionsController
的overwrite。
跑一下。登录成功,跳转。
故意输错密码,登录失败,但是直接server端返回了401,而并没有渲染create.js.erb
模版。
这个是为什么?
重点在这里。warden.authenticate!(auth_options)
warden的默认策略直接跳过下面的步骤直接返回给我们401了。我们需要告诉warden我们期待的是什么处理。
这里需要对oauth_options中的recall 进行overwrite。
注意到使用remote: true
默认的request的format是:js
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Users::SessionsController < Devise::SessionsController respond_to :js def create self.resource = warden.authenticate!(auth_options) sign_in(resource_name, resource) yield resource if block_given? @redirect_to = after_sign_in_path_for(resource) end def failure render :create end protected def auth_options { scope: resource_name, recall: "#{controller_path}#failure" } end end
|
故意输错一下试一试,特么为什么还是直接返回401而没有使用我们定义的failure方法?
去看一下devise initializer里面配置的说明。
1 2 3 4 5 6 7 8 9
| # ==> Navigation configuration # Lists the formats that should be treated as navigational. Formats like # :html, should redirect to the sign in page when the user does not have # access, but formats like :xml or :json, should return 401. # If you have any extra navigational formats, like :iphone or :mobile, you # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. # config.navigational_formats = ['*/*', :html]
|
那么就是说只要config.navigational_formats = ['*/*', :html, :js]
就ok了。
添加之后再试一下,还是不行。搜索一番。据说要添加
1
| config.http_authenticatable_on_xhr = false
|
这特么是什么鬼。看一下devise default的failure app。
1 2 3 4 5 6 7 8 9
| def respond if http_auth? http_auth elsif warden_options[:recall] recall else redirect end end
|
我们看到只有http_auth?
为false我们才会调用recall。
1 2 3 4 5 6 7
| def http_auth? if request.xhr? Devise.http_authenticatable_on_xhr else !(request_format && is_navigational_format?) end end
|
可以看到我们只有设置Devise.http_authenticatable_on_xhr
为 false才使得验证失败时候的处理落到我们自己定义的recall中。
以上。