Rails:在控制器中处理之前验证参数?

时间:2014-04-20 10:19:16

标签: ruby-on-rails validation ruby-on-rails-4 actioncontroller

我正在创建一个rails应用程序,用户可以在输入字段中粘贴soundcloud链接。然后,此链接将发送到我post_controller中的创建操作,并用于获取该歌曲的JSON文件。

# app/controllers/post_controller.rb

def create
  require 'open-uri'

  raw_link = params[:post][:link]

  raw_link.downcase.include? "soundcloud.com/"
  tmp_media_json = JSON.load(open("http://soundcloud.com/oembed?format=json&url=#{raw_link}"))
  if tmp_media_json['thumbnail_url']["placeholder"]
    tmp_media_json['thumbnail_url'] = JSON.load(open("http://soundcloud.com/oembed?format=json&url=#{tmp_media_json['author_url']}"))['thumbnail_url']
  end

  media_thumbnail = tmp_media_json['thumbnail_url'].gsub('t500x500.jpg', 't80x80.jpg')
  media_title = tmp_media_json['title']
  media_iframe = tmp_media_json['html']
  media_type = params[:post][:media_type]

  @post = Post.new(link: media_iframe, title: media_title, thumbnail: media_thumbnail, media_type: media_type)

  respond_to do |format|
    if @post.save
      format.js { render :file => "/pages/create_new_post.js.erb" }
      format.html { redirect_to @post, notice: 'Post was successfully created.' }
      format.json { render action: 'show', status: :created, location: @post }
    else
      format.html { render action: 'new' }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

Post模型中,我正在尝试运行validates :link, presence: true,但问题是它似乎是在创建操作中的所有代码之后完成的。我希望在create action中的所有代码之前完成验证。 (因为如果没有有效的链接,创建操作中的代码将无效)。

我该怎么做或有更好的做法?

3 个答案:

答案 0 :(得分:0)

class YourController < ApplicationController
    before_filter :my_filter
    before_filter :my_filter2, :except => [:index, :new, :create]
    def my_filter
     # your code gose here
    end
    def my_filter2
     # your code gose here
    end
........
end

答案 1 :(得分:0)

您需要将json抓取代码移出控制器并移入模型。思考单一责任原则(SRP):http://en.wikipedia.org/wiki/Single_responsibility_principle

此外,这个问题有点令人困惑,因为你正在验证“链接”属性,这是由于来自soundcloud的JSON加载的结果,所以这种方式使你的问题无法实现。

话虽如此,保持控制器精益

# controller...
def create
  @post = Post.new_from_link(params[:post][:link], params[:post][:media_type])
  @post.save
  ...render...

end

# post model...
class Post < ActiveRecord::Base
  attr_accessor :raw_link, :raw_media_type

  def new_from_link(raw_link, raw_media_type)
    @raw_link = raw_link
    @raw_media_type = raw_media_type
  end

  def assign_soundcloud_attributes
    fetcher = SoundCloudFetcher.new(raw_link, raw_media_type).fetch
    self.link = fetcher.link
    self.media_type = fetcher.media_type
    self.media_thumbnail = fetcher.media_thumbnail
    self.media_iframe = fetcher.media_iframe
  end
end

class SoundCloudFetcher
  attr_accessor :link, :media_type, :media_thumbnail, :media_title, :media_iframe

  def self.initialize(raw_link, raw_media_type)
    @raw_link = raw_link
    @raw_media_type = raw_media_type
  end

  def fetch
    ...go get and set the data...
    return self
  end
end 

所以上面的代码不完整。它错过了对#assign_soundcloud_attributes的实际调用。此设计可让您在要拨打电话的位置移动。您可以将呼叫置于#new_from_link中,也可以将其置于before_validation或before_create回调中,具体取决于您的需要。

要清除这一点的问题是您打算验证传入的raw_link,还是要验证从soundcloud api调用返回的链接。

如果要验证原始链接,请将对#assign_soundcloud_attributes的调用移至before_create回调。

如果要验证从SoundCloud api调用中检索的实际链接属性,请将其放在#new_from_link或#before_validation回调中。

答案 2 :(得分:0)

你可以使用带有 rails_parms gem的before_filter。

请参阅https://github.com/nicolasblanco/rails_param

它的工作原理如下:

param! :q,           String, required: true
param! :categories,  Array
param! :sort,        String, default: "title"
param! :order,       String, in: %w(asc desc), transform: :downcase, default: "asc"
param! :price,       String, format: /[<\=>]\s*\$\d+/