ASP.NET MVC

ใน ASP.NET MVC เราสามารถออกแบบ URL ขึ้นมาแล้วส่ง request ไปยัง action ที่กำหนดไว้ได้ ทำให้ URL ของเว็บของเราไม่ขึ้นอยู่กับโครงสร้างของไฟล์ที่อยู่บนดิสก์จริงๆ โดย ASP.NET MVC อาศัย URL Routing ที่เพิ่มเข้ามาใน ASP.NET 3.5 SP1

หลังจากเราสร้าง Project Web Application ขึ้นมาแล้ว ลองเปิดดูที่ Global.asax จะเห็นว่าต่างจาก ASP.NET Web Application ธรรมดา เพราะจะมาการทำ Routing มาให้แล้ว

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            "Default",                                              // Route name
            "{controller}/{action}/{id}",                           // URL with parameters
            new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
        );
    }
    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

กฏง่ายๆของการ Routing คือ

  • หน้าไปหลัง เริ่มทำการทดสอบจากกฏที่เพิ่มเข้าไปก่อนเป็นลำดับแรก เจออันไหนก่อนก็จะ route ไปตามนั้น
  • ใช้ {} เพื่อเก็บค่าตัวแปร เราสามารถตั้งตัวแปรสำหรับ URL ได้ เช่น
    /blog/{year}/{month}/{day} จะ match กับ /blog/2009/1/2 โดยจะเกิดตัวแปรของ URL ชื่อ year, month และ day ขึ้นโดยมีค่าเป็น 2009, 2 และ 1 ตามลำดับ
  • ใช้ * เพื่อระบุว่าเป็น parameter ที่ไม่ทราบจำนวน

ASP.NET Routing จะต้องมี Routing Handler ซึ่งจะต้องเป็นคลาสที่ implement interface IRouteHandler ซึ่งปกติใน ASP.NET MVC จะแอบใช้ System.Web.Mvc.MvcRouteHandler

จากด้านบน เราสามารถแปลได้ง่ายๆว่า

  • ไม่ต้องสนใจถ้า request url ที่เป็น .axd โดยส่วนที่อยู่หน้า . จะถูกเก็บไว้ในตัวแปรชื่อ request ส่วนที่เหลือทั้งหมดจะถูกเก็บใน pathInfo
  • Request URL จะถูก Route ไปยัง controller และ action โดยระบุพารามิเตอร์ชื่อ id โดยอัตโนมัติหากไม่มีการกำหนดค่าใดๆ

เรามาลองสร้าง Flickr Mashup ขึ้นมาเล่นๆดูดีกว่า

ก่อนอื่นสร้าง PhotoController ขึ้นมาใหม่ โดยมีโค้ดดังนี้

public class PhotoController : Controller
{        
    public ActionResult Index()
    {
        Flickr f = new Flickr();
        Photos p = f.PhotosGetRecent(25, 1);            
        return View(p.PhotoCollection);
    }
}

จากแอคชันข้างต้น เราดึงรายละเอียดของรูปที่ถูกอัพโหลดขึ้นไปไว้ใน Flickr จำนวน 25 รูปแล้วเก็บไว้ใน ViewData โดยมีใช้
โดยใช้ View ง่ายๆ ชื่อ Index คือ

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Photo.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>MVC Flickr Sample</title>
</head>
<body>
    <div>
    <% foreach (var p in ViewData.Model)
       { %>                     
       <a href="<%= p.WebUrl %>">
       <img src="<%= p.SquareThumbnailUrl %>" alt="<%= p.Title %>" />                  
       </a>
    <% }%>
    </div>
</body>
</html>

โดยที่ View นี้เป็น Strong typed view

namespace MvcApplication1.Views.Photo
{
    public partial class Index : ViewPage<FlickrNet.Photo[]>
    {
    }
}

หลังจากเราตั้งค่า Flickr API เรียบร้อยตาม Document ก็ทดลองรันดูด้วย Url /photo/

ScreenShot001

ทีนี้เราลองเพิ่มอะไรขึ้นมาเล็กน้อยด้วยการเพิ่ม Route เข้าไปดังต่อไปนี้

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Search",                                              // Route name
            "search/{key}/{page}",                           // URL with parameters
            new { controller = "Photo", action = "Search", key = "", page = 1 },  // Parameter defaults
        );

        routes.MapRoute(
            "User",                                              // Route name
            "photo/{username}/{page}",                           // URL with parameters
            new { controller = "Photo", action = "PhotoByUser", page = 1 },  // Parameter defaults
        );

        routes.MapRoute(
            "Default",                                              // Route name
            "{controller}/{action}/{id}",                           // URL with parameters
            new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
        );

    }

จะเห็นว่าเราส่ง Request ไปยัง Action ใดๆ ก็ได้โดยไม่ขึ้นกับชื่อ กล่าวคือ เราจะทำการค้นหารูปหาก Request URL อยู่ในรูปแบบ search/คำค้น/หน้า และหากเป็น /photo/ชื่อผู้ใช้/หน้า ก็จะแสดงรูปของผู้ใช้คนนั้นแทนโดยเราไม่จำเป็นจะต้องสร้าง Search controller แต่อย่างใด แต่เราจะส่งไปที่ action Search และ PhotoByUser ตามลำดับ

โค้ดของ action ทั้งสองเป็นดังนี้

    public ActionResult PhotoByUser(string username, int page)
    {
        Flickr f = new Flickr();
        FoundUser u = f.PeopleFindByUsername(username);            
        if (u != null)
        {               
            Photos p = f.PeopleGetPublicPhotos(u.UserId, 25, page);                
            return View("index", p.PhotoCollection);
        }
        return View("index");
    }

    public ActionResult Search(string key, int page)
    {           
        Flickr f = new Flickr();
        PhotoSearchOptions option = new PhotoSearchOptions();
        option.Page = page;
        option.Text = key;         
        Photos p = f.PhotosSearch(option);                       
        return View("index", p.PhotoCollection);                        
    }

เมื่อเราทดสอบด้วยการเปิด URL /search/grassland/ และ /photo/wiennat จะปรากฎรูปขึ้นมาตามต้องการ

ScreenShot002

แล้วถ้าเราลองเปิดด้วย /search/grassland/ข้อความ และ /photo/wiennat/ข้อความ ล่ะ

ScreenShot004

ผลลัพธ์คือเกิด error ขึ้นเนื่องจากพารามิเตอร์ page นั้นมีค่าเป็น NULL แต่ว่าเราต้องการพารามิเตอร์ที่เป็นตัวเลข เรามีทางแก้สองทางคือ
1. กำหนดให้ประเภทพารามิเตอร์ page ให้เป็น int?
2. จำกัดรูปแบบของพารามิเตอร์ โดยกำหนด Constrain ให้รับแต่ตัวเลข เท่านั้น

    routes.MapRoute(
            "Search",                                              // Route name
            "search/{key}/{page}",                           // URL with parameters
            new { controller = "Photo", action = "Search", key = "", page = 1 },  // Parameter defaults
            new { key="[A-Za-z0-9]+", page="[0-9]*" }
        );

เมื่อผู้ใช้เข้ามาด้วย /search/grassland/ข้อความ ก็จะปรากฏข้อความที่บอกว่าไม่พบเอกสารดังกล่าวแทน

ScreenShot004

เป็นอันจบวิธีการใช้งาน URL Routing อย่างง่ายๆแล้ว สำหรับตอนหน้า เราจะมาดูว่าเราจะใช้ URL Routing ใน Web Form กันอย่างไร

สำหรับการใช้งานอื่นกรุณาทิ้งไว้ใน comment

After reading many blog post about ASP.NET MVC for a while, I decide to write a simple application for better understanding. Flickr app seems to be best fit this time as it can be implemented in an hour.

Continue reading...