Computer >> Máy Tính >  >> Lập trình >> Ruby

`Respond_to` mà không có tất cả đau đớn

Khi bạn tạo một đoạn đầu trong Rails, bạn sẽ thấy respond_to thông thường khối:

app / controllers / task_controller.rb
  def destroy
    @task.destroy
    respond_to do |format|
      format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

Nhưng một số hành động của bạn, chẳng hạn như index , không có chúng!

app / controllers / task_controller.rb
  # GET /tasks
  # GET /tasks.json
  def index
    @tasks = Task.all
  end

Điều này tệ đây. Tại sao? Nếu bạn nhấn /tasks.txttxt không được ứng dụng của bạn hỗ trợ, bạn sẽ gặp phải lỗi sai:

ActionView::MissingTemplate (Missing template tasks/index, application/index with {:locale=>[:en], :formats=>[:text], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}

Điều này không đúng lắm. Bạn nên nói với khách hàng rằng họ đang yêu cầu định dạng mà bạn không hỗ trợ, không phải là bạn không thể tìm thấy tệp phù hợp.

Nếu đây là UnknownFormat lỗi, bạn có thể trả lại mã phản hồi tốt hơn. Thay vào đó, những lỗi này sẽ bị trộn lẫn với những lỗi khác, không liên quan và sẽ thực sự khó để xử lý chúng.

Bạn có thể thêm respond_to chặn index của bạn hành động:

app / controllers / task_controller.rb
  # GET /tasks
  # GET /tasks.json
  def index
    @tasks = Task.all
    respond_to do |format|
      format.html
      format.json
    end
  end

Sau đó, bạn sẽ nhận được ngoại lệ và mã lỗi mà bạn mong đợi:

Started GET "/tasks.txt" for 127.0.0.1 at 2014-11-03 22:05:12 -0800
Processing by TasksController#index as TEXT
Completed 406 Not Acceptable in 21ms

ActionController::UnknownFormat (ActionController::UnknownFormat):
  app/controllers/tasks_controller.rb:8:in `index'

Tốt hơn nhiều. Nhưng xả rác tất cả các bộ điều khiển của bạn bằng respond_to thật điên rồ. Nó cảm thấy không-Rails-ish. Nó vi phạm DRY. Và nó làm bạn mất tập trung khỏi công việc mà bộ điều khiển của bạn đang thực sự làm.

Bạn vẫn muốn xử lý các định dạng xấu một cách chính xác. Vậy bạn sẽ làm gì?

A respond_to phím tắt

Nếu bạn không làm bất cứ điều gì đặc biệt để hiển thị các đối tượng của mình, bạn có thể sử dụng một phím tắt. Nếu bạn viết:

app / controllers / task_controller.rb
def index
  @tasks = Task.all
  respond_to :html, :json
end

nó hoạt động giống như cách viết respond_to đầy đủ khối trong index . Đó là một cách ngắn gọn để nói với Rails về tất cả các định dạng mà hành động của bạn biết. Và nếu các hành động khác nhau hỗ trợ các định dạng khác nhau, thì đây là một cách tốt để xử lý những khác biệt đó mà không cần nhiều mã.

Xử lý các định dạng ở cấp bộ điều khiển

Tuy nhiên, thông thường, mỗi hành động trong bộ điều khiển của bạn sẽ hoạt động với cùng một các định dạng. Nếu index trả lời json , new cũng vậy và create , và mọi thứ khác. Vì vậy, thật tuyệt nếu bạn có thể có respond_to điều đó sẽ ảnh hưởng đến toàn bộ bộ điều khiển:

app / controllers / task_controller.rb
class TasksController < ApplicationController
  before_action :set_task, only: [:show, :edit, :update, :destroy]
  respond_to :html, :json

  # GET /tasks
  # GET /tasks.json
  def index
    @tasks = Task.all
    respond_with(@tasks)
  end

Và điều này thực sự hoạt động:

Started GET "/tasks.txt" for 127.0.0.1 at 2014-11-03 22:17:37 -0800
Processing by TasksController#index as TEXT
Completed 406 Not Acceptable in 7ms

ActionController::UnknownFormat (ActionController::UnknownFormat):
  app/controllers/tasks_controller.rb:8:in `index'

Chính xác là loại lỗi mà chúng tôi hy vọng sẽ mắc phải! Và bạn không cần phải bối rối với từng thao tác để thực hiện.

Đôi khi bạn muốn làm những việc khác nhau tùy thuộc vào trạng thái của mô hình. Ví dụ:đối với create , bạn sẽ chuyển hướng hoặc hiển thị lại biểu mẫu, tùy thuộc vào việc mô hình đó có hợp lệ hay không.

Rails có thể xử lý điều này. Nhưng bạn vẫn phải cho nó biết bạn muốn nó kiểm tra đối tượng nào, với respond_with . Vì vậy, thay vì:

app / controllers / task_controller.rb
  def create
    @task = Task.new(task_params)

    respond_to do |format|
      if @task.save
        format.html { redirect_to @task, notice: 'Task was successfully created.' }
        format.json { render :show, status: :created, location: @task }
      else
        format.html { render :new }
        format.json { render json: @task.errors, status: :unprocessable_entity }
      end
    end
  end

bạn có thể viết:

app / controllers / task_controller.rb
  def create
    @task = Task.new(task_params)
    flash[:notice] = "Task was successfully created." if @task.save
    respond_with(@task)
  end

Bằng cách này, bạn tách mã của mình khỏi các định dạng mà bạn phản hồi. Bạn có thể nói với Rails một lần định dạng nào bạn muốn xử lý. Bạn không phải lặp lại chúng trong mọi hành động.

Đá quý phản hồi

Trong Rails 4.2, có một điểm bắt buộc:respond_with không còn được bao gồm. Nhưng bạn có thể lấy lại nếu bạn cài đặt responders đá quý. responders gem mang lại một số tính năng thú vị khác với nó.

Bạn có thể đặt tin nhắn flash trong respond_with bằng cách bao gồm responders :flash ở đầu bộ điều khiển của bạn:

app / controllers / task_controller.rb
class TasksController < ApplicationController
  responders :flash

Thuận tiện, bạn có thể đặt mặc định cho các tin nhắn flash này trong các tệp ngôn ngữ của mình.

Ngoài ra, nếu bạn có responders đá quý trong Gemfile của bạn và bạn tạo giàn giáo Rails, trình tạo sẽ tạo bộ điều khiển bằng cách sử dụng respond_with thay vì respond_to :

app / controllers / task_controller.rb
class TasksController < ApplicationController
  before_action :set_task, only: [:show, :edit, :update, :destroy]
  respond_to :html, :json
  
  def index
    @tasks = Task.all
    respond_with(@tasks)
  end

  def show
    respond_with(@task)
  end

  # ...

Cái này gọn gàng hơn nhiều so với các giàn giáo mà Rails đi kèm.

Và cuối cùng, nếu bạn chỉ muốn phản hồi với một định dạng cho các hành động cụ thể của bộ điều khiển, bạn có thể gọi respond_to nhiều lần:

class TasksController < ApplicationController
  respond_to :html
  respond_to :js, only: :create
end

Cảm ơn Jeroen Weeink trong phần bình luận cho mẹo cuối cùng đó!

respond_with hoặc respond_to ?

Nếu bạn muốn trả lại thông tin khác nhau cho các định dạng khác nhau, bạn có một số tùy chọn. respond_to cấp bộ điều khiển kết hợp với respond_with là một cách tuyệt vời để có được bộ điều khiển ngắn. Nhưng nó có xu hướng hữu ích nhất khi tất cả các hành động của bộ điều khiển của bạn phản hồi với cùng một định dạng và hoạt động theo cách mà Rails mong đợi.

Tuy nhiên, đôi khi bạn muốn có một vài hành động có thể hành động khác đi. Một lớp lót respond_to rất tốt để xử lý tình huống đó.

Nếu bạn cần kiểm soát nhiều hơn, hãy sử dụng respond_to đầy đủ với một khối và bạn có thể xử lý từng định dạng theo cách bạn muốn.

Với bất kỳ định dạng nào trong số này, các yêu cầu định dạng bạn không hỗ trợ sẽ gặp phải lỗi. Và cả ứng dụng của bạn và khách hàng của nó sẽ ít bị nhầm lẫn hơn rất nhiều.