Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

`Respond_to` โดยไม่ต้องเจ็บปวด

เมื่อคุณสร้างนั่งร้านใน Rails คุณจะเห็น respond_to ปกติ บล็อก:

app/controllers/tasks_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

แต่การกระทำบางอย่างของคุณ เช่น index ไม่ได้มีพวกเขา!

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

นี้ไม่ดี. ทำไม หากคุณกด /tasks.txt และ txt แอปของคุณไม่รองรับ คุณจะได้รับข้อผิดพลาดที่ไม่ถูกต้อง:

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

นี่ไม่ถูกต้องนัก คุณควรบอกลูกค้าว่าพวกเขากำลังขอรูปแบบที่คุณไม่สนับสนุน ไม่ใช่ว่าคุณไม่พบไฟล์ที่ถูกต้อง

หากเป็น UnknownFormat ผิดพลาด คุณสามารถส่งคืนรหัสตอบกลับที่ดีกว่าได้ แต่ข้อผิดพลาดเหล่านี้จะปะปนกับข้อผิดพลาดอื่นๆ ที่ไม่เกี่ยวข้อง และจะจัดการได้ยากจริงๆ

คุณสามารถเพิ่ม respond_to บล็อก index . ของคุณ การกระทำ:

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

จากนั้น คุณจะได้รับข้อยกเว้นและรหัสข้อผิดพลาดที่คุณคาดหวัง:

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'

ดีขึ้นมาก แต่ทิ้งผู้ควบคุมทั้งหมดของคุณด้วย respond_to บ้าไปแล้ว มันให้ความรู้สึก un-Rails-ish มันละเมิด DRY และทำให้คุณเสียสมาธิจากงานที่ตัวควบคุมของคุณกำลังทำอยู่

คุณยังต้องการจัดการรูปแบบที่ไม่ดีอย่างถูกต้อง แล้วคุณจะทำอย่างไร?

A respond_to ทางลัด

หากคุณไม่ได้ทำอะไรเป็นพิเศษเพื่อแสดงวัตถุ คุณสามารถใช้ทางลัดได้ หากคุณเขียน:

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

มันทำงานในลักษณะเดียวกับการเขียน respond_to . แบบเต็ม บล็อกใน index . เป็นวิธีสั้นๆ ในการบอก Rails เกี่ยวกับรูปแบบทั้งหมดที่การกระทำของคุณรู้ และหากการกระทำต่างกันรองรับรูปแบบที่ต่างกัน นี่เป็นวิธีที่ดีในการจัดการความแตกต่างเหล่านั้นโดยไม่ต้องใช้โค้ดมากนัก

จัดการรูปแบบที่ระดับคอนโทรลเลอร์

โดยปกติแล้ว แต่ละการกระทำในคอนโทรลเลอร์ของคุณจะทำงานกับ เหมือนกัน รูปแบบ ถ้า index ตอบกลับ json ดังนั้นจะ new และ create และทุกสิ่งทุกอย่าง ดังนั้น คงจะดีถ้าคุณมี respond_to ที่จะส่งผลต่อตัวควบคุมทั้งหมด:

app/controllers/tasks_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

และสิ่งนี้ได้ผลจริง:

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'

ชนิดของข้อผิดพลาดที่เราหวังว่าจะได้รับ! และคุณไม่ต้องวุ่นวายกับแต่ละการกระทำเพื่อทำสิ่งนั้น

บางครั้ง คุณต้องการทำสิ่งต่าง ๆ ขึ้นอยู่กับสถานะของโมเดล ตัวอย่างเช่น สำหรับ create คุณจะเปลี่ยนเส้นทางหรือแสดงแบบฟอร์มอีกครั้ง ขึ้นอยู่กับว่าโมเดลนั้นถูกต้องหรือไม่

รางสามารถจัดการสิ่งนี้ได้ แต่คุณยังคงต้องบอกว่าต้องการให้ตรวจสอบวัตถุใดด้วย respond_with . ดังนั้นแทนที่จะเป็น:

app/controllers/tasks_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

คุณสามารถเขียน:

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

ด้วยวิธีนี้ คุณจะแยกโค้ดของคุณออกจากรูปแบบที่คุณตอบกลับ คุณสามารถบอก Rails ได้ ครั้งเดียว รูปแบบใดที่คุณต้องการจัดการ คุณไม่จำเป็นต้องทำซ้ำในทุกการกระทำ

อัญมณีตอบกลับ

ใน Rails 4.2 มีสิ่งที่จับต้องได้:respond_with ไม่รวมอีกต่อไป แต่คุณสามารถกู้คืนได้หากคุณติดตั้ง responders พลอย และ responders gem นำคุณสมบัติที่ดีอื่น ๆ มาด้วย

คุณสามารถตั้งค่าข้อความแฟลชใน respond_with โดยใส่ responders :flash ที่ด้านบนของคอนโทรลเลอร์ของคุณ:

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

สะดวกสบาย คุณสามารถตั้งค่าเริ่มต้นสำหรับข้อความแฟลชเหล่านี้ในไฟล์ภาษาของคุณ

นอกจากนี้ หากคุณมี responders อัญมณีใน Gemfile . ของคุณ และคุณสร้างโครงนั่งร้าน Rails ตัวสร้างจะสร้างตัวควบคุมโดยใช้ respond_with แทน respond_to :

app/controllers/tasks_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

  # ...

นี้สะอาดกว่านั่งร้าน Rails มาก

และสุดท้าย หากคุณต้องการตอบสนองด้วยรูปแบบเฉพาะสำหรับการดำเนินการกับตัวควบคุม คุณสามารถเรียก respond_to หลายครั้ง:

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

ขอบคุณ Jeroen Weeink ในความคิดเห็นสำหรับเคล็ดลับสุดท้ายนั้น!

respond_with หรือ respond_to ?

หากคุณต้องการส่งคืนข้อมูลที่แตกต่างกันสำหรับรูปแบบต่างๆ คุณมีทางเลือกสองสามทาง respond_toระดับคอนโทรลเลอร์ รวมกับ respond_with เป็นวิธีที่ยอดเยี่ยมในการรับตัวควบคุมแบบสั้น แต่มีแนวโน้มว่าจะช่วยได้มากที่สุดเมื่อการกระทำของผู้ควบคุมทั้งหมดตอบสนองต่อรูปแบบเดียวกัน และดำเนินการในลักษณะที่ Rails คาดหวัง

แม้ว่าในบางครั้ง คุณต้องการให้มีการกระทำบางอย่างที่แตกต่างออกไป เส้นเดียว respond_to เหมาะสำหรับรับมือสถานการณ์นั้น

หากคุณต้องการการควบคุมเพิ่มเติม ให้ใช้ respond_to . แบบเต็ม ด้วยบล็อก และคุณสามารถจัดการแต่ละรูปแบบได้ตามที่คุณต้องการ

ในกรณีเหล่านี้ คำขอรูปแบบที่คุณไม่สนับสนุนจะได้รับข้อผิดพลาดที่ถูกต้อง และทั้งแอปและไคลเอ็นต์ของแอปจะยิ่งสับสนน้อยลงมาก