WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

VS2010 で ASP.NET MVC 4

by WebSurfer 14. July 2014 14:30

何を今さらと言われるるかもしれませんが、VS2010 でも ASP.NET MVC 4 を使用しての開発ができるということを知って、自分の開発マシンにもインストールしてみました。

VS2010 で ASP.NET MVC 4

実は、MVC4 は VS2012 以上でなければサポートされてないと誤解しており、VS2012 はシステム要件が Windows 7 以上ということで(自分の開発マシンの OS は Vista)諦めてました。(汗)

Visual Studio 2012 / ASP.NET MVC 4 正式リリース というブログに書いてあった Microsoft のダウンロードサイト Visual Studio 2010 SP1 および Visual Web Developer 2010 SP1 用 ASP.NET MVC 4 から AspNetMVC4Setup.exe と AspNetMVC4Setup_JPN.exe を入手してインストールしました。

インストールした結果、上の画像のように VS2010 でも MVC4 のテンプレートが利用できるようになりました。

この画面で[OK]ボタンをクリックすると下の画像のダイアログが現れ、MVC4 用に用意された (1) 空、(2) 基本、(3) インターネットアプリケーション、(4) イントラネットアプリケーション、(5) モバイルアプリケーション、(6) Web API という 6 種類のテンプレートから選べるようになっています。

MVC4 用テンプレート

VS2012 に実装されているテンプレートとは少し違いがあって、VS2010 には MVC4 の新機能の一つである Single Page Application 用のテンプレートがありません。ベータ版には実装されていたが、開発が間に合わず RC 版で削除されたそうです。

全ての機能はとてもまだ使いこなせてません。なので、Web や本の記事の受け売りですが、以下に主な新機能の概要を書いておきます。

  1. モバイルアプリケーション・テンプレート
    基本的な構造は、「インターネットアプリケーション」テンプレートで提供されるフォーム認証を使用するアカウントコントローラーを含むアプリケーションと同じだそうです。ただし、View のデザインに jQuery Mobile を使用することで、モバイル端末でのタッチ操作を行いやすい Web アプリケーションを開発できるようになっているそうです。
  2. Display Modes によるビューの切り替え
    デスクトップ PC ブラウザ向けの View と、モバイル端末ブラウザ向けの View をそれぞれ用意しておくことにより、ASP.NET MVC4 フレームワークが自動的にブラウザ種類を判断して View の切り替えを行う機能です。モバイル端末の種類ごとに View を用意し(例えば、iPhone 用とAndroid 用)切り替えることも可能とのことです。
  3. jQuery Mobile, View Switcher と Browser Overriding
    モバイル向け Web アプリケーション作成には、上に述べた「モバイルアプリケーション」テンプレートを利用するアプローチの他に、「インターネットアプリケーション」テンプレートをベースとした Web アプリケーションにモバイル端末向け View を追加するアプローチがあります。
    それには NuGet パッケージとして提供されている jQuery.Mobile.MVC を VS2010 の[ツール(T)]メニューからプロジェクトに追加します。それにより jQuery Mobile と Display Modes をベースとした View Switcher 機能が利用できるようになります。
    Browser Overriding は、ブラウザ上に表示されるリンクの操作で、デスクトップ用の表示とモバイル端末用の表示を切り替えるのに用いる MVC4 の新しい機能だそうです。
  4. ASP.NET Web API
    ブラウザやモバイルデバイスを含むさまざまなクライアントに対して RESTful なサービスを提供するのに適した HTTP サービス構築用のフレームワークです。
    このテンプレートを利用して、RESTful な CRUD (Create, Read, Update, Delete)処理に必要な HTTP サービスのため、POST, GET, PUT, DELETE それぞれの HTTP メソッドに対応したアクションメソッドの雛形を作成できます。 概要は MSDN Blog の記事 ASP.NET Web API を使ってみよう: MVC 4 新機能シリーズ が参考になると思います。
  5. ASP.NET Single Page Application
    JavaScript と Web API をベースとし、AJAX と HTML 5 を使用して、定期的にページを再読み込みしないで、ユーザーのアプリケーション操作に合わせてページを動的に更新し、滑らかで応答性の高い Web アプリケーションを作成するためのテンプレートだそうです。
    概要は MSDN マガジンの記事 単一ページ アプリケーション: ASP.NET を使用して最新の応答性の高い Web アプリケーションを構築する が参考になると思います。
    ベータ版の時点からサポートが外された JavaScript ライブラリもあるそうなので、VS2010 にこの機能を追加する場合はその点に注意する必要がありそうです。(VS2012 を使用したほうがよさそうです)

上記以外の MVC4 の新機能としては、Razor 構文(ASP.NET Web Pages 2)の新機能、JavaScript/CSS ファイルの縮小化と結合処置の自動化機能、Visual Studio コード生成のためのレシピ機能、非同期コントローラーの Task サポート、NuGet パッケージングマネージャーによるコンポーネント管理があります。

・・・が、とてもついていけないので説明は割愛します。もっと勉強しないと世の中の技術の進歩に付いていけないようです。(汗)

Tags:

MVC

MVC は maxJsonLength を無視

by WebSurfer 7. August 2013 13:32

Ajax を使用する際、クライアントとサーバー間でやり取りするデータには JSON (JavaScript Object Notation) 形式の UTF-8 文字列を使うのがデファクトスタンダードになっています。

ASP.NET Web Forms アプリケーションで Ajax を使って Web サービスと通信を行う場合も、先の記事 ASP.NET AJAX と Web サービスjQuery AJAX と Web サービス でも書きましたように、データのやり取りには JSON 形式の文字列を使っています。

クライアントからサーバーへ送信できる JSON 文字列の長さは、セキュリティ対策のため、デフォルトで 102,400 文字に制限されています。この値は、web.config の jsonSerialization 要素 (ASP.NET 設定スキーマ) の設定によって変更できます。以下のような感じです。

<system.web.extensions>
    <scripting>
        <webServices>
            <jsonSerialization maxJsonLength="3000000"/>
        </webServices>
    </scripting>
</system.web.extensions>

上の maxJsonLength 要素の設定により、JSON 文字列のデシリアライズに使用されている(と思われる)JavaScriptSerializer クラスの MaxJsonLength プロパティの設定が書き換えられるようで、デフォルト値の 2,097,152 文字を超えて JSON 文字列を送信することも可能です。(どこまで可能かは��検証ですが、2,097,152 文字を超えて可能なのは ASP.NET 4 Web Forms および MVC3 で確認しました)

ところが、呼び出す相手が Web サービスのメソッドでなく、MVC のアクションメソッドの場合、web.config の maxJsonLength 要素の設定は無視され、文字数制限は JavaScriptSerializerクラスの MaxJsonLength プロパティのデフォルト値 2,097,152 文字となります。

このあたりは、いろいろググって調べている時に、stackoverflow の記事 Can I set an unlimited length for maxJsonLength in web.config? を見つけて分かりました。そのページの Answers の 2 つ目に書いてあります。

いろいろ調べましたが、クライアントからサーバーに送信される JSON 文字列の長さ制限を変更する方法は見つかりませんでした。JavaScriptSerializer クラスの MaxJsonLength プロパティのデフォルト値 2,097,152 文字で固定となってしまいます。

ただし、逆方向のサーバーからクライアントに送信される JSON 文字列の長さ制限(MaxJsonLength プロパティのデフォルト値 2,097,152 文字)を緩和するための workaround はあります。詳しくは、上に紹介した stackoverflow のページに書いてありますので見てください。

クライアントからサーバーに送信する JSON データの文字数の制限を変更する方法はないか調べているときにわかったこと(何故変更できないか)を以下に書いておきます。

MVC のアクションメソッドを呼び出す場合、2,097,152 文字を超えてクライアントからサーバーにJSON データを送信すると「ArgumentException: JSON JavaScriptSerializer を使用したシリアル化または逆シリアル化中にエラーが発生しました。文字列の長さが maxJsonLength プロパティで設定されている値を超えています。」というエラーとなります。

スタックトレースを見ると JavaScriptSerializer クラスの DeserializeObject メソッドで JSON 文字列を受け取って、それデシリアライズするときに例外(ArgumentException・・・その条件の一つが「input の長さが MaxJsonLength の値を超えています。」)が発生したことが分かります。

さらに、スタックトレースから、そのメソッドは JsonValueProviderFactory クラスの GetDeserializedObject メソッドで使われているのが分かります。CodePlex のサイトで、その ソースコード を調べたら以下のようになっていました。

private static object GetDeserializedObject(
                    ControllerContext controllerContext)
{
  if (!controllerContext.HttpContext.Request.
          ContentType.StartsWith("application/json", 
                       StringComparison.OrdinalIgnoreCase))
  {
    // not JSON request
    return null;
  }

  StreamReader reader = new StreamReader(
        controllerContext.HttpContext.Request.InputStream);
  string bodyText = reader.ReadToEnd();
  if (String.IsNullOrEmpty(bodyText))
  {
    // no JSON data
    return null;
  }

  JavaScriptSerializer serializer = new JavaScriptSerializer();
  object jsonData = serializer.DeserializeObject(bodyText);
  return jsonData;
} 

上のコードの通り、GetDeserializedObject メソッドで使われている JavaScriptSerializer オブジェクトの MaxJsonLength プロパティの設定を変更する手段はないので、MVC のアクションメソッドを呼び出している限りは制限を 2,097,152 文字を超えて設定する手段はなさそうです。

どうしても 2,097,152 文字を超えて設定する必要があれば(実際、そんな必要があるのか分かりませんが)、アクションメソッドではなく、web サービス(.asmx)を作ってそのメソッドを呼び出すようにして、web.config の maxRequestLength 要素を 2,097,152 文字を超えた値に設定することで対応が可能です。

aspnet:MaxJsonDeserializerMembers について

上記の JSON 文字数の制限に加えて、2011 年 12 月 30 日に公開されたセキュリティ更新プログラム MS11-100 で HTTP 要求内の JSON メンバーの最大数がデフォルトで 1,000 に制限されるようになりました。

この 1,000 という数は、クライアントからサーバーに送信される JSON オブジェクト内の key:value アイテムの総数です。

デフォルト値 1,000 の変更は、web.config の ASP.NET appSettings 要素で aspnet:MaxJsonDeserializerMembers キーを設定することで可能です。

この制限を越えると JsonValueProviderFactory の EntryLimitedDictionary.Add メソッドで InvalidOperationException がスローされます。

ちなみにエラーメッセージは「InvalidOperationException: JSON リクエストが大きすぎるため、逆シリアル化できませんでした。」となります。

なお、web サービス(.asmx)のメソッドにも aspnet:MaxJsonDeserializerMembers による制限が有効かどうかは未確認です。

Tags: , ,

MVC

EditorFor と DisplayFor の違い

by WebSurfer 27. March 2013 21:38

Html Helper の EditorFor と DisplayFor が、POST 要求に対する応答で、異なった値を表示することがあります。その理由と解決策を備忘録として書いておきます。

EditFor と DisplayFor の違い

上の画像を表示した Model, View, Controller のコードは以下の通りです(説明に関係ない部分は省略してあります)。

Model

public class FileModel
{
  public string FileName { get; set; }
}

View

@model MvcApplication1.Models.FileModel

@using (Html.BeginForm())
{
  @Html.EditorFor(m => m.FileName)
  <input type="submit" value="POST" />
  @Html.DisplayFor(m => m.FileName)
}

Controller

public class FileController : Controller
{
  [HttpPost]
  public ActionResult Index(FileModel m)
  {
    m.FileName += ".ext";
    return View(m);
  }
}

ユーザーがテキストボックス(EditorFor 相当)にファイル名を入力し、form を submit(POST 要求)すると、Controller で model の FileName プロパティに拡張子 ".ext" を追加します。

この場合、応答画面では、DisplayFor には拡張子 ".ext" が追加されて表示されるものの、EditorFor には ".ext" は追加されません。 上の画像の例を見てください。

つまり、EditorFor には POST された値がそのまま使われています。

何故そうなるかと言うと、例えば、EditorFor の入力にユーザーが間違った値を入力した場合、サーバーに POST された時に検証 NG とし、エラーメッセージ(例:入力が間違っています)を表示するとともに、EditorFor にはユーザー入力をそのまま表示したいという理由だそうです。

具体的には、POST された値は ModelState ディクショナリ(model ではない)に格納されていて、Html Helper はまず ModelState ディクショナリを調べて、そこに値があればそれを表示するようになっています。

詳しくは以下のページを見てください。

ASP.NET MVC’s Html Helpers Render the Wrong Value!

value が POST されるか否かが問題になります。例えば、EditorFor に限らず、HiddenFor もその value は POST されるので、結果は EditorFor と同じになります。

一方、DisplayFor には、POST される value などというものはないので、model の値が使われます。

EditorFor にも DisplayFor と同様に model の値(拡張子 .ext が追加されたもの)を表示するには、ModelState ディクショナリを Clear することです。

そうすれば model から値を取ってくるので、期待した結果になります。具体的には以下のコードを追加します。

if (ModelState.IsValid)
{
  ModelState.Clear();
}

ただしこのようにして、検証結果 OK で POST 要求への応答をそのまま返すのは、二重 POST の問題が起こりうるので、好ましくないようです。

二重 POST 問題

MVC に限らず、Web アプリケーション開発の基本として、Post/Redirect/Get (PRG) パターンを使う・・・即ち、POST 要求への応答をそのまま返すのは検証結果 NG の場合のみとし、検証結果 OK の場合は、例え同じページを表示するにしても、リダイレクトしてブラウザに GET 要求させるのがよいそうです。(詳しくはリンク先を見てください)

Post/Redirect/Get による問題解決

Post/Redirect/Get パターンを使うように、上記のコードを書き直すと、以下のようになると思います。

public class FileController : Controller
{
  [HttpGet]
  public ActionResult Index()
  {
    FileModel model;

    object obj = TempData["ValidationResult"];
    if (obj is FileModel)
    {
      // 検証 OK ⇒ 再確認
      model = (FileModel)obj;
    }
    else
    {
      // 初期画面
      model = new FileModel();
    }
    return View(model);
  }

  [HttpPost]
  public ActionResult Index(FileModel m)
  {
    if (ModelState.IsValid)
    {
      // 検証 OK ⇒ 再確認
      m.FileName =
        ModelState["FileName"].Value.AttemptedValue
        + ".ext";
      TempData["ValidationResult"] = m;
      return RedirectToAction("Index");
    }
    else
    {
      // 検証失敗
      return View(m);
    }
  }
}

Tags:

MVC

About this blog

2010年5月にこのブログを立ち上げました。その後 ブロ��2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  April 2021  >>
MoTuWeThFrSaSu
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

View posts in large calendar