ASP.NET MVC Frameworkをさわってみる 2

前回はページの表示のみだったので、次はURLにクエリを渡してみる。

単純に「Home/Index」というURLに対して、「message」というクエリ名で値を渡すとする。普通ならこのクエリの値はHttpRequestのQueryStringプロパティで取得するが、ASP.NET MVC Frameworkではこれを自動的にメソッドの引数にマップできる。
マップの仕方は単にURLにマップされたメソッド、ここでは「Index」メソッドにクエリ名と同じ名前の引数を定義するだけ。

App_Code/Controllers/HomeController.cs
[ControllerAction]
public void Index(string message) {
}

こうすることで自動的に「message」引数に同じ名前のクエリの値が渡される。(大文字小文字は区別されない)

この値を画面に表示する場合は、まず画面に値を引き渡す必要がある。それにはViewDataプロパティを使用する。

App_Code/Controllers/HomeController.cs
[ControllerAction]
public void Index(string message) {
    ViewData["message"] = message;

    RenderView("Home");
}

このプロパティはキーと値のマップなので、ここでは「message」というキー名で値を設定している。この手法は従来のViewStateやSessionプロパティと同じやり方なので馴染みのある方法だろう。

そしてこれを画面側に表示する。

Views/Home/Home.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs" Inherits="Views_Home" %>

<!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 runat="server">
    <title>Home</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <p><%= ViewData["message"] %></p>
    </div>
    </form>
</body>
</html>

ViewDataプロパティから値を取得するだけ。

このやり方でも別に問題は無いが、ViewDataは値とキーのマップであるため、値を取得する時は文字列でキー名を指定し、object型で値を取得するためタイプセーフとは言い難い。

しかし、以下のようにすればこれを解決できる。

Views/Home/Home.aspx.cs
using System;

public partial class Views_Home : System.Web.Mvc.ViewPage<string> {
}

画面が継承するViewPageクラスに引き渡す値の型を型パラメータとして指定する。こうすることでViewDataの型がマップから指定された型に変更される。

そうすると画面のコードを以下のように書き直す事ができる。

<p><%= ViewData %></p>

あとはコントローラメソッドからの値の引渡し方を変更する。

App_Code/Controllers/HomeController.cs
[ControllerAction]
public void Index(string message) {
    RenderView("Home", (object)message);
}

RenderViewメソッドの第二引数で引き渡す値を指定する。object型にキャストしているのはstring型で渡すと「RenderView(string viewName, string masterName)」という別のオーバーロードと解釈されてしまうため。

こうすることでタイプセーフに値を渡す事ができる。独自の型を定義して渡す場合などに重宝するだろう。

また、この方法では「/Home/Index?message=Hello」というURLでアクセスすることになるが、アプリケーションファイルに以下のようなRouteを定義することで「/Home/Index/Hello」というようなURLにする事もできる。

Global.asax

<%@ Application Language="C#" %>
<%@ Import Namespace="System.Web.Mvc" %>

<script RunAt="server">

    void Application_Start(object sender, EventArgs e) {
        RouteTable.Routes.Add(new Route {
            // 独自のルート
            Url = "[controller]/[action]/[message]",
            Defaults = new { action = "Index", message = (string)null },
            RouteHandler = typeof(MvcRouteHandler)
        });
        RouteTable.Routes.Add(new Route {
            Url = "Default.aspx",
            Defaults = new { controller = "Home", action = "Index", id = (string)null },
            RouteHandler = typeof(MvcRouteHandler)
        });
    }
       
</script>

「[controller]/[action]/[message]」というように三つ目のパスをパラメータにする。

この辺の自由度は素晴らしい。