Rails高级开发(MVCR)
Posted 柚长成记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rails高级开发(MVCR)相关的知识,希望对你有一定的参考价值。
Rails介绍
Ruby on Rails 是什么
- web开发框架
- 基于ruby
- 2004/07DHH开发的
Rails设计哲学
- 遵循web标准以及HTTP协议
- COC约定大于配置
- MVC
- DRY不要重复造轮子
- 软件工程设计模式
- Assets静态资源管理
- Restful路由与API
- Testing测试
- Deployment部署
- Automation自动化
敏捷开发快速迭代:创意或想法->线上-〉修改-〉线上-〉下次迭代
从本地开发到线上:
- 环境:Development/Test/Production/Staging
- Test
- Configurations
- Deployment
Dynamic Pages:
- Routes
- HTTP GET/POST/DELETE/PUT
- custom routes:例如默认参数、命名空间、请求方式、正则匹配
- easy to refactor
- Restful
- Cache:Page Cache & Fragment Cache
- Response:html JSON XML etc.
- Inheritance
- Controller层:filter
- Model层:ORM & trigger
Assets:
- 工具:Asset Pipeline、Minify、Combine、Webpack、babel
- Js/CSSminify and combine
- Assets URL
- Etag
SASS Coffeescript
测试
单元测试:rspec/minitest
- 集成测试:cucumber/selenium
脚本和自动化
- rails c/s/g
- rake task:rake db:create/migrate、rake routes以及自定义rake任务
libraries
- gem
- bundle
- rails是很多gem的集合
- 可以创建自己的gem
MVC设计模式
Model
ORM
对象关系模型,Active Record实现数据记录与对象的映射,数据表与模型的映射
Rails对数据库的支持
ActiveRecord对关系型数据库的支持
- SQLite:Rails默认数据库
- mysql
- PostgreSQL
- Oracle
- SQLServer
对非关系型数据库的支持
- MongoDB(需要gem Mongoid/MongoMapper)
- Redis
- HBase
引入MySQL数据库
本地电脑安装MySQL数据库
gem ‘mysql2’
database.yml:
default: &default adapter: mysql2 encoding: utf8 pool: 5 host: 127.0.0.1 username: root (root账号才能执行 rake db:create) password: development: <<: *default database: circles_development test: <<: *default database: circles_test production: <<: *default host: 127.0.0.1 database: circles_production username: circles password: circles_p_roduction
Model基础
- 命名约定是驼峰式单数,数据库表名是蛇底式复数
- 新建一个model,默认继承于ActiveRecord::Base
- save(create or update)失败的原因:验证失败,或者数据库连接失败
Model类方法
- self.table_name = “”
- validates(from gem activemodel)
- scope
- associations
- before_save
Scopes
自定义查询范围
scope :evils, -> { where(style: 'evil') }
Validation
- 验证默认触发事件:save create update,可以用on参数指定触发事件
验证失败会把错误信息放进对象的errors中,如下方式可以在view中显示:
user.errors.messages.values.flatten.each
save之前看是否可以通过验证的方法:
user.valid?
- 自定义validate,验证不通过使用
errors.add(:name, 'info')
- 跳过验证:save(validate: false) update_attribute/column(s)
关联
- 描述表间关系
分类:
- 自关联
- 一对多
- 一对一
- 多对多:需要手动建立关联关系表
- has_and_belongs_to_many,无需访问中间表
- has_many :through,需要访问中间表及其中自定义字段
blog belongs_to :user ->blogs中需要有user_id外键
- 参数:
- -> { where }
- class_name
- foreign_key
- primary_key
- dependent: :destroy
Callback
与对象的生命周期增删改查的事件绑定的方法
会触发callback的方法与不会触发callback的方法
- 所有的回调被自动放在事务中,before回调返回false就会rollback,after回调需要抛出异常才会rollback
CRUD深入
查询
find vs find_by vs find_by!
- find参数是i,找不到会异常
- find_by参数是属性键值对,找不到返回nil
- find_by!找不到会异常
find_by_sql 返回是数组,使用场景:复杂的需要定制的查询。需要注意sql注入风险。
- where:n+1查询:
- 表现:Blog.where().comments
- 解决办法:Blog.where().includes(:comments).comments
更新
- new_record?是否new但是没保存到数据库
- changed? 是否有更改但是没保存到数据库
- changed_attributes 更改但是没保存到数据库的属性
- attributes = hash 之后需要save
- update_attributes 更新多个属性
- update_attribute 更新一个属性
Model自定义属性
- 无需表中有对应字段,或者可以覆盖表中已有字段
- 定义new_attr_name = 的方法,该方法表单中可以用来输入,c中白名单添加新属性
- 定义new_attr_name方法,编辑可以读取到已有的值
Transaction
- 事务是基于db的,而不是针对一个表
- 事务的作用是,一对操作一起成功或者一起失败
Blog/blog.transaction do
blog.save!
raise 'error'
# 有异常就会rollback,异常会继续往外层抛出end
Migrations
- from gem activerecord
作用:
- 使用Ruby DSL管理数据库模式
- 通过RDB(关系型数据库)模式管理,在不同db之间使用
- 支持版本管理和团队协作
- 支持db rollback
- 不仅是操作数据库模式,还可以是对数据的修改
rake -T 查看支持的rake db命令
- 永远不要修改已经提交的migrations,要不断新增并修改
- schema.rb映射数据库模式
View
View基础
- gem:actionview(view层功能)& actionpack(controller层功能)
- 查找:app/views 以及自定义查找view目录append_view_path
- 分类:
- template:index.html.erb
- partial:_form.html.erb(使用的时候没有下划线)
- layout:application.html.erb
- 解析:完成ruby语法到字符串的解析
- erb
- <% %> 执行
- <%= %>显示
- <%# %> 注释
- <% -%> 结尾无换行
- haml
- builder:json builder,xml builder etc
- erb
- 命名:
- index:action或partial的名字
- html:输出文件类型(mime type),还可以是xml
- erb:解释引擎
引入Bootstrap
- gemfile:
gem 'bootstrap-sass', '~>3.3.5.1'
- app/assets/stylesheets/application.scss:
- @import “bootstrap-sprockets”;
- @import “bootstrap”;
render & layout
- render:
- in controller:
- 修改action的查找view的行为,构建http response
- 可以返回各种格式(text json html xml file partial)
- 支持的参数:
- :content_type
- :layout(false)
- :location
- :status(code & symbol)
- in view:渲染partial
<%= render (partial:)"shared/navbar", locals: { var: @var } %>
- in controller:
- layout:
- 放公用的组件,例如:head、navbar、footer、flash以及自定义布局contend_for
- controller中,
layout 'application'
显示指定使用的布局 - 运行时选择逻辑,
layout :generate_layout
定义方法最终返回布局字符串
helper
- image_tag/image_path
- javascript_include_tag
- stylesheet_link_tag
- distance_of_time_in_words
- numberto (转换到不同领域的数字) numberwith(设置数字显示格式)
- strip_links (过滤用户输入内容防止被攻击,去掉链接) strip_tags(去掉标签)
- <% benchmark ‘name’ do %> … <% end %> 可以log代码运行速度,VC中可用
- <% cache do %> …<% end %>可以片段缓存,VC中可用
- <% var = capture do %>…html fragment…<% end %>,之后再<%= var%>
- <%= string.html_safe %> 才可以安全输出html标签,否则是纯字符串
form_tag
- 生成一个HTMLform,用于非模型相关的操作,比如:搜索、登陆
不同类型的input,都是以tag结尾:
- text_field_tag
- password_field_tag
- email_field_tag
- file_field_tag
- submit_tag
- etc
浏览器只支持GET/POST的请求方式,Rails的PATCH/PUT/DELETE请求实际上是通过POST请求方式然后在表单内部添加一个隐藏的输入,添加_method参数来区分,参数的值是put/patch/delete
- 方法参数:url(表单提交的地址),method: :post
- helper自动生成的两个隐藏的input:utf-8 & authenticity_token
- input标签最重要的是name属性,与参数传递的key有关,id属性是根据name属性生成的
form_for
- 生成一个HTMLform,用于模型相关的操作,比如:商品创建
不同类型的input,调用方式f.helper,第一个参数必须为实例拥有的属性:
- text_field
- password_field
- email_field
- file_field
- submit
- fields_for(为其他模型的属性输入)
- etc
和资源实例绑定,方法参数为实例变量@+url+hash
- 支持模型关联,一对一、一对多
- 自动表单数据设置,提交失败会保留错误内容,机制:create创建失败render new而非redirect_to new,使用的是create的@user,而不是new中的@new
- 支持表单参数传递命名空间,params.require(:user).permit(:name),有不允许的参数输入会异常
CSRF攻击与防治
原因:允许用户自定义html tag(img的src属性以及a的onclick属性),而且后端没有过滤直接输出
Rails的应对措施:
使用正确的HTTP请求方式
application controller中,protect_form_forgery
表单的authenticity_token参数,只有后端知道token,匹配一致才是合法请求
对于ajax请求,csrf_meta_tags提供authenticity_token。
Rails防范不了js取得token再发起请求的攻击,需要网站配合:
后端需要在存入输入时需要过滤以及替换非法标签以及字符串
输出时,只允许白名单中的标签输出,其他都不输出,escape掉
Rails Ajax
Jquery Ajax
Ajax in Rails
- gem ‘jquery_rails’ 把jquery整合进helper
- application.js:require jquery以及jquery_ujs
- 使用:form_for也是添加remote: true参数
- action需要render json: { }
Ajax不支持文件上传
- ajax本质时js,js不支持文件操作
- 异步上传文件:实质是同步,需要采用iframe的形式,设置表单target为frame
- 或者采用js第三方组件,例如AjaxUpload,原理同上
Controller
Controller基础
- 来源于gem actionpack
- 命名约定是复数,users_controller.rb,对应文件中UsersController
- 新建一个controller,默认继承于ApplicationController,又继承于ActionController::Base
- action中通过实例变量@向对应的view传递数据
- action中通过params获取view中的表单向controller传递的参数
- 一个action内部只能有一个redirect_to或者render
- action内部使用return关键字跳出action执行
- 默认的以及自定义的action都需要以redirect_to或者render结束,否则会提示找不到模版的异常
- app/controllers/concerns可以放一些通用的逻辑,封装成模块
redirect_to
- redirect_to:
- 重新发起一次请求
- redirect_to url/:back
- 参数::status
- vs render:
- 相同点:都是完成http响应
- 不同点:render是构建http响应,redirect_to是重新发起请求直到拿到响应
session vs cookies
- 背景:HTTP 短连接(请求响应之后连接就会被切断)、无状态
- HTTP用cookies保存状态:
- 服务器到浏览器的响应:set-cookie
- 浏览器到服务器的请求:cookie
- session是后端实现,cookie是浏览器实现
- session的key存储基于cookie,也可以基于后端的数据库存储
- session支持存储后端的数据结构,例如对象;cookie只支持字符串
- 两者的读取以及写入形式都如同哈希
- session没有大小限制;cookie有大小限制
- cookie设置有效期(默认是会话结束)以及域名(作用域)信息
- config/initializers/session_store.rb中设置session存储方式以及存储于cookie指定的key
controller中其他方法
- send_file、send_data返回响应结果
- request
- request.fullpath
- request.get?
- request.ip
- request.headers
- request.body
response
- response.location
- response.response_code
- response.body=
Rails.logger.info “debug_info” 手动添加日志调试信息
controller中的类方法
- before/around/after_filter:在action之前/前后/之后运行的方法
- protect_from_forgery
- helper_method:定义c对应的v中可以用的helper,例如:current_user
- rescue_from 异常类, with: :method,方法定义抓取异常后的处理
current_user设计
module Concerns
module UserSession
def self.included base
base.class_eval do
helper_method :logged_in?
helper_method :current_user
end
end
def signin_user user
session[:user_id] = user.id end
def logout_user
session[:user_id] = nil
end
def logged_in?
!!session[:user_id] end
def current_user if logged_in? @current_user ||= User.find(session[:user_id]) else
nil
end
end
endend
Route
Route基础
- 规定了特定格式的URL请求到后端controller的action的分发规则
- config/routes.rb
- 使用指定方法和参数来生成路由
- 路由查找是自上而下的,前面的会覆盖后面的路由
- 可以对路由的片段做限制(正则表达式)
- 可以重定向 to: redirect(url)
路由设计
普通路由
get 'users/:id', to: 'users#show'#same asget 'users/:id' => 'users#show'#action params[:id]match 'photos', to: 'photos#show', via: [:get, :post]
命名路由
#定义 生成helperget 'users/:id', to: 'users#show', as: 'user'#使用user_path/url(@user)
user_path/url(@user.id)
复数资源:同一个URL可以拥有不同的请求方式(GET/POST/PUT/DELETE)
rake routes 查看helper方法、http verb、url pattern以及controller#action
单数资源
命名空间
代码的命名空间和url的前缀两者都有
scope
代码的命名空间和url的前缀两者只选择其中之一
嵌套路由
嵌套路由最多一共两层
部分的action
resources :users, only/except: [:index, :destroy]
添加自定义的路由
- member:需要传递ID
- collection:不需要传递ID
根路由
以上是关于Rails高级开发(MVCR)的主要内容,如果未能解决你的问题,请参考以下文章
为 memcached 和 Rails 组合片段和对象缓存的最佳方式