AtomFeedView ใน SpringMVC 3 (RESTFul)

October 22nd, 2009 by roofimon Leave a reply »

ตัวอย่างการใช้งาน RestFul ใน SpringMVC3.0 คือเราจะสร้างแอพพลิเคชั่นที่สร้าง Content เหมือน Blog ขึ้นมาและเนื้อหาสามารถถูกแปลงจากการแสดงผลแบบ HTML ปกติให้ไปเป็น Atom Feed ได้ด้วยก่อนอื่นเรามาดูการสร้าง
Content ก่อนเพราะเราจะใช้ package de.svenjacobs.loremipsum.LoremIpsum เพื่อสุ่มสร้าง content มาดังนี้

...
import de.svenjacobs.loremipsum.LoremIpsum;

public class SampleContent {

	private static final LoremIpsum textGenerator = new LoremIpsum();
	private static final Random random = new Random();
	private static int idCounter = 0;
	private String author;
	private Date publicationDate;
	private String text;
	private int id;

	public static SampleContent generateContent(String author, Date date) {
		SampleContent content = new SampleContent();
		content.author = author;
		content.publicationDate = date;
		content.id = idCounter++;
		content.text = textGenerator.getParagraphs(random.nextInt(9) + 1);

		return content;
	}
	.
	.
	// get set
	.
}

จากนั้นเราจะสร้าง Controller ที่ทำหน้าที่สร้าง Content ที่สร้างวิวแบบ HTML ปกติก่อน

@Controller
public class ContentController {

	private List<SampleContent> contentList = new ArrayList<SampleContent>();

	@RequestMapping(value="/content", method=RequestMethod.GET)
	public ModelAndView getContent() {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("content");
		mav.addObject("sampleContentList", contentList);
		return mav;
	}

	@RequestMapping(value="/content.html", method=RequestMethod.POST)
	public String addContent() {
		contentList.add(SampleContent.generateContent("Alef Arendsen", new Date()));
		return "redirect:content.html";
	}
}

การทำงานของ handler ตัวแรกจะทำหน้าที่ลิสท์ไอเท็ม SampleContent ออกมาโดยมันจะรับ request ประเภท GET เข้ามา ส่วน handler ตัวที่สองจะทำหน้าที่เพิ่มคอนเทนท์ SampleContent เข้าไปด้วยการใช้
SampleContent.generateContent() ส่วน handler ตัวนี้จะรับ request ประเภท POST นอกจากนี้เราใช้ @RequestMapping เพื่อระบุ URI ที่เราระบุให้ handler ซึ่งการใช้งาน @RequestMapping นั้นเราต้องไปเพิ่ม Configuration
ชื่อ Component Scanner เข้าไปที่ rest-servlet.xml นอกจากนี้ส่วนสำคัญที่เราต้องใช้ในการทำงาน REST สำหรับ Spring คือ ViewResolver ซึ่งในกรณีนี้เราจะใช้ InternalResourceViewResolver ซึ่งมันจะทำหน้าที่เลือกชนิดของวิวให้ถูกต้อง
(ในกรณีนี้คือ content เพราะเราใช้ getContent() ใน handler)

Implementing the AtomView
สำหรับการสร้าง AtomView เราจะใช้ Rome โปรเจคโดยส่วนของ AtomView นั้นเราจำเป็นต้องสร้างวิวของเราเองเพื่อใช้ในการแสดงผล Atom โดยเราจะสร้าง SampleContentAtomView ที่มีรายละเอียดการทำงานดังนี้

public class SampleContentAtomView extends AbstractAtomFeedView {

    @Override
    protected void buildFeedMetadata(Map model, Feed feed, HttpServletRequest request) {
        System.out.println("SampleContentAtomView.buildFeedMetadata");
        feed.setId("tag:springsource.com");
        feed.setTitle("Sample Content");
        @SuppressWarnings("unchecked")
        List<SampleContent> contentList = (List) model.get("sampleContentList");
        for (SampleContent content : contentList) {
            Date date = content.getPublicationDate();
            if (feed.getUpdated() == null || date.compareTo(feed.getUpdated()) > 0) {
                feed.setUpdated(date);
            }
        }
    }

   @Override
    protected List buildFeedEntries(Map model,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        @SuppressWarnings("unchecked")
        List<SampleContent> contentList = (List) model.get("sampleContentList");
        List entries = new ArrayList(contentList.size());

        for (SampleContent content : contentList) {
            Entry entry = new Entry();
            String date = String.format("%1$tY-%1$tm-%1$td", content.getPublicationDate());
// see http://diveintomark.org/archives/2004/05/28/howto-atom-id#other
            entry.setId(String.format("tag:springsource.com,%s:%d", date, content.getId()));
            entry.setTitle(String.format("On %s, %s wrote", date, content.getAuthor()));
            entry.setUpdated(content.getPublicationDate());

            Content summary = new Content();
            summary.setValue(content.getText());
            entry.setSummary(summary);

            entries.add(entry);
        }

        return entries;

    }
}

ส่วนสุดท้ายคือการการกำหนด media type เพิ่มสำหรับ Atom เนื่องจาก Atom จะใช้ ‘application/atom+xml’ สำหรับ Html เราจะใช้ ‘text/html’ หรือ ‘text/xhtml’ ซึ่งโดยปกติเบราส์เซอร์จะกำหนดค่ากลุ่มของ media type ที่มันส่งออไปไปพร้อม
กับ HTTP header และไม่มีทางที่เราจะเปลี่ยนประเภทของ media type ได้เลยโดยการใช้ browser ดังนั้นการใช้ประเภทของไฟล์ (*.atom, *.json …) เป็นทางออกที่ดีในการบอกเซิร์ฟเวอร์ว่าประเภทของ representation ที่เราต้องการคืออะไร

Spring 3.0 มี ContentNegotiatingViewResolver นั้นสามารถทำงานได้ทั้ง extension และ accept header และหลังจากที่เรากำหนด media type ที่เราต้องการใช้มันจะทำหน้าที่ส่งงานให้กับ view resolver ตัวที่เหมาะสามให้กับเราโดยในตัวอย่าง
นี้มันจะทำการโยนไปให้ BeanNameViewResolver( resolve ตัว SampleContentAtomView ในกรณีที่ต้องการ ) หรืออาจจะเป็น InternalResourceViewResolver ซึ่งเราจะต้องเปลี่ยน Configuration ให้เป็นแบบด้านล่าง

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="atom" value="application/atom+xml"/>
                <entry key="html" value="text/html"/>
            </map>
        </property>

        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
                <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="prefix" value="/WEB-INF/jsp/"/>
                    <property name="suffix" value=".jsp"/>
                </bean>
            </list>
        </property>
    </bean>

ตัว ContentNegotiatingViewResolver จะทำการหา extension ก่อนว่ามีหรือไม่หลังจากที่ได้รับ Accept Header มาแล้วดังนั้นเราจะต้องแมป extension ให้เหมาะกับ media types ที่เราต้องการได้เช่น .html เราต้องกำหนดให้ใช้ text/html ส่วน
.atom เราจะให้ใช้ application/atom+xml ดังนั้นเมื่อ .atom ถูกส่งเข้ามาตัว ContentNegotiatingViewResolver จะทำการหาวิวที่เหมาะสมกับ application/atom+xml ส่วนในกรณีที่เป็น .html มันจะทำการเปลี่ยนไปหาวิวที่ใช้แสดง text/html
จากนี้เราจะสามารถเรียก

http://localhost:8888/SpringMVC3RestFul-1.0-SNAPSHOT/rest/content.html

เพื่อแสดงและเพิ่ม content ได้

http://localhost:8888/SpringMVC3RestFul-1.0-SNAPSHOT/rest/content.atom

ใช้สำหรับแสดงผลเป็น Atom Feed ครับ

******* source code สามารถ check out ได้ที่ http://code.google.com/p/spring-atomfeed-view/

Advertisement

Leave a Reply

You must be logged in to post a comment.