normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

ASP.NET MVC4 の Web API を学んでみる その4 〜Filter編〜

その1 〜 その3 にて、 Web API の動作について解説したため、今回は Web API で Filter を利用する方法について紹介する。

コントローラ と Web API 用コントローラの違い

まず重要な点として、「通常のコントローラに適用する Filter と Web API のコントローラに適用する Filter では、継承するクラスが異なる」という点が存在する。

  • コントローラに適用する Filter → System.Web.Mvc.FilterAttribute
  • Web API のコントローラに適用する Filter → System.Web.Http.Filters.IFilter

コントローラに適用する Filter については別途「 ASP.NET MVCにおけるFilterの作成方法と実行順序制御 id:waritohutsu:20090704 」を参照いただくとして、Web API のコントロールに適用する Filter の作成を紹介する。
また、どうやらコントローラ向けのフィルタには存在したフィルタの実行順序制御プロパティである Order は存在しないようだ。

Web API 向け コントローラに適用する Filter を作成する

Web API 向けに作成されたフィルター・インターフェースである System.Web.Http.Filters.IFilter を継承した System.Web.Http.Filters.ActionFilterAttribute 属性クラスを利用し、以下の属性クラスを作成する。

  • MyActionFilterAttribute.cs
using System.Diagnostics;
using System.Web.Http.Filters;

namespace Mvc4WebApiApp.Filters
{
    public class MyActionFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        private readonly string message;

        public MyActionFilterAttribute(string message = "デフォルト")
        {
            this.message = message;
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            Debug.WriteLine("★★OnActionExecuted =" + message);
            base.OnActionExecuted(actionExecutedContext);
        }

        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            Debug.WriteLine("★★OnActionExecuting =" + message);
            base.OnActionExecuting(actionContext);
        }
    }
}

継承元の ActionFilterAttribute 属性クラスはクラスとメソッドのみに付与可能な属性でり、多重に付与が可能であるため、こちらが引き継がれる。

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IFilter
    {
        //(以下略)

Web API 向け Filter を利用し、ApiControllerに適用する

Filter を作成したので、次に Web API 向けコントローラである ApiController に対して同 Filter を適用する。適用を検証するため、以下の ApiController を作成した。

using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Diagnostics;
using System.Reflection;
using Mvc4WebApiApp.Filters;

namespace Mvc4WebApiApp.Controllers
{
    [MyActionFilter("①クラス単位")]
    public class ValuesController : ApiController
    {
        // GET /api/values
        [MyActionFilter("②メソッド単位")]
        public string[] Get()
        {
            Debug.WriteLine("☆☆ 現在のメソッド = {0}.{1}()",
                new object[] { this.GetType().Name, MethodBase.GetCurrentMethod().Name });
            return Enumerable.Range(1, 10).Select(n => "value No." + n).ToArray();
        }
        
        //(中略)

Web API 向け Filter の動作確認

作成した Web API 向けフィルターとコントローラを利用したアプリケーションの動作を確認する。Visual Studio からアプリケーションを起動し、http://loaclhost:<ポート番号>/api/values にアクセスすると、以下が「出力」ウィンドウに表示されることが確認できる。

★★OnActionExecuting =①クラス単位
★★OnActionExecuting =②メソッド単位
☆☆ 現在のメソッド = ValuesController.Get()
★★OnActionExecuted =②メソッド単位
★★OnActionExecuted =①クラス単位

ASP.NET MVC4 の Web API を学んでみる その3 〜モデルを利用したバインド〜

前回は基本的なパラメータのバインディングを試したが、次にモデルを利用したバインディングを紹介する。

モデルを利用したバインドのサンプルコード

モデルクラス、Web API コントローラ、画面のソースコードを以下に記載する。

  • HomeViewModel.cs
namespace Mvc4WebApiApp.ViewModels
{
    public class HomeViewModel
    {
        public string Comment { get; set; }
        public int Hentai { get; set; }
    }
}

ApiController も特に解説は不要だと思うが、DataContractJsonSerializer を利用してモデルを JSON 文字列に変換している点だけご注意いただきたい。

  • VMValuesController.cs
using System.Collections.Generic;
using System.Web.Http;
using System.Diagnostics;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Text;
using Mvc4WebApiApp.ViewModels;

namespace Mvc4WebApiApp.Controllers
{
    public class VMValuesController : ApiController
    {
        // GET /api/<controller>
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET /api/<controller>/5
        public string Get(int id)
        {
            return "value";
        }

        // POST /api/<controller>
        public HomeViewModel Post(HomeViewModel model)
        {
            //DataContractJsonSerializer を利用したモデルのシリアライズ
            var ms = new MemoryStream();
            new DataContractJsonSerializer(model.GetType()).WriteObject(ms, model);
            Debug.WriteLine("model = {1}", Encoding.UTF8.GetString(ms.ToArray()));
            return model;
        }

        // PUT /api/<controller>/5
        public void Put(int id, string value)
        {
        }

        // DELETE /api/<controller>/5
        public void Delete(int id)
        {
        }
    }
}
  • Index.cshtml
@model Mvc4WebApiApp.ViewModels.HomeViewModel
@{
    ViewBag.Title = "ViewModel";
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>ASP.NET Web API</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script>
    <meta name="viewport" content="width=device-width" />
</head>
<body>
    <div id="body">
        <form id="myform" action="/">
        <input type="button" id="api_button" value="リクエスト送付" />
        <p>
            <input type="radio" name="hentai" value="0" checked />とても変態
            <input type="radio" name="hentai" value="1" />普通に変態
            <input type="radio" name="hentai" value="2" />ちょっと変態
            <input type="radio" name="hentai" value="3" />割と普通
        </p>
        <p>
            <input type="text" value="文字列" name="comment" />
        </p>
        </form>
    </div>
    <script type="text/javascript">
        $('#api_button').on('click', function () {
            var data = $('#myform').serialize();
            $.ajax({
                type: 'POST',
                url: '@Url.Content("~/api/vmvalues")',
                data: data,
                success: function (res) {
                    alert("response = "+JSON.stringify(res));
                },
                error: function (res) {
                    alert('エラー');
                }
            });
        });
    </script>
</body>
</html>

サンプルの動作イメージ

以下にサンプルの動作イメージを示す。JSON 形式を利用したデータのやり取りが確認できる。

ASP.NET MVC4 の Web API を学んでみる その2 〜GET/POST のバインド〜

前回の記事に引き続き、 GET と POST でのバインドについて紹介する。ソースコードベースで紹介させて頂くが、必要な場合は是非サンプルアプリケーションを動作させて確認してほしい。

GET における基本的なバインド方式

簡単な GET のバインドを確認しよう。ソースコード内のコメントに詳細を記載したので、解説は不要だと思う。

  • ValuesController.cs の抜粋
    public class ValuesController : ApiController
    {
        // GET /api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET /api/values/5
        public string Get(int id)
        {
            Debug.WriteLine("☆☆ id = " + id);
            return "value";
        }

        // GET /api/values/?name=normalian&num=10
        public string Get(string name, int num)
        {
            Debug.WriteLine("☆☆ name = " + name);
            Debug.WriteLine("☆☆ num = " + num);
            return "その他";
        }
    (中略)

POST における基本的なバインド方式

  • ValuesController.cs の抜粋
    public class ValuesController : ApiController
    {
        // GET /api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

    (中略)

        // POST /api/values
        public string Post(int hentai, string comment)
        {
            Debug.WriteLine("☆☆ hentai = " + hentai);
            Debug.WriteLine("☆☆ comment = " + comment);
            return "きたよー";
        }

    (中略)
  • Index.cshtml
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>ASP.NET Web API</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script>
    <meta name="viewport" content="width=device-width" />
</head>
<body>
    <header>
        <div class="content-wrapper">
            <div class="float-left">
                <p class="site-title">
                    <a href="/">ASP.NET Web API のサンプル</a></p>
            </div>
        </div>
    </header>
    <div id="body">
        <form id="myform" action="/">
        <input type="button" id="api_button" value="リクエスト送付" />
        <p>
            <input type="radio" name="hentai" value="0" checked />とても変態
            <input type="radio" name="hentai" value="1" />普通に変態
            <input type="radio" name="hentai" value="2" />ちょっと変態
            <input type="radio" name="hentai" value="3" />割と普通
        </p>
        <p>
            <input type="text" value="文字列" name="comment" />
        </p>
        </form>
    </div>
    <script type="text/javascript">
        $('#api_button').on('click', function () {
            var data = $('#myform').serialize();
            $.ajax({
                type: 'POST',
                url: '@Url.Content("~/api/values")',
                data: data,
                success: function (res) {
                    alert(res);
                }
            });
        });
    </script>
</body>
</html>

上記のコードで確認頂けると思うが、jQuery.serialize() メソッドを利用して hentai, comment パラメータを ValuesController に送付している。送付結果は以下となる。

ASP.NET MVC4 の Web API を学んでみる その1 〜基礎編〜

既に RC版 がリリースされている ASP.NET MVC4 の Web API を利用してみたのでメモを記載する。既に日本語でも Web API の紹介記事が書かれているので、まずは以下の記事を参照頂きたい。

今回はプロジェクトテンプレートから生成した Web API アプリケーションの動作概要について確認する。

基本的な使い方について

Global.asax では routes.MapHttpRoute() メソッドを利用し、Web API コントローラのルーティングを設定している。

  • Global.asax(の抜粋)
    public class WebApiApplication : System.Web.HttpApplication
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

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

            //Web API コントローラ用のルーティング
            routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            //従来のコントローラ用のルーティング
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }

従来のコントローラと異なり、Web API 用のコントローラは ApiController を継承している点に注意が必要だ*1

  • ValuesController.cs(の抜粋)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using System.Diagnostics;

namespace Mvc4WebApiApp.Controllers
{
    public class ValuesController : ApiController
    {
        // GET /api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET /api/values/5
        public string Get(int id)
        {
            return "value";
        }
    (中略)
}

テンプレート アプリケーションの起動

Visual Studio から アプリケーションを起動し、ブラウザから http://localhost:<ポート>/api/Values にアクセスすることで ValuesController.Get() メソッドの返り値を取得できる。動作イメージについては以下を参照して欲しい。

*1:従来のコントローラは Controller を継承

IE10 デスクトップ版/Metro UI版 の User Agent を確認してみた

※7/13追記IE10 User Agent String Update に詳細な比較があるのでこちらを参照頂きたい

Release Preview が発表されて暫く経った Windows 8 に同梱される IE10、正式リリースされたわけではないが、利用されている方も多いだろう。ふとWebアプリを作ろうとした際、以下を考えたので IE10 の User Agent を調べてみた。

  • Metro UI はタッチ操作が主体だから jQuery Mobile を使いたい
  • デスクトップ はマウス操作が主体だから jQuery UI を使いたい
  • 上記を判断するには IE10 の User Agent を調べれば良さそう

検証結果

デスクトップ版/Metro UI版 における User-Agent の差異はほぼなかった。IE10 Release Preview の User Agent は以下になる。

OS デスクトップ版での検証結果 Metro UI版での検証結果
Windows 8 32bit版 User-Agent = Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0) User-Agent = Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)
Windows 8 64bit版 User-Agent = Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0) User-Agent = Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)

Windows 8 64bit 版の IE10 では一部 User Agent に差異があるのが、Windows 8 32bit版に関しては、User Agent が完全に一致しており、IE10 がデスクトップ版 と Metro UI 版かを区別できない仕様になっている。
※7/13 追記 IE10 User Agent の挙動は「Metro UI か デスクトップ か」での判断でなく、「HWがタッチデバイス対応をしているか?」や「32bit, 64bit, ARM」等のハードウェア情報により異なるようだ。

検証用のコード

念のため、検証用コードを紹介する。今回は ASP.NET MVC を利用し、以下のコードを Controller に記載することでブラウザ上に User Agent を表示して確認を行った*1

  • HomeController.cs
namespace Mvc4App1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "User-Agent = " + Request.UserAgent;

            return View();
        }
    (中略)
    }

上記のコードを利用し、ブラウザ画面上に User Agent を表示した結果は以下となる。

*1:View側に直接 Razor 記法で書いても良かったかもしれないが…

色々と画期的な「jQuery Mobileスマートフォンアプリ開発」を書評してみる

Community Open Day 2012GoAzure も終わり、大きいイベント×2が無事完遂できてひと段落したつもりになっている割と普通だが、各位は如何お過ごしだろうか。今回はオススメな書籍を発見したので、そちらを紹介したいと思う。

はじめに

昨今、いまだに続くスマートフォン戦国時代を生き抜くエンジニア各位に問いたい。マルチデバイス対応のスマートフォン・アプリ開発に対し、各位はどう対応しているだろうか?
iPhone なら女性受けが良いからとiPhone向けのアプリ開発を行い、Objective-Cに涙した人もいるだろう。出荷台数が多い Android 向けにアプリを作って儲けようとし、機種やOSバージョンごとの差異にツンデレ彼女に振られるがごとく涙した人も居るだろう。Windows Phone 向けにアプリを作ろうとしたらIS12Tが出荷停止で目頭が熱くなった人もいるだろう。。。(白目
そんな我々の悩みを解決する書籍がついに登場した。クロスプラットフォームなアプリ開発で多々実績のある jQuery Mobile の本格的な解説書である「Android/iPhone/Windows Phone対応 jQuery Mobileスマートフォンアプリ開発」の出現である。

Android/iPhone/Windows Phone対応 jQuery Mobileスマートフォンアプリ開発

Android/iPhone/Windows Phone対応 jQuery Mobileスマートフォンアプリ開発

同書を利用することでイカしたスマートフォン向けアプリを作り、たんまりと稼いで左団扇な生活を過ごすことも可能だろう。勝ちまくりモテまくりな左団扇生活に興味のある方は是非一読して頂きたい。

対象読者

さて、そんな同書の主なターゲット層をもうすこし詳細に記載しよう。私の主観となるが、以下の条件どれかに当てはまる方が本書オススメな読者といえるだろう。

  • HTML5 の知識を利用したクロスプラットフォームスマートフォンアプリ開発をしたい方
  • jQuery Mobile を開発した際の TIPS, ハマリどころを学習したい方
  • Webアプリケーション開発に知識があり、これからスマーフォトンアプリを開発したい方
  • 勝ちまくりモテまくりな左団扇生活に興味のある方

逆に「プラットフォーム固有の能力を引き出すため、Objective-C, Java, C#/VB.NET を書きたくて仕方ない」という方は別途他の書籍の通読をオススメする。

解説

本書は大きく「入門編 jQuery Mobile冒険の手引き」、「応用編 jQuery Mobileをレベルアップする便利なアイテム」、「実践編 楽しいサンプルアプリの作り方」の三つに分かれている。以下に各節でのポイントを記載するため、参考にして頂ければ幸いだ。

  • 入門編 jQuery Mobile冒険の手引き
    • jQuery Mobile を利用する際のハマり所を多々記載しており、初学者が一度はハマるであろうポイントがほぼ網羅されていると思われる程に重要な情報が多い。本節の情報は Webアプリ/PhoneGap(後述) に関係なく必要な知識となるため、jQuery Mobile に興味のある方は必読と言えるだろう。ご存じの方も多いと思うが、お楽しみはこれからだ! - ドッグイアと jQuery Mobileで記載される通り、「jQuery Mobileっていうのを jQuery UI みたいにとらえると痛い目見るぜハニー」であるため、是非本節を一読頂きたい。
  • 応用編 jQuery Mobileをレベルアップする便利なアイテム
    • 本節では、jQuery Mobile の機能を追加する部品やライブラリについて解説している。特に、jqPlot をここまで詳しく解説している書籍・サイトは存在しないのではないかという充実ぶりだ。その他にも jQuery Validation や jQuery Mobile 自身のテーマフレームワーク等にも触れるなかなかに濃い節となっている。
  • 実践編 楽しいサンプルアプリの作り方
    • 本節では、PhoneGap と呼ばれるライブラリを用い、Web アプリ向けのライブラリである jQuery Mobile を利用したスマートフォン向けアプリケーション開発方法を紹介している。本節を利用することで、HTML5を利用したネイティブなスマートフォンアプリの実践的な開発方法が学習可能だ。「勝ちまくりモテまくりな左団扇生活」を実現するためには、本節までたどり着く必要がある点にも言及したい。

Metro UI 版IE10 でイントラサイトやループバックアドレスに接続する際の注意点

既にRC版がリリースされた Windows 8 だが、同OSにインストールされているIE10における注意点について今回は紹介したいと思う。

イントラサイトやループバックアドレスに接続する際に発生する問題

発生する問題そのものは極めてシンプルで、イントラサイトやループバックアドレスMetro UI版 IE10 でWebサイトに接続すると以下の様に「このページは表示できません」と表示される点だ。

同様の環境で デスクトップ版の IE10 で接続する場合、以下の様にWebサイトが正しく表示される。

イントラサイトやループバックアドレスに接続する際に発生する問題の原因

以下のサイトで紹介されているが、IE10から Enhanced Protected Mode と呼ばれるセキュリティ強化が実装され、Metro UI ではデフォルトで当該機能がオンになっている。

次のブロブ内における Private Network resources セクションにて、本問題の原因と対応方法が記載されている。プライベートネットワークのリソース(ループバックアドレス、イントラサイト)に接続する場合は信頼済みサイトとして設定する必要があるとのことだ。

イントラサイトやループバックアドレスに接続する際に発生する問題への対応

以下の画像例に従い、デスクトップ版のIE10にて、接続先端末のIPアドレスを信頼済みサイトに追加する。

上記設定後、再度 Metro UI 版の IE10 でイントラサイトに接続した結果、無事に疎通が完了した。

オススメの対応方法は…

今回紹介した手順はローカルポリシーでの設定となっているが、推奨手順は「グループポリシーから信頼済みサイトリストに追加する」の様だ。理由は以下で @kazuk さんが述べて下さっている。