Posts Tagged ‘RESTFul’

สร้าง RESTFul WS ด้วย Spring MVC3

August 10th, 2010

Introduction
ไม่ recap เรื่อง REST แล้วนะครับเพราะเขียนเรื่องแนวคิดไปสองสามบทความแล้วครับเราเข้าเรื่องกัน เลยว่านอก จากการใช้ RESTEasy, Jersey แล้วเรายังสามารถใช้ SpringMVC3 เพื่อสร้าง RESTFul Service ได้เหมือนกันครับ
ก่อนอื่น sourcecode ทั้งหมดอยู่ที่นี่ครับ https://restfulspringmvc3.googlecode.com/svn/trunk/
Spring 3 REST support
ก่อนที่ Spring จะรองรับการทำ REST เราสามารถเลือกใช้ Framework อื่นๆเช่น Restlet, RestEasy, และ Jersey เพื่อนำมาสร้าง RESTFul ws ขึ้นมาโดยที่ Framework เหล่านี้จะ implement อยู่บนมาตรฐาน JAX-RS (JSR 311)
ปัจจุบัน Spring3 ได้ทำการรวมเอาความสามารถเรื่อง RESTFul WS เข้ามาไว้ในตัวเรียบร้อยแต่วิธีการสร้าง REST ของ Spring3 จะไม่เป็นไปตามมาตรฐาน JAX-RS ซึ่งทีมสร้างเองยืนยันว่า Spring3 มีความสามารถมากกว่า JAX-RS เช่นการ intergrate เข้ากับ SpringMVC แบบ seamless สิ่งที่เราต้องทำมีเพียงแค่ใส่ Annotation เข้าไปที่ Controller เช่น:

  • Annotations, @RequestMapping และ @PathVariable, ถูกเตรียมไว้สำหรับการทำ resource identification และ URI mappings
  • ContentNegotiatingViewResolver มีไว้สำหรับการสร้าง representations ที่เหมาะสมกับ MIME/content types
  • Integrate กับ Spring Web MVC ได้อย่างสวยงาม

เรามาเริ่มกันเลยดีกว่าครับอันดับแรกให้ สร้าง Spring Web Project ด้วย Eclipse ก่อน (หาทำเอาเองนะครับ) อันดับแรกเราจะสร้าง model ก่อนซึ่งในที่นี้คือ class Employee โดยมีรายละเอียดดังนี้

@XmlRootElement(name="employee")
public class Employee {
   private long id;
   private String name;
   private String email;

   public Employee() {}

   public Employee(long id, String name, String email) {
      this.id = id;
      this.name = name;
      this.email = email;
   }
   .. get/set...

ส่วนต่อไปคือการสร้าง ส่วนที่เรียกว่า Utility Class ที่ทำหน้าที่ื Wrap Employee อีกชั้นหนึ่ง

@XmlRootElement(name="employees")
public class EmployeeList {
   private int count;
   private List<Employee> employees;

   public EmployeeList() {}

   public EmployeeList(List<Employee> employees) {
      this.employees = employees;
      this.count = employees.size();
   }

   public int getCount() {
      return count;
   }

   public void setCount(int count) {
      this.count = count;
   }

   @XmlElement(name="employee")
   public List<Employee> getEmployees() {
      return employees;
   }
   public void setEmployees(List<Employee> employees) {
      this.employees = employees;
   }

เมื่อเราได้ model และ util class แล้วต่อไปเราจะสร้าง service ขึ้นมาครอบสองสิ่งนี้ไว้และเราจะผูก service นี้เข้ากับ controller เพื่อรับหน้าที่จัดการการข้อมูลของ employee ทั้งหมดที่จะมีอยู่

public class EmployeeDS {

	private static Map<Long, Employee> allEmployees;
	static {
		allEmployees = new HashMap<Long, Employee>();
		Employee e1 = new Employee(1L, "Huang Yi Ming", "huangyim@cn.ibm.com");
		Employee e2 = new Employee(2L, "Wu Dong Fei", "wudongf@cn.ibm.com");
		allEmployees.put(e1.getId(), e1);
		allEmployees.put(e2.getId(), e2);
	}

	public void add(Employee e) {
		allEmployees.put(e.getId(), e);
	}

	public Employee get(long id) {
		return allEmployees.get(id);
	}

	public List<Employee> getAll() {
		List<Employee> employees = new ArrayList<Employee>();
		for( Iterator<Employee> it = allEmployees.values().iterator(); it.hasNext(); ) {
			Employee e = it.next();
			employees.add(e);
		}
		return employees;
	}

	public void remove(long id) {
		allEmployees.remove(id);
	}

	public void update(Employee e) {
		allEmployees.put(e.getId(), e);
	}

ส่วนต่อมาคือสิ่งที่สำคัญมากๆ เราจะสร้าง Controller ขึ้นมาพื่อทำหน้าที่รองรับ request จากผั่ง client ซึ่งใน application นี้เราออกแบบให้ระบบของเรารองรับการทำงานผ่าน URI ทั้งหมด 5 แบบดังนี้

  • GET /enployees = เอาข้อมูล employee ออกมาทั้งหมด
  • GET /employee/id = เอาข้อมูล employee ที่มี่ id ตามที่ต้องการออกมา
  • PUT /employee/id = แก้ไขข้อมูล employee ที่มี id ตามที่ต้องการ
  • POST /employee = สร้าง employee ใหม่
  • DELETE /employee/id = ลบข้อมูล employee ที่มี id ตามที่ต้องการ

ดังนั้นเราจะสร้าง controller ขึ้นมาให้มีรายละเอียดการทำงานได้ปรมาณนี้ครับ ซึ่งเราสามารถทำได้ง่ายมาก


@Controller
public class EmployeeController {

	private EmployeeDS employeeDS;

	public void setEmployeeDS(EmployeeDS ds) {
		this.employeeDS = ds;
	}

	private Jaxb2Marshaller jaxb2Mashaller;

	public void setJaxb2Mashaller(Jaxb2Marshaller jaxb2Mashaller) {
		this.jaxb2Mashaller = jaxb2Mashaller;
	}

	private static final String XML_VIEW_NAME = "employees";

	@RequestMapping(method=RequestMethod.GET, value="/employee/{id}")
	public ModelAndView getEmployee(@PathVariable String id) {
		Employee e = employeeDS.get(Long.parseLong(id));
		return new ModelAndView(XML_VIEW_NAME, "object", e);
	}

	@RequestMapping(method=RequestMethod.PUT, value="/employee/{id}")
	public ModelAndView updateEmployee(@RequestBody String body) {
		Source source = new StreamSource(new StringReader(body));
		Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
		employeeDS.update(e);
		return new ModelAndView(XML_VIEW_NAME, "object", e);
	}

	@RequestMapping(method=RequestMethod.POST, value="/employee")
	public ModelAndView addEmployee(@RequestBody String body) {
		Source source = new StreamSource(new StringReader(body));
		Employee e = (Employee) jaxb2Mashaller.unmarshal(source);
		employeeDS.add(e);
		return new ModelAndView(XML_VIEW_NAME, "object", e);
	}

	@RequestMapping(method=RequestMethod.DELETE, value="/employee/{id}")
	public ModelAndView removeEmployee(@PathVariable String id) {
		employeeDS.remove(Long.parseLong(id));
		List<Employee> employees = employeeDS.getAll();
		EmployeeList list = new EmployeeList(employees);
		return new ModelAndView(XML_VIEW_NAME, "employees", list);
	}

	@RequestMapping(method=RequestMethod.GET, value="/employees")
	public ModelAndView getEmployees() {
		List<Employee> employees = employeeDS.getAll();
		EmployeeList list = new EmployeeList(employees);
		return new ModelAndView(XML_VIEW_NAME, "employees", list);
	}
}

ส่วนที่น่าสนใจสำหรับ Controller คือ @RequestMapping เราจะใช้มันเป็นตัวกำหนดหน้าที่ของแต่ละ function ให้ controller ไม่ว่าจะเป็น HTTP Method หรือ URI ที่จะใช้ร่วมกันซึ่งถึงตรงนี้เราได้องค์ประกอบพื้นฐานทั้งหมดครบแล้วไม่ว่าจะเป็น model, util, controller ต่อไปเราจะต้อง config ทั้งสามสิ่งให้ทำงานร่วมกันผ่าน configuration file ของ spring โดยผมใช้ชื่อ application-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation=...
	<bean id="employeeDS" class="dw.spring3.rest.ds.EmployeeDS" />
</beans>

ส่วนต่อไปคือการกำหนดรายละเอียดการทำงานของ controller โดยเราจะกำหนดไฟล์ rest-servlet.xml

	<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
		<property name="classesToBeBound">
			<list>
				<value>dw.spring3.rest.bean.Employee</value>
				<value>dw.spring3.rest.bean.EmployeeList</value>
			</list>
		</property>
	</bean>

	<bean id="employees" class="org.springframework.web.servlet.view.xml.MarshallingView">
		<constructor-arg ref="jaxbMarshaller" />
	</bean>

	<bean id="employeeController" class="dw.spring3.rest.controller.EmployeeController">
		<property name="employeeDS" ref="employeeDS" />
		<property name="jaxb2Mashaller" ref="jaxbMarshaller" />
	</bean>

ส่วนที่น่าสนใจมีสามส่วนคือส่วนที่เรากำหนด class สองคลาส Employee, EmployeeList ให้กับ jaxbMarshaller เนื่องจากเราจต้องการทำทั้งการ Bind และ Unbind ทั้งสองนี้ในระหว่างการทำงานตลอดเวลา ส่วนที่สองคือการกำหนด jaxbMarshaller ให้กับ employess และการผูก employeeDS และ jaxb2Mashaller ให้กับ employeeController เมื่อเราทำได้ทั้งหมดนี้แล้วต่อไปคือเราจะทดสอบกันแล้วว่ามันทำงานได้จริงไหมด้วยการเขียน main class ง่ายๆ

public class App {
    public static void main(String[] args) throws IOException {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet("http://localhost:8080/service/employee/2");
        get.addHeader("accept", "application/xml");
        HttpResponse response = client.execute(get);
        if (response.getStatusLine().getStatusCode() != 200) {
            throw new RuntimeException("Operation failed: "
                    + response.getStatusLine().getStatusCode());
        }
        System.out.println("Content-Type: "
                + response.getEntity().getContentType().getValue());
        BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        String line = reader.readLine();
        while (line != null) {
            System.out.println(line);
            line = reader.readLine();
        }
        client.getConnectionManager().shutdown();
    }
}

ที่เหลือไปเขียนเอาเองนะครับ :) เพราะ work แน่นอน ง่ายจริงๆ

Richardson Maturity Model: 3 สามขั้นสู่สุดยอด REST

April 1st, 2010

แปลมาจาก Richardson Maturity Model: 3 steps toward the glory of REST

Contents
Level 0
Level 1 – Resources
Level 2 – HTTP Verbs
Level 3 – Hypermedia Controls
The Meaning of the Levels
ถ้าเราพิจรณาเนื้อหาของหนังสือ REST In Practice แล้วพบว่าใจความสำคัญของหนังสือเล่มนี้คือการอธิบายถึงความสามารถของ RESTFul Web Services ที่เข้ามาช่วยเราในการแก้ปัญหาเรื่องของการ Integration ขององค์กรระดับ Enterprise กำลังเผชิญอยู่ โดยที่ใจความหลักของหนังสือเล่มนี้ได้อธิบายว่า “เวบ” เป็นสิ่งที่แสดงให้เห็นถึงความสามารถในการ scale ระบบที่ใหญ่มากๆและมันก็ยังทำงานได้ดี ทำให้เราสามารถดึงเอาแนวคิดสำคัญๆจาก “เวบ” เพื่อนำมาประยุกต์เข้ากับระบบของเราได้ เพื่อทำให้ระบบของเรา scale ได้ง่ายและมีปนะสิทธิภาพเหมือนกับ “Web”
» Read more: Richardson Maturity Model: 3 สามขั้นสู่สุดยอด REST

Lightweight SOAs ทางเลือกของอนาคต

January 28th, 2010

แปลและย่อมาจาก MS Architecture Journal : Lightweight SOAs: Exploring Patterns and Principles of a New Generationof SOA Solutions

Introduction
ตลอดหลายปีที่ผ่านมาหลายคนเฝ้ามองและตั้งคำถามว่าเพราะอะไรถึงทำให้วิธีการสร้าง Service Oriented (SOA) ด้วยวิธีการแบบดั้งเดิมถึงไม่สามารประสบความสำเร็จในเรื่องของการเพิ่มคุณค่าทางธุรกิจและตอบสนองความต้องการทางธุรกิจได้อย่างรวดเร็ว เหมือนกับตอนที่มันถูกสร้างขึ้น เนื่องจากโจทย์สองข้อนี้ถือเป็นหัวใจหลักในการนำเสนอเรื่องของ SOA เข้ามาในองค์กร ในบทความนี้เราจะมาพิจรณาถึงจุดต่างๆในสถาปัตยกรรมของมันที่เป็นปัจจัยให้เราไม่สามารถทำ SOA ได้สำเร็จได้ไม่ว่าจะเป็น ความซับซ้อนของ SOAP, WS-* หรือ Enterprise Service Bus เอง หลังจากนั้นเราจะนำเสนอสถาปัตยกรรมทางเลือกที่ดูยืดหยุ่นกว่า ไม่ซับซ้อนมาก อย่าง REST และ Web-Oriented Architecture

SOA: Architecting Without Constraints
SOA ถือเป็นเสาหลักของ distributed system ในช่วงหลายปีที่ผ่านมาโดยที่หัวใจหลักของ SOA คือความสามารถในการตอบสนองของฝั่ง IT ที่มีต่อการเปลี่ยนแปลงทางธุรกิจอย่างรวดเร็วในปัจจุบันด้วยการนำเสนอแนวคิดของการสร้างเซอร์วิสใหม่ๆจากการประกอบเอาเซอร์วิสอินเทอร์เฟสหลักที่ถูกเตรียมไว้แล้ว ซึ่งเซอร์วิสหลักเหล่านั้นจะถูกนำกลับมาใช้ได้บ่อยมากทำให้ระยะเวลาในการสร้างเซอร์วิสใหม่ลดลงไปอย่างมาก แต่ข้อเสียหลักๆของ traditional SOA คือมันไม่มี Constraints หรือข้อกำหนดหลักว่าการทำ SOA ต้องประกอบไปด้วยคุณลักษณะอะไรบ้างซึ่งถือว่าเป็นข้อเสียหลักของมันเลยเพราะทุกคนก็จะตีความและทำกันไปแบบไร้ทิศทาง สำหรับปัจจุบัน SOA นั้น ถ้าเราพิจรณาจากวิธีการทางสถาปัตยกรรมจะพบว่าการได้มาซึ่งวิธีการทำงานแบบในลักษณะนี้นั้น SOA แบบดั้งเดิมจำเป็นต้องมีคุณลักษณะพื้นฐานดังนี้

  • ต้องมีการยกระดับให้ SOAP และ WSDL ให้เป็นอินเทอร์เฟสหลักสำหรับการเชื่อมต่อกับเซอร์วิสต่าง
  • ใช้มาตรฐานตระกูล WS-* เข้ามารับมือกับการทำงานระดับ mission-critical
  • ติดตั้ง ESB เพื่อใช้สำหรับซ่อนการทำงานที่ซับซ้อนของการเชื่อมต่อเซอร์วิสและการประสานงานข้ามแพลทฟอร์มและโปรโตคอล
  • ใช้ Integration Server เข้ามาช่วยในเรื่องของการจำลอง complex business process
  • ใช้ SOA-governance tool เข้ามาช่วยในเรื่องของการจัดการและบริหารภาพรวมทั้งหมดของ SOA
  • ซึ่งดูจากลิสท์แล้วเรายังเห็นว่ามันดูเรียบง่าย และเมื่อเราร่างภาพออกมาเราจะได้โครงสร้างของ SOA ออกมาคร่าวๆดังภาพ

» Read more: Lightweight SOAs ทางเลือกของอนาคต

Designing RESTful Services

January 21st, 2010

Designing RESTful Services

The Object Model
เราจะใช้ Object Model ตามไดอะแกรมด้านล่างเป็นหลักในการออกแบบระบบ ซึ่งจะประกอบไปด้วย
Model the URIs
สิ่งที่เราควรสำหรับการออกแบบอินเทอร์เฟสต่างๆในระบบของเราคือการตั้งชื่อให้ endpoint สำหรับแต่ละ endpoint ที่มีในระบบก่อน ซึ่งสำหรับ RESTful นั้น endpoint จะถูกอ้างไปถึง resources และจะถูกระบุได้ด้วย URI เสมอซึ่งการใช้ URI ถือแบบข้อกำหนดพื้นฐานของ RESTful services โดยที่ในเซอร์วิสโมเดลของเรานั้นเราจะทำงานโดยตรงกับ Orders, Customers และ Products ซึ่งเราจะถือว่ามันเป็น top-level resources ซึ่งเราคาดหวังเบื้อต้นว่าเราจะสามารถเรียกลิสท์ของมัน ออกมาได้และสามารถจัดการมันได้เป็นชิ้นๆไปได้ สำหรับ LineItems นั้นจะถูกจับรวมเข้ากับ Order ออบเจคซึ่งทำให้มันไม่ถูกจัดลำดับให้เป็น top-level resource ซึ่งเราจะเรียกออบเจคแบบนี้ว่า sub-resource ดังนั้นเราจะมากำหนด URI สำหรับ top-level resource ก่อน
/orders
/orders/{id}
/products
/products/{id}
/customers
/customers/{id}

ตามตัวอย่างเราจะเห็น /orders ที่ใช้สำหรับดึงเอา Order ทั้งหมดออกมาแสดงและสำหรับการดึงเอา Order ตัวที่ชี้เฉพาะนั้นเราสามารถทำได้ด้วยการเรียก /orders/{id} โดยที่ {id} คือแพทเทิร์นของสตริงที่เป็นตัวระบุ Order นั้นๆดังนั้นสำหรับอีกสองออบเจคที่เหลือก็จะมีวิธีการเข้าถึงในรูปแบบเดียวกัน

Defining the Data Format
ส่วนที่สำคัญอีกอย่างของการสร้าง RESTful services คือการกำหนดรูปแบบของ resources ที่เราจะส่งกลับไปยังเครื่องไคล์แอนท์ ซึ่งเราต้องยอมรับว่า XML เป็นรุปแบบของการส่งข้อมูลที่ได้รับความนิยมมากที่สุด เพราะเกือบทุกภาษาในโลกมี API สำหรับจัดการกับ XML ทั้งหมด ส่วนรูปแบบที่ได้รับความนิยมรองลงมาคือ JSON ที่แพร่หลายมากในหมู่คนที่ใช้ AJAX แต่ในหนังสือเล่มนี้เราจะเน้นไปที่ XMLและการใช้งาน XMl นั้นเราต้องการหนดรูปของเอกสารผ่านการใช้ XML Schema เนื่องจากตัว XML Schema นั้นอธิบายโครงสร้างของเอกสารได้ละเอียดมากกว่าและอ่านเข้าใจได้ง่ายกว่า DTD มาก

Read and Update Format
หลังจากที่เราได้ XML เป็นตัวกลางสำหรับรับส่งข้อมูลแล้วส่วนต่อไปคือการดูในรายละเอียดของรูปแบบการทำงานเนื่องจากฟอร์แม็ทของเอกสารจะมีลักษณะที่แตกต่างกันมากระหว่างการทำการ read หรือ update กับการ create ตัว resource บนเซิร์ฟเวอร์ดังนั้นเราจะมาดูการ read/update กันก่อน ซึ่งส่วนแรกที่เราจะดูกันคือส่วนของ link ที่เราใช้สำหรับทุกๆออบเจคไม่ว่าจะเป็น Order, Customer และ Product

<link rel="self" href="http://example.com/..."/></div>
<div>
link อิลิเมนท์นั้นใช้สำหรับลิงค์ไปยังข้อมูลเพิ่มเติมของรีซอร์สที่เราสนใจ เพราะบางครั้งเราไม่สามารถส่งข้อมูลทั้งหมดกลับมาที่ฝั่งไคล์แอนท์ได้ ทำให้เราต้องใส่ link มาแทน ส่วน rel หมายถึงว่าตัว link นี้ชี้ไปยังรีซอร์สตัวไหนซึ่ง self หมายความว่ามันชี้ไปที่ตัวมันเอง โดยที่ปลายทางที่มันชี้ไปจะดูที่ href ส่วนต่อไปคือรายละเอียดของ Customer ที่เราจะใช้แสดง
<customer id="771">
<link rel="self" href="http://example.com/customers/771"/>
<first-name>Bill</first-name>
<last-name>Burke</last-name>
<street>555 Beacon St.<street>
<city>Boston</city>
<state>MA</state>
<zip>02115</zip>
</customer>

หลังจาก Customer แล้วเราจะมาดูส่วนของ Product ที่ดูเรียบง่ายมาก

<product id="543">
<link rel="self" href="http://example.com/products/543"/>
<name>iPhone</name>
<cost>$199.99</cost>
</product>

ต่อมาเราจะลองมาดูกันว่าถ้าเราลองใส่ attribute ให้ Customer และ Product เข้าไปอีกเราก็ยังสามารถเข้าใจโครงสร้างของมันได้ง่ายเหมือนเดิม นี่คือข้อดีของ RESTful อีกข้อหนึ่ง

<order id="133">
<link rel="self" href="http://example.com/orders/133"/>
<total>$199.02</total>
<date>December 22, 2008 06:56</date>
<customer id="117">
<link rel="self" href="http://example.com/customers/117"/>
<first-name>Bill</first-name>
<last-name>Burke</last-name>
<street>555 Beacon St.<street>
<city>Boston</city>
<state>MA</state>
<zip>02115</zip>
</customer>
<line-items>
<line-item id="144">
<product id="543">
<link rel="self" href="http://example.com/products/543"/>
<name>iPhone</name>
<cost>$199.99</cost>
</product>
<quantity>1</quantity>
</line-item>
</line-items>
</order>

จากตัวอย่างข้อมูลด้านบนเราจะเห็นว่า Order เป็น top-level อิลิเมนท์ที่มีข้อมูล total และ date โดยที่มันเองมี seb-element เป็น Customer และ Product ซึ่งการใช้รูปแบบของข้อมูลแบบนี้เราจะเห็นประโยชน์ของการใช้ link ในกรณีที่ลูกค้ารู้ข้อมูลเพิ่มเติมของ Customer หรือ Product ก็สามารถกดผ่าน link ไปได้เลย

Create Format
ในกรณีที่เราต้องการสร้าง OrderS, CustomerS หรือ ProductS รูปแบบของเมสเสจที่เราจะใช้ส่งนั้น จะต้องมีการตัดเอาอิลิเมนท์บางอย่างออกไปเช่น ID และ Link เพราะสองสิ่งนี้จะถูกสร้างภายหลังที่รีเควสถูกประมวลผลเรียบร้อยแล้วดังนั้นรีเควสเมสเสจจะมีรูปร่างดังตัวอย่าง

<product>
<name>iPhone</name>
<cost>$199.99</cost>
</product>

ดังนั้นสำหรับ Customer และ Orders เองก็จะใช้รูปแบบของเมสเสจนี้เหมือนกัน

Assigning HTTP Methods
ส่วนต่อมาที่เราต้องทำคือเราต้องเลือก HTTP methods ให้เหมาะสมกับรีซอร์สที่เราจะเปิดให้ใช้งาน และตามข้อตกลงเราควรจะดูว่าการทำงานของรีซอร์จให้เป็นไปตามมาตรฐานของ HTTP method เช่น HTTP GET ควรถูกจัดเข้ากับการทำงานที่เป็นแบบ read-only การกำหนด HTTP method ให้กับรีซอร์สแบบสอดคล้องกันจะทำให้คนที่มาเรียกใช้เซอร์วิสของเราสามารถทำความเข้าใจกับความสามารถของเซอร์วิสที่เราเตรียมให้ไว้ได้ง่ายมาก ต่อไปเราจะลองมาทำความเข้าใจทีละเมธอดที่จะถูกนำไปใช้เพื่อสร้าง URI ที่เราต้องการ

Browsing All Orders, Customers, or Products
สำหรับการเรียกดูข้อมูล Customer, Order หรือ Product ทั้งหมดเราจะกำหนด URI ให้เป็นลักษณะนี้
/orders
/products
/customers

โดยที่การทำงานของมันจะถูกผูกเข้ากับ HTTP GET และเมื่อเราเอามารวมกันเราจะได้รีเควสเมสเสจประมาณนี้

GET /products HTTP/1.1

ดังนั้นเมื่อเราส่งรีเควสไปแล้วเราจะได้ข้อมูลกลับมาจากเซิร์ฟเวอร์ลักษณะประมาณนี้

HTTP/1.1 200 OK
Content-Type: application/xml
<products>
<product id="111">
<link rel="self" href="http://example.com/products/111"/>
<name>iPhone</name>
<cost>$199.99</cost>
</product>
<product id="222">
<link rel="self" href="http://example.com/products/222"/>
<name>Macbook</name>
<cost>$1599.99</cost>
</product>
...
</products>

ปัญหาหลักของการใช้ URI แบบนี้คือในกรณีที่ข้อมูลมีขนาดน้อยมันจะทำงานได้ตามปกติแต่ถ้าข้อมูลมีเยอะมากจะทำให้เราได้ผลกลับมามีขนาดใหญ่มาก ดังนั้นเพื่อแก้ปัญหาเราจะส่ง
ตัวแปรเพิ่มเข้ามาเพื่อจำกัดปริมาณข้อมูลที่ส่งมาเช่นเราจะทำการเพิ่มตัวแปรชื่อ startIndex และ size เข้าไปดังนี้

GET /orders?startIndex=0&size=5 HTTP/1.1
GET /products?startIndex=0&size=5 HTTP/1.1
GET /customers?startIndex=0&size=5 HTTP/1.1

Obtaining Individual Orders, Customers, or Products
ต่อไปคือการดึงเอาเฉพาะออบเจคที่เราต้องการออกมาเท่านั้นซึ่งจะเลือกใช้ URI ตามนี้ทั้ง Orders, Customers และ Products
/orders/{id}
/products/{id}
/customers/{id}

แน่นอนเราจะผูกการทำงานนี้เข้ากับ HTTP GET และส่งค่าที่ใช้ระบุตัวตนของออบเจคนั้นๆไปด้วยเช่น
GET /orders/232 HTTP/1.1

หลังจากที่ส่งรีเควสนี้เพื่อขอ Representation ของ Order หมายเลข 232 ไปแล้วผลที่เราจะได้รับมาก็จะมีลักษณะตามนี้

HTTP/1.1 200 OK
Content-Type: application/xml
<order id="232">...</order>
Creating an Order, Customer, or Product
Let’s look at both ways.หลังจากที่เราดูการดึงข้อมูลด้วยการใช้ HTTP GET แล้วจากนี้เราจะมาดูการสร้าง Order, Customer และ Product ด้วยกสนใช้ HTTP PUT หรือ POST

Creating with PUT
ถ้าเรายึดตามคำจำกัดความแล้ว HTTP PUT จะใช้สำหรับการสร้างหรือแก้ไขเสตทของออบเจคบนเซอิร์ฟเวอร์ เริ่มที่การสร้างก่อน, ในกรณีที่เราต้องการสร้าง Order, Customer หรือ Product เราก็เพียงส่งรีเควสแบบ PUT ไปที่ URI ที่เป็นตัวแทนของออบเจคนั้นๆเช่น

PUT /orders/233 HTTP/1.1
PUT /customers/112 HTTP/1.1
PUT /products/664 HTTP/1.1

และจากมาตรฐานของ HTTP PUT เราจะพบว่ามันจะส่งผลกลับมาเป็น 201 “Created” ในกรณีที่มันสร้างรีซอร์สได้ตามที่เราส่งไป และนอกจากนี้ HTTP PUT ยังเป็น idempotent อีกด้วยเพราะไม่ว่าเราจะส่ง /orders/233 ไปที่เซิร์ฟเวอร์กี่ครั้งก็ตามผลที่ได้จะเหมือนเดิมเสมอ นั่นหมายความว่าเราสามารถส่งรีเควสซ้ำมากี่รอบก็ได้โดยที่ไม่ต้องคำนึงผลข้างเคียงที่จะเกิดส่วนข้อเสียหลักของการสร้างข้อมูลด้วย PUT คือที่ฝั่งไคล์แอนท์จำเป็นต้องเตรียม ID ให้กับออบเจคนั้นๆด้วยดังนั้นในกาณีที่เราออกแบบระบบให้มีการสร้าง ID ที่ฝั่งฌซิร์ฟเวอร์เราจำเป็นต้องใช้
POST แทน PUT

Creating with POST
การสร้าง Order, Customer หรือ Product ด้วย HTTP POST นั้นจะซับซ้อนกว่า PUT โดยที่การสร้างนั้นผั่งไคล์แอนท์ต้องส่งข้อมูลที่จำเป็นทั้งหมดไปที่ URI ที่ใช้แทนออบเจค โดยเว้นในส่วนของ ID ไว้

Content-Type: application/xml
<order>
<total>$199.02</total>
<date>December 22, 2008 06:56</date>
...
</order>

หลังจากได้รับเมสเสจนี้แล้วเซิร์ฟเวอร์จะทำการ process XML และทำการสร้าง Order ตัวใหม่ขึ้นมาโดยการใช้ database-generated ID ให้เราแต่ปัญหาคือหลังจากสร้างเสร็จแล้วเราจะอ้างถึงออบเจคที่เนสเพิ่งสร้างไปได้ยังไง เพราะเราไม่รู้จัก ID ของมัน, แล้ว URI ไหมที่เราจะใช้อ้างถึงมั้น ??????? เพื่อแก้ปัญหานี้เราจำเป็นต้องเพิ่มข้อมูลเข้าไปใน HTTP response ดังนี้

HTTP/1.1 201 Created
Content-Type: application/xml
Location: http://example.com/orders/233
<order id="233">
<link rel="self" href="http://example.com/orders/133"/>
<total>$199.02</total>
<date>December 22, 2008 06:56</date>
...
</order>

จากข้อมูลชุดนี้ที่เราได้กลับมาจากเซอิร์ฟเวอร์เราจะได้ response code 201 (Created) กลับมาเหมือนกับ PUT และเรายังได้ Location: มาอีกเพื่อใช้เป็น URI สำหรับการอ้างถึงออบเจคตัวที่เราเพิ่งสร้างไป เพื่อใช้ในการ update หรือ delete

<strong>Updating an Order, Customer, or Product</strong>
ต่อไปเป็นกรณีที่เราต้องการแก้ไขข้อมูลอะไรบางอย่างของ Order, Customer, Product ฝั่งไคล์แอนท์จะต้องส่ง representation ของข้อมูลใหม่ที่เราต้องการไปยัง URI ของออบเจคที่เราต้องการ  เช่นเราต้องการแก้ไขข้อมูลชุดที่ 232 ให้เปลี่ยนค่า cost เป้น 149.99 ก็สามารถทำได้ด้วยการส่ง HTTP PUT ดังนี้

PUT /orders/232 HTTP/1.1
Content-Type: application/xml
<product id="111">
<name>iPhone</name>
<cost>$149.99</cost>
</product>

ข้อดีของการใช้ PUT คือการทำงานมันเป็นแบบ idempotent ดังนั้นถ้าการทำงานที่ฝั่งเซิร์ฟเวอร์สำเร็จมันจะส่งผลกลับมาเป็น response code = 200 (OK) พร้อมกันกับ response message หรือในกรณีที่ไม่มี responsemessage ก็จะส่ง response code = 204 (No Content) กลับมาในกรณีนี้เราสามารถใช้ POST แทน PUT ได้แต่ฝั่งไคล์แอนท์จะต้อง assume เองว่าการแก้ไขด้วย POST จะเป็นแบบ nonidempotent

Removing an Order, Customer, or Product
สำหรับการลบข้อมูลนั้นเราสามารถทำได้ด้วยการส่ง HTTP DELETE ไปที่ URI ที่เราต้องการเช่น
DELETE /orders/233
หลังจากมีการลบข้อมูลไปเรียบร้อยแล้วเราจะได้รับ response code 200 หรือ 204 เช่นกัน

Cancelling an Order
มาถึงจุดนี้เราได้กำหนดฟังก์ชั่นการทำงานของเราได้เกือบทั้งหมดแล้วเช่น GET สำหรับการอ่าน, PUT สำหรับการแก้ไข และ POST สำหรับการสร้างและ DELETE สำหรับการลบ แต่อย่างไรก็ตามในกรณีที่เราต้องการทำcancelled ตัว OrderS และความหมายของการ cancelled นั้นไม่ใช่การลบออกจากระบบแต่เป็นการเปลี่ยนสถานะของข้อมูลเท่านั้น เราจะรับมือกับการทำงานแบบนี้ได้อย่างไร

Overloading the meaning of DELETE
วิธีการแรกคือการทำการ Overloading HTTP DELETE โดยเราสามารถใช้ HTTP DELETE แต่เราจะพ่วงตัวแปรเพิ่มเข้าไปอีกเพื่อขยายความโดยรีเควสที่ส่งไปจะมีรูปแบบตามนี้

DELETE /orders/233?cancel=true

นี่คือการทำ Overloading HTTP DELETE ซึ่งวิธีการนี้ไม่ถือเป็นวิธีการปฏิบัติที่ดีเนื่องจากมันจะทำให้ความยืดหยุ่นต่ำ.

States versus operations

สำหรับการออกแบบอินเทอร์เฟสของแต่ละโอเปอร์เรชั่นของออบเจคโมเดลของเรานั้น ก่อนตัดสินใจ เราควรจะตั้งคำถามกับตัวเราเองซึ่งเป็นผู้ออกแบบก่อนว่า “โอเปอร์เรชั่นที่กำลังจะทำนั้นเป็นการเปลี่ยนสเตทของรีซอร์สหรือไม่?” ถ้าคำตอบคือใช่ข้อมูลของโอเปอร์เรชั่นนั้นควรถูกใส่ไว้ใน data format ของรีซอร์สด้วย(ออกอาการ งงกันทุกคน) ยกตัวอย่างเช่นการทำ Cancelling ตัว Order เพราะเราจะเห็นได้ว่ากระบวนการทำงานของมันคือการเปลี่ยนสเตทของ Order ดังนั้นข้อมูลเรื่องสถานะของการถูก Cancel จำเป็นจะต้องถูกใส่ไว้ใน data format ของ Order ด้วยเช่น

<order id="133">
<link rel="self" href="http://example.com/orders/133"/>
<total>$199.02</total>
<date>December 22, 2008 06:56</date>
<cancelled>false</cancelled>
...
</order>
ดังนั้นเมื่อมีการใส่ข้อมูลนี้ลงไปใน data format แล้วทำหใ้เราสามารถใช้ operation ของ HTTP PUT ที่เรามีไว้อยู่แล้วเพื่อทำการเปลี่ยนสถานะของ Order ได้ดังนี้
PUT /orders/233 HTTP/1.1
Content-Type: application/xml
<order id="233">
<total>$199.02</total>
<date>December 22, 2008 06:56</date>
<cancelled>true</cancelled>
...
</order>
จากตัวอย่างนี้เราทำการส่ง representation ใหม่ของ Order ที่มีสถานะของ cancelled=true กลับไปยังเซิร์ฟเวอร์ แต่อย่างไรก็ตามการโมเดลโอเปอร์เรชั่นให้เป็นตามเสตทของรีซอร์สแบบนี้ก็ยังไม่สามารถตอบโจทย์ของเราได้
ทั้งหมดเช่นในกรณีที่เราต้องการ purge ตัว Order ที่มีสถานะ cancelled=true ออกจากฐานข็อมูลของเรานั้น เนาจะไม่สามารถโมเดล purge ให้ทำงานได้เหมือนกับที่เราทำกับ cancelled ได้การแก้ปัญหานี้เราสามารถทำได้ด้วยการ
ใช้การโมเดลโอเปอร์เรชั่นนี้ให้เป็น subresource ของ /orders และทำการสั่งให้ purge ทำงานผ่าน HTTP POST ดังนี้
POST /orders/purge HTTP/1.1
สิ่งที่น่าสนใจในโอเปอร์เรชั่นนี้คือเนื่องจากการ purge เราจะได้ URI มาหนึ่งตัว สำหรับ RESTful เราสามารถนำมันกลับมาใช้ได้อีกกับ HTTP method อื่นๆเช่นเราอาจสร้าง GET /orders/purge เพื่อทำหน้าที่ส่ง Order ทั้งหมด
ที่ถูก purge ออกไป หรือแม้กระทั่งสามารถใส่เงื่อไขในการ purge เช่นเราจะ purge เฉพาะ Order ที่มีอายุเกินกว่าช่วงเวลาที่เรากำหนดไว้ก็ได้ ซึ่งเราจะเป็นได้ว่ามันทำให้เราออกแบบระบบได้ยืดหยุ่นมากขึ้นมากๆ
Wrapping Up
จากเนื้อหาข้างบน เราจะเห็นตัวอย่างการโมเดลโอเปอร์เรชั่นด้วย RESTful ซึ่งจะเน้นการออกแบบ URI ที่เป็นตัวแทนของ end point เป็นหลัก โดยที่ end point ที่เรากล่าวถึงก็คือ resource ที่ฝั่งเซิร์ฟเวอร์เตรียมไว้ให้ และสำหรับทุกๆ resource เราจะทำการกำหนด HTTP methods ที่เหมาะสมให้กับมันว่ามันควรจะถูกผูกเข้ากับการทำงานแบบใด และเราได้ทำการออกแบบ data format เพื่อให้แลกเปลี่ยนระหว่างต้นทางและปลายทาง ตอนนี้เราพร้อมแล้วที่จะเริ่มสร้าง แอพพลิเคชั่นจริงๆขึ้นมา ซึ่งจะเริ่มที่บทถัดไป