WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

MVC は maxJsonLength を無視

by WebSurfer 2013年8月7日 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

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar