前面我们讲的link_to、form_for、form_tag都是rails的前端的helpers方法(翻译过来就是辅助方法)。helpers方法还有很多,当然这节课我们要讲的helpers方法不是前端使用的helpers方法,而是控制器或模型中能够直接调用的一些辅助方法,并且我们能够自定义方法的名称以及里面的一些功能,这样的helpers方法我们可以称之为自定义的helpers方法(下面我们都叫自定义方法好了)。
前面我们讲过我们可以将自定义的库文件写在lib文件夹中,但是这种文件一般代码量很大独立成文件,暴露接口给用户去使用,如果是一些比较简短的小方法小接口想在工程中去调用怎么办呢?这时候就需要helpers文件发挥作用了。
我们可以看到工程目录下有如下文件:
除了application_helper这个文件是默认生成的以外,只要你有一个控制器或者说一个资源,会自动生成对应的helper文件,这个helper文件里的方法就能够在对应名称的控制器文件中使用。比如我们想在user控制器中使用我们自己定义的一个接口(或者叫函数),那么我们就可以将这个接口写在对应的users_helper文件中,同时在我们控制器的基类application_controller.rb中包含一下我们的users_helper,这样你就可以在user控制器中使用这个方法了。
下面我们就用实例来讲一下在控制器中使用helpers文件的方式:
如下会话控制器和用户控制器都有这么一句,将已登陆用户的id保存进session变量的user_id参数里面,我们两个地方用到这个功能:1是登陆成功以后用到这个功能,2是注册成功以后也用到这个功能(因为前面我们实现了让用户注册完就直接是登陆状态了)
然后我们利用helpers文件(文件名中是helper没s)来实现:
添加一个方法后,结果如下:
这个函数就是将@user对象作为参数user传入log_in这个方法,将参数user(也就是@user对象)的id属性保存到全局变量session的user_id参数里,这样就完成了公共方法的定义
但是还不能使用,我们接下来还得把sessions_helper.rb文件包含进application_controller.rb这个文件里,更改后如下:
直接添加第5行代码如下(会自动提示SessionsHelper,这个就是sessions_helper文件,虽然大小写下划线不一致但实际指的是同一个文件):
然后回到注册控制器和登陆控制器文件,把session[:user_id]=@user.id替换成log_in @user
这样helpers的用法就完成了,但是我们还需要继续添加几个helpers方法,因为我们的微博应用虽然有了登陆功能,但是我们直接在浏览器输入路由比如微博show页面的路由就可以直接访问(直接跳过登陆和注册也可以访问),所以需要完善。
我们需要添加一个功能,就是进入一个页面的时候判断你有没有登陆,如果你没有登陆就强制将页面转到登陆页。
第一步我们要添加的就是返回当前用户的这么一个方法,因为我们当前返回用户是怎么做的呢?就是查找session的user_id,但这样不好我们需要利用helpers文件来完善:
如下添加第6到9行
还有查找用户可以使用find也可以使用find_by,参数都是id,但是区别在于查找失败find会抛出异常,而find_by返回nil,而我们这里需要的就是返回nil而不是抛出异常直接是错误页面了。
我们还可以再次修改一下,因为如果我们工程中经常调用这个current_user函数,我们调用一次就要查询一次数据库啊,很消耗资源而且速度慢。所以我们在查询之前可以先判断一下@current_user这个变量存在不存在,如果存在就不用再去查询数据库直接返回变量结果就行了,更改完如下:
||这里跟判断语句的作用是一样的。
完成这个工作以后我们就要添加一个共有函数判断用户有没有登陆,在里面要判断current_user这个公共函数的返回值,如果用户已经登陆返回true否则返回false,添加的代码如下第11到15行
还有就是添加了共有函数log_in,我们肯定还要添加一个登出方法,我们添加log_out方法代表登出(第17到19行),登出代码第18行我们之前就讲过了只要将session的会话保持的user_id删除就行,还有千万千万别忘了把@current_user变量置空,否则你在log_out里面登出了但是@current_user变量还有内容所以current_user方法还是会返回上一个用户,这肯定是不行的:
完整代码如下,注意我们在application_controller.rb文件中添加了SessionsHelper了(所以再添加新的公共函数就可以直接在session控制器中使用了不需要再去里面包含):
到此,我们就可以在工程中使用logged_in?这个方法,
那么我们回到sessions控制器中,先来修改destroy代码(这个代码是用来退出登录的用户):
修改的时候我们首先要判断该用户是不是登陆状态,如果是我们才进行退出操作,改完如下:
那么其他还要改哪里呢?我们应该去那些有对应视图的action中去判断有没有登陆,有才可以访问否则强制返回登陆页面。
其实也很简单,比如我们看一下user控制器的show这个动作(就是显示用户的个人页面):
我们往里面添加判断语句判断有没有登陆,有才可以访问否则强制返回根页面,更改后如下:
然后我们再去更改下路由的根地址:
原来的根地址如下是posts的index页面
改成登陆页面:
那么登陆页面路由是啥呢?跟会话保持的session有关,不是浏览器输入的路由哦,我们之前在routes.rb文件中就写了,如下找到
所以我们就知道根页面路由要改成如下:
然后我们回到浏览器,刷新一下下面的页面:
结果就强制回到根页面(因为我们把根页面改成登陆页了):
然后我们浏览器输入http://localhost:3000/users/4结果仍然回到登陆页面:
因为其他的还没添加,所以我们输入其他页面路由还是可以访问
如下报错不是不能访问,而是不存在该记录
还可以访问及其他页面:
这样我们就实现了强制你不能访问某些页面,除非你先登陆,但是如果不登陆很多页面都不能访问,那么我们到每个页面对应的action都要写判断语句就很麻烦。那么有没有这样一个方法,让我们只在一个控制器中(一个控制器里有多个对应视图的action)判断一次就可以了,指定哪些页面需要跳转回登陆页面(即哪些页面需要登陆才可以访问)这个我们先不讲,我们现在只要知道在每个需要如此处理的地方添加如下的判断就能实现:
if logged_in?
else
redirect_to root_url #跳转到根页面end
?
记住根页面路由是http://localhost:3000/,而我们把根页面设置为登陆页面,所以http://localhost:3000/login路由跟前面那个路由都是一样的。
现在还有个问题,我们输入后点击登陆
发现点击完还是跳转回该页面:
这个问题先留着
?