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:
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!
# 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.txt
và txt
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:
# 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:
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:
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ì:
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:
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ý. Và 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:
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
:
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.