Ruby on Rails :: RESTful on Rails(3) >> respond_to

Posted by PunNeng, Sat Feb 03 23:40:00 UTC 2007

มาต่อกัน ยังไม่จบ

คราวนี้เข้ามาข้างใน controller กันบ้าง ฝั่ง logic ข้ามไป มาดูตรง respond_to กันดีกว่า

  1
  2
  3
  4
  5
  6
respond_to do |format|
      format.html # index.rhtml
      format.js     # index.rjs ผมเพิ่มเอง
      format.xml  { render :xml => @products.to_xml }
      format.rss   { render :action => "rss.rxml" }  # อันนี้ผมก็เพิ่มเอง
    end

คงจะเห็น code อันนี้ใน index หรือใน create จะเห็น

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
respond_to do |format|
      if @product.save
        flash[:notice] = 'Product was successfully created.'
        format.html { redirect_to product_url(@product) }
        format.xml  { head :created, :location => product_url(@product) }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @product.errors.to_xml }
      end
    end

มันไว้ทำอะไร ??? มันจัดการกับ mime type นั่นแล ซึ่งขึ้นอยู่กับ request ที่ส่งมา ว่าต้องการ respond เป็นอะไรกลับไป ถ้าต้องการ html ก็ใช้วิธีปกติที่เคยทำกัน แต่ถ้าเกิดต้องการในรูปของ xml ก็เพียง request มาว่าต้องการ xml ซึ่ง Rails จะไปจัดการกับ HTTP Accept header จากที่ request มาเอง

ทำไมต้อง xml ?? ก็เพราะว่า Web Services หนะสิ แค่ request มา แล้วมันก็จะ return เป็น xml ไปให้ แค่นี้ไม่ว่าภาษาใดๆ ก็จะเข้าใจได้้้ด้วย xml แล้ว

มาดูในตัวอย่าง ถ้าเกิดส่ง url มาเป็น /products แน่นอน มันย่อม render ออกไปเป็น index.rhtml แต่ถ้าเป็น /products.xml มันก็จะ render เป็น xml ให้ โดยใช้ attribute เป็นตัวกำหนด node (อันนี้ลองเล่นกันเองนะครับ)

ถ้าเป็น ajax เรียกมา มันก็จะไป render ตัว index.rjs(อันนี้ก็สนุก สำหรับ ajax แปะไว้ๆ) หรือถ้าเป็น /products.rss มันก็จะไป render ตัว index.rxml ซึ่งทั้ง .js และ .rxml ก็มีรูปแบบของมันอยู่ ไว้จะย้อนมาเล่าให้ฟังทีหลัง สำหรับ rss หรือ atom จริงๆ แล้วมี plugin ช่วย ใช้ plugin จะผ่อนแรงกว่าเยอะ

ส่วนอันที่เป็น create ถ้าใช้ HTML ก็ปกติดี แต่ถ้าเกิดต้องการเป็น Web Services ให้สร้าง header ให้มีหน้าตาแบบนี้(ประยุกต์และอ้างอิงจาก DHH)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
POST /products/create HTTP/1.1
Host: example.org
User-Agent: Thingio/1.0
Accept: application/xml
Content-Type: application/xml
Content-Length: nnn

<product>
  <name>First product!!</name>
  <description>This is a book</description>
</product>

เนื่องจาก Content-Type เป็น application/xml ตัว rails เลยทำการแปลงให้อยู่ในรูปของ "product[name]=First%20product!!&product[description]=This%20is%20a%20book" ซึ่งเป็น form ปกติที่ใช้อยู่ ซึ่งตัว Content-Type จะเป็นตัวบอกว่าให้ xml มานะ ส่วนตัว Accept จะเป็นตัวบอกว่า ของที่คุณต้องเรียกใช้ ก็เป็น xml เหมือนกันนะ เพื่อให้มันรับรูปอย่างชัดเจนในการ respond แล้วมันก็จะวิ่งไปที่ format.xml เอง ถ้าจะใช้ WS กับ rails ชุดนี้ อย่าลืมระบุ Accept มาด้วยนะครับ

หลังจากที่ทำการสร้างให้เสร็จแล้ว มันก็จะส่ง header กลับไป หน้าตาที่มันถูกสร้าง จะเป็นแบบนี้

HTTP/1.1 201 Created
Content-Length: 0  
Location: http://example.org/products/show/1

ในกรณีที่เราต้องการที่จะระบุ mime type เพิ่มเติม สามารถไปเพิ่มได้ที่ config/environment.rb

Mime::Type.register "image/jpg", :jpg

เพิ่งนึงถึง DRY ได้ ว่ามันเข้า concept ของ DRY พอดี เราไม่ต้องทำซ้ำ ไม่ว่าจะต้องการ respond แบบไหนเราก็ทำเพียงครั้งเดียว ง่ายต่อการ maintenance

นี่แล ความเมามันส์ของ RESTful

ปล. rails มันส์กว่า ruby เพียวๆ แฮะ แก้ไขครั้งที่ 1 : เพิ่งนึกได้ว่า ถ้าหากเป็น method ที่ไม่ได้เกี่ยวกับ CRUD ถ้าจะ render ตามที่สั่งใน format ไว้ ให้เพิ่ม ?format=your_format ไปด้วย เช่น ?format=xml

แก้ไขล่าสุด วันที่ 28 กรกฏาคม 2550 เวลา 1.34 น.

Filed Under: Ruby on Rails | Tags: rest restful ruby on rails simply_restful

Comments

  1. Sutara 12.15.07 / 23PM

    ขอบคุณมากนะคะ กำลังงงเลยว่าทำไมต้องทำหลาย format ที่แท้ก็อย่างนี้นี่เอง

Have your say

A name is required. You may use HTML in your comments.




codegent: we're hiring