ASP.NET MVC5 Attribute Routing簡介

by Vivid 30. 七月 2014 04:54

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N140715003
出刊日期:2014/7/30
開發工具:Visual Studio 2013 Ultimate
版本:.NET Framework 4.5.X

 

路由 (Routing)是ASP.NET MVC 將URL對應到Action的機制,ASP.NET MVC5 版本新增一個路由機制,稱做Attribute Routing,簡化路由的設定動作,能更精確地掌控路由的行為,在這一篇文章之中,我們將介紹ASP.NET MVC5 Attribute Routing的運作方式與使用。

顧名思義Attribute Routing,就是利用Attribute來控制路由,在C# 程式語言之中,Attribute使用 [ ]符號表示。我們從在ASP.NET MVC專案之中啟用Attribute Routing開始談起。

啟用Attribute Routing

首先利用Visual Studio 2013建立一個範本專案,從「File」-「New」-「Project」,在「New Project」對話盒中,選取左方「Installed」-「Template」-「Visual C#」程式語言,從「Web」分類中,選取「ASP.NET Web Application」。設定專案名稱後按下「OK」鍵,在「New ASP.NET Project」對話盒中選取「MVC」,勾選下方的「MVC」項目,然後按下「OK」按鈕建立專案。

若在ASP.NET MVC範本專案要啟用Attribute Routing,你可以在專案中App_Start資料夾內RouteConfig.cs檔案內RouteConfig類別的RegisterRoutes方法之中加入一行程式碼,叫用MapMvcAttributeRoutes()方法來啟用之:

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

      routes.MapMvcAttributeRoutes( );

      routes.MapRoute(
          name: "Default" ,
          url: "{controller}/{action}/{id}" ,
          defaults: new { controller = "Home" , action = "Index" , id = UrlParameter.Optional }
      );
    }
  }
}

 

你可以保留預設的Default路由,預設的路由機制,而叫用MapMvcAttributeRoutes()方法的程式碼應該要出現在預設路由程式之前。

因為範本專案在Global.asax檔案內的Application_Start事件處理常式之中,叫用了RouteConfig.RegisterRoutes方法,因此網站只要一執行,就會啟用Attribute Routing:

protected void Application_Start( ) {
   AreaRegistration.RegisterAllAreas( );
   FilterConfig.RegisterGlobalFilters( GlobalFilters.Filters );
   RouteConfig.RegisterRoutes( RouteTable.Routes );
   BundleConfig.RegisterBundles( BundleTable.Bundles );
}

 

在以下的範例中在Models定義一個Employee類別,程式如下:

public class Employee {
    public int ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

 

設計一個EmployeeController程式如下,包含一個DetailsByName方法,使用Route Attribute指定路由為「"Employee/{empName}"」:

public class EmployeeController : Controller {
[Route( "Employee/{empName}" )]
public ActionResult DetailsByName( string empName ) {
  if ( empName==null ) {
    return HttpNotFound( );
  }
  List<Employee> empList = new List<Employee>( ) {
    new Employee() { ID=1 , Name="Mary", Age=30 } ,
    new Employee() { ID=2 , Name="Candy", Age=32 } ,
    new Employee() { ID=3 , Name="Lilly", Age=62 } ,
    new Employee() { ID=4 , Name="Sally", Age=35 } ,
    new Employee() { ID=5 , Name="Mickey", Age=41 }
  };

  var emp = empList.FirstOrDefault<Employee>( e => e.Name.ToUpper() == empName.ToUpper() );
  return View( emp );
}
}

 

同時產生一個Details View定義如下:

@model MVCAttributeRoute.Models.Employee

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>DetailsByName</title>
</head>
<body>
    <div>
        <h4>Employee</h4>
        <hr />
        <dl class="dl-horizontal">
            <dt>
                @Html.DisplayNameFor(model => model.Name)
            </dt>
   
            <dd>
                @Html.DisplayFor(model => model.Name)
            </dd>
   
            <dt>
                @Html.DisplayNameFor(model => model.Age)
            </dt>
   
            <dd>
                @Html.DisplayFor(model => model.Age)
            </dd>
   
        </dl>
    </div>
    <p>
        @Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
        @Html.ActionLink("Back to List", "List")
    </p>
</body>
</html>

 

若執行專案,在URL輸入:

http://localhost:64432/Employee/Mary

執行結果請參考下圖所示,可以正確地將員工資料印出:

clip_image002

圖 1

使用Visual Studio,在程式中設定中斷點如下,按F5以除錯模式執行:

clip_image004

圖 2

接著在瀏覽器輸入以下URL,省略掉empName:

http://localhost:64432/Employee/

開發工具不會進入中斷點,而直接顯示錯誤訊息,請參考下圖所示,這表示路由系統沒有將請求導向DetailsByName方法:

clip_image006

圖 3

路由選擇性參數

要解決上個問題,可以將empName設計成選擇性參數,這樣一來便可以省略掉參數沒有傳值的問題。你可以在上個範例的Route Attribute中使用「?」符號來代表選擇性參數,例如修改上個範例DetailsByName程式碼如下:

[Route( "Employee/{empName?}" )]
public ActionResult DetailsByName( string empName ) {
   if ( empName==null ) {
     return HttpNotFound( );
   }
   List<Employee> empList = new List<Employee>( ) {
     new Employee() { ID=1 , Name="Mary", Age=30 } ,
     new Employee() { ID=2 , Name="Candy", Age=32 } ,
     new Employee() { ID=3 , Name="Lilly", Age=62 } ,
     new Employee() { ID=4 , Name="Sally", Age=35 } ,
     new Employee() { ID=5 , Name="Mickey", Age=41 }
   };

   var emp = empList.FirstOrDefault<Employee>( e => e.Name.ToUpper() == empName.ToUpper() );
   return View( emp );
}

 

同樣以Visual Studio 2013測試,在程式中先設定中斷點:

clip_image008

圖 4

接著在瀏覽器輸入以下URL,省略掉empName:

http://localhost:64432/Employee/

這次以除錯模式執行時,Visual Stuido 2013開發工具會進入中斷點,雖然依舊找不到員工資料,但從測試中得知,路由系統有將請求導向DetailsByName方法,請參考下圖所示:

clip_image010

圖 5

這回你會得到一個404錯誤,請參考下圖所示:

clip_image012

圖 6

設定路由參數預設值

要解決上一個問題的最好方式是:為路由參數設定一個預設值。只要在參數使用「=」號指定一個值就好了,例如修改範例中的DetailsByName方法,設定預設值為「Mary」:

[Route( "Employee/{empName=Mary}" )]
public ActionResult DetailsByName( string empName ) {
  if ( empName == null ) {
    return HttpNotFound( );
  }
  List<Employee> empList = new List<Employee>( ) {
    new Employee() { ID=1 , Name="Mary", Age=30 } ,
    new Employee() { ID=2 , Name="Candy", Age=32 } ,
    new Employee() { ID=3 , Name="Lilly", Age=62 } ,
    new Employee() { ID=4 , Name="Sally", Age=35 } ,
    new Employee() { ID=5 , Name="Mickey", Age=41 }
  };

  var emp = empList.FirstOrDefault<Employee>( e => e.Name.ToUpper( ) == empName.ToUpper( ) );
  return View( emp );
}

 

接著在瀏覽器輸入以下URL,省略掉empName:

http://localhost:64432/Employee/

這樣可以正確地導向DetailsByName方法,執行結果請參考下圖所示:

clip_image014

圖 7

設定路由前置字元(Route Prefix)

如果每個Action都需要設定路由,你可以在控制器,為所有的Action設定一個路由前置字元(Route Prefix),這樣的好處是,Action的設定將會更為簡潔。例如以下的EmployeeController包含兩個Action:List與DetailsByName,這兩個Action的路由都是以「Employee」字串開始:

public class EmployeeController : Controller {
  // GET: Employee
   [Route( "Employee" )]
  public ActionResult List( ) {
    List<Employee> empList = new List<Employee>( ) {
      new Employee() { ID=1 , Name="Mary", Age=30 } ,
      new Employee() { ID=2 , Name="Candy", Age=32 } ,
      new Employee() { ID=3 , Name="Lilly", Age=62 } ,
      new Employee() { ID=4 , Name="Sally", Age=35 } ,
      new Employee() { ID=5 , Name="Mickey", Age=41 }
    };

    return View(empList );
  }
  [Route( "Employee/{empName=Mary}" )]
  public ActionResult DetailsByName( string empName ) {
    if ( empName == null ) {
      return HttpNotFound( );
    }
    List<Employee> empList = new List<Employee>( ) {
      new Employee() { ID=1 , Name="Mary", Age=30 } ,
      new Employee() { ID=2 , Name="Candy", Age=32 } ,
      new Employee() { ID=3 , Name="Lilly", Age=62 } ,
      new Employee() { ID=4 , Name="Sally", Age=35 } ,
      new Employee() { ID=5 , Name="Mickey", Age=41 }
    };

    var emp = empList.FirstOrDefault<Employee>( e => e.Name.ToUpper( ) == empName.ToUpper( ) );
    return View( emp );
  }
}

 

執行程式接著在瀏覽器輸入以下URL,省略掉empName:

http://localhost:64432/Employee

則會叫用到List方法,顯示員工清單,執行結果請參考下圖所示:

clip_image016

圖 8

在瀏覽器輸入以下URL,指定empName為「Candy」:

http://localhost:64432/Employee/Candy

將叫用到DetailsByName方法,執行結果請參考下圖所示:

clip_image018

圖 9

改用RoutePrefix Attribute

上個例子,需要重複在Action的Route Attribute之中指定「Employee」字串,我們可以改用RoutePrefix Attribute讓程式變更簡潔一些,修改程式碼如下將會得到和上個範例一樣的執行結果:

[RoutePrefix( "Employee" )]
public class EmployeeController : Controller {
   // Employee
    [Route( )]
   public ActionResult List( ) {
     List<Employee> empList = new List<Employee>( ) {
       new Employee() { ID=1 , Name="Mary", Age=30 } ,
       new Employee() { ID=2 , Name="Candy", Age=32 } ,
       new Employee() { ID=3 , Name="Lilly", Age=62 } ,
       new Employee() { ID=4 , Name="Sally", Age=35 } ,
       new Employee() { ID=5 , Name="Mickey", Age=41 }
     };

     return View(empList );
   }

   //Employee/Candy
   [Route( "{empName=Mary}" )]
   public ActionResult DetailsByName( string empName ) {
     if ( empName == null ) {
       return HttpNotFound( );
     }
     List<Employee> empList = new List<Employee>( ) {
       new Employee() { ID=1 , Name="Mary", Age=30 } ,
       new Employee() { ID=2 , Name="Candy", Age=32 } ,
       new Employee() { ID=3 , Name="Lilly", Age=62 } ,
       new Employee() { ID=4 , Name="Sally", Age=35 } ,
       new Employee() { ID=5 , Name="Mickey", Age=41 }
     };

     var emp = empList.FirstOrDefault<Employee>( e => e.Name.ToUpper( ) == empName.ToUpper( ) );
     return View( emp );
   }
}

 

改寫路由前置字元(Route Prefix)

若某個Action路由規則和路由前置字元(Route Prefix)設定的不同,你可以使用波浪(~)符號來改寫路由前置字元(Route Prefix)定義的規則,例如以下的List方法:

[RoutePrefix( "Employee" )]
public class EmployeeController : Controller {
   // EmployeeList
    [Route("~/EmployeeList" )]
   public ActionResult List( ) {
     List<Employee> empList = new List<Employee>( ) {
       new Employee() { ID=1 , Name="Mary", Age=30 } ,
       new Employee() { ID=2 , Name="Candy", Age=32 } ,
       new Employee() { ID=3 , Name="Lilly", Age=62 } ,
       new Employee() { ID=4 , Name="Sally", Age=35 } ,
       new Employee() { ID=5 , Name="Mickey", Age=41 }
     };

     return View(empList );
   }

   //Employee/Candy
   [Route( "{empName=Mary}" )]
   public ActionResult DetailsByName( string empName ) {
     if ( empName == null ) {
       return HttpNotFound( );
     }
     List<Employee> empList = new List<Employee>( ) {
       new Employee() { ID=1 , Name="Mary", Age=30 } ,
       new Employee() { ID=2 , Name="Candy", Age=32 } ,
       new Employee() { ID=3 , Name="Lilly", Age=62 } ,
       new Employee() { ID=4 , Name="Sally", Age=35 } ,
       new Employee() { ID=5 , Name="Mickey", Age=41 }
     };

     var emp = empList.FirstOrDefault<Employee>( e => e.Name.ToUpper( ) == empName.ToUpper( ) );
     return View( emp );
   }
}

 

執行程式接著在瀏覽器輸入以下URL:

http://localhost:64432/EmployeeList

則會叫用到List 方法,執行結果請參考下圖所示:

clip_image020

在瀏覽器輸入以下URL,指定empName為「Candy」:

http://localhost:64432/Employee/Candy

設定路由前置字元(Route Prefix)還是有效的,此回將叫用到DetailsByName方法,執行結果請參考下圖所示:

clip_image022

圖 10

 

設定路由條件約束(Constraints)

路由條件約束可以讓你限制路由的參數為特定的型別,或座落在某個範圍之中,設定的語法為「{參數:條件約束}」({parameter:constraint}),可以使用的條件約束如下表:

條件約束

說明

alpha

限定為大寫或小寫的英文字元(a-z或A-Z)

bool

限定為布林值

datetime

限定為DateTime值

decimal

限定為decimal值

double

限定為64位元的符點數值

float

限定為32位元的符點數值

guid

限定為GUID值

int

限定為32位元的整數值

length

限定字串,要符合指定的長度

long

限定為64位元的整數值

max

限定為整數,不能大於指定的值

maxlength

限定字串最大長度

min

限定為整數,不能小於指定的值

minlength

限定字串最小長度

range

限定為整數的範圍

regex

限定要符合規則運算式

以下範例設定DetailsByID方法使用({ID:int})條件約束,設定只能輸入整數值:

 

[RoutePrefix( "Employee" )]
  public class EmployeeController : Controller {

    [Route( "{ID:int}" )]
    public ActionResult DetailsByID( int? ID ) {
      if ( ID == null ) {
        return HttpNotFound( );
      }
      List<Employee> empList = new List<Employee>( ) {
        new Employee() { ID=1 , Name="Mary", Age=30 } ,
        new Employee() { ID=2 , Name="Candy", Age=32 } ,
        new Employee() { ID=3 , Name="Lilly", Age=62 } ,
        new Employee() { ID=4 , Name="Sally", Age=35 } ,
        new Employee() { ID=5 , Name="Mickey", Age=41 }
      };

      var emp = empList.FirstOrDefault<Employee>( e => e.ID == ID) ;
      return View( "DetailsByName" , emp );
    }
  }

 

以下的設定則限定參數ID的範圍在3-5之間

[Route( "{ID:int:range(3,5)}" )]

執行程式接著在瀏覽器輸入以下URL,

http://localhost:64432/employee/5

則列出員工編號5號的資料,請參考下圖所示:

clip_image026

圖 12

若輸入以下URL:

http://localhost:64432/employee/1

則會得到錯誤訊息,請參考下圖所示:

clip_image028

圖 13

Tags:

.NET Magazine國際中文電子雜誌 | ASP.NET MVC | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List