Windows Azure Tips from Prospex

システム開発 × インフラ構築・運用 × グラフィックデザイン = プロスペックス

フォーム認証のURLを設定したのに /Account/LogOn へリダイレクトされる

clock July 20, 2011 22:29 by author Sekine

つい先日、

MVC 3 のWebアプリケーションを Azure 上へ配置した時にハマった出来事。

普通にフォーム認証を定義し、コントローラ側のAuthorizeAttributeで承認可否をする。


Web.configは以下の様に、

<configuration>

  <system.web>

    <authentication mode="Forms">
      <forms loginUrl="~/Login"
             timeout="30"
             path="/"
             slidingExpiration="true"/>
    </authentication>


コントローラ側は、クラス自体に”Admin”ロールのみ許可する。

[Authorize(Roles = "Admin")]
public class SecurytyController : Controller
{
    public ActionResult Index()
    {
        // 何かの処理

        return View();
    }
}

まぁ、こんな感じで特に変わった事は無いのだが


loginページは、FormsAuthentication などの

ASP.NET認証チケットの発行を行い、正常的な認証処理を行う。



開発時には、正常に動作し、

いざ Azure へ上げるために、ライブラリ参照など見直し、

パッケージ化してデプロイし、動作させたところ・・・


非認証時のリダイレクトが、設定したログインページでは無く、

/Account/LogOn へリダイレクトされてしまうようになった。

/Account/LogOn ページは、MVCアプリケーションでの

デフォルトテンプレートの loginUrl 定義なので、

Web.config があやしく思い、良く見直して再度デプロイ


それでも・・やはり、/Account/LogOn へ飛ばされてしまう。

/Account/LogOn を受けるコントローラは無いので当然404エラーとなる。


そこで、ローカルの環境で実行確認をしたところ、

作成中の時は /Login ページが表示されていたのが、

ローカル実行でも、/Account/LogOn へ飛ばされるようになってしまった。



少し考え、しばらく途方に暮れた。


原因は、 「~ライブラリ参照など見直し~」 の所にあった!。


Azure WebRole は、MVC3のランタイムが無いため、

各DLLを /bin へ直接配置するために参照設定を

ローカルコピー True にする。


各DLLは、

C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 3\Assemblies の
System.Web.Mvc.dll

他、
C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies 配下のすべてのDLLファイルという事なのだが・・

「すべて」は誤りで、
・WebMatrix.Data.dll
・WebMatrix.WebData.dll
は、不要!!!。

WebMatrixのアプリケーションじゃ無ければ含める必要は無い。

WebMatrix.Data.dll が読みこまれると、

<authentication>
    <forms loginUrl=

の値は、/Account/LogOn に上書きされてしまい、

ログインページが意図したものでは無くなります。

WebMatrix.Data.dll
WebMatrix.WebData.dll

を参照から削除し、再度デプロイしたら、

ちゃんと、/Login ページへリダイレクトされるように直りました。



ちなみに、WebMatrix.Data.dll でも、appSettingsを定義することで、

任意URLへ変更することができます。

<configuration>
  <appSettings>
    <add key="loginUrl" value="~/Login"/>
  </appSettings>

一応これで、ログインページを変更することは出来たのだが、

コントローラ側の、Authorize属性を付けた、HttpPostのアクションを呼ぶと、

500エラーとなり、うまく動かなかった。

何か原因はあるかと思うのだが、

WebMatrix.**の2つのDLLを、わざわざ参照する必要はないので、

これ以上の調査は今回は見送ります。



MVC コントローラーをDIする

clock June 20, 2011 21:49 by author Sekine

タイトル通り、ASP NET MVC のコントローラーをDIします。

MVCとは?という人のために、ASP.NETで至極簡単にコントローラーとビューを作成します。

作成プロジェクト名 MvcApp
C#、MVC3、Razorテンプレート使用、空のアプリケーションとして作成

作成直後は、こんな構成
image

空のアプリケーションなので、このまま実行しても、表示するものが無いため、

エラーとなります。


Global.asax(.cs) に注目

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

    routes.MapRoute(
        "Default", // ルート名
        "{controller}/{action}/{id}", // パラメーター付きの URL
        new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
    );
}

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

Application_Start内から、RegisterRoutesメソッドを呼び、

ルーティングマップの登録を最初に行っています。

MVCでは、マップに従い、コントローラーが呼ばれます。

routes.MapRouteで、処理するコントローラーを登録しています。

初期定義では、URL指定が無い場合に、Homeコントローラーが呼ばれると思ってください。

※マップ定義を変更すれば、処理コントローラーを変更できます。
詳しくは説明しませんが、様々なルール定義をすることで、念密に振り分けることが出来ます。


初期のままの実行では、Homeコントローラーが無いため、エラーとなるので、

Homeコントローラーを作成します。

ソリューションエクスプローラ上で、

Controllers 右クリック、追加、コントローラー を選択

image

こんな画面が出るので、HomeController とします。

HomeController.csが作成されます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApp.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }

    }
}


次に、ビューの作成

csコード内の、Indexメソッドのところで、右クリックしてサブメニューを出すと、

ビューの追加という項目があるので、選択・クリックします。

image

ビュー名には、メソッド名と同じく Index を指定します。

レイアウトまたはマスターページを使用する、には空白でも良いです。

追加ボタン押下で、Views/Home/Index.cshtml が作成されます。

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>

これで、Homeコントローラーと、その表示ビューが作成できました。

image

実行します。Homeコントローラーがあるので今度はエラーにはなりません。

image


さて、

コントローラーとビューの関係は、コントローラー名とメソッド名、

それに、Viewsフォルダ配下のフォルダ名とファイル名とに関連付けされます。

コントローラーの戻り値にView(); として返してましたが、

引数を与えることで、別のビューを返す(=別の画面を表示させる)こともできます。


MVCの動作論理を明確に説明するには、このブログ記事では難しいので、

これ以上は割愛します。別の機会があれば書きたいと思いますが。



では、本題のコントローラーのDIについて。

MVCコントローラーは、Controllerクラス (System.Web.Mvc名前空間)を継承する必要があります。

Controllerクラスを継承さえしていれば、何処に配置しても良いのです。

※プロジェクト内のControllersフォルダに配置する必要もありません。


厳密には、IControllerインターフェースを継承すれば、コントローラーとして振る舞うことが可能ですが、

MVC内部コードでは、Controllerクラスの継承元のControllerBaseクラスの依存が、

非常に大きく、Controllerを継承するのが無難です。



コントローラーは、URLリクエストの解析(マップ定義も含め)を経て、

処理するコントローラークラスが決定されます。

この決定処理は、コントローラーファクトリが行います。

コントローラーが選ばれる条件としては、

IControllerインターフェースを継承し、クラス名がControllerで終わっていること。


即ち、コントローラーのDIは、既にコントローラーファクトリから、DIされているのです。



それでは、具体的に、

ソリューションに、クラスライブラリプロジェクトを追加します。

プロジェクト名 ImplLibrary
C# クラスライブラリ

参照設定として、

System.Web
System.Web.Routing
System.Web.Mvc

を加えます。

プロジェクトに、Controllerクラスを継承する、AboutControllerというクラスを作成します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Web.Mvc;

namespace ImplLibrary
{
    public class AboutController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

次に、MVCプロジェクト側に、Aboutを表示するビューを追加します。

Indexメソッド付近を右クリックしても、MVCプロジェクト内ではないので、

前のようにビューの追加メニューは出てきません。

そのため、Viewsフォルダに直接フォルダとビューファイルを作成します。

Views右クリック、追加、新しいフォルダー: About フォルダーを作る。

作成した、Aboutフォルダーを右クリック、追加、新しい項目で、

MVC 3 ビューページ (Razor) を選択し、ファイル名を、Index.cshtml とします。


Index.cshtml 作成後は、内容・構造を、Home/Index.cshtml と同じにし、

文言を少し変更しましょう。

-- About/Index.cshtmlの内容 --

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>

ここは、Aboutです。


MVCアプリケーションでは、ImplLibraryをプロジェクト参照します。

作成したソリューションは、以下のようになります。
image

実行してみます。
image

/Aboutへアクセスし、AboutControllerが呼ばれて、About/Index.cshtml が表示されます。

これで、コントローラーを別のプロジェクトに分離することができます。

保守性もグッと良くなります。



次に、動的DIへ向けて

生成されるモジュールは、MVCプロジェクトのモジュール(本体)と、ImplLibrary.dllですが、

ImplLibrary.dll は、/bin 配下にあります。

ASP.NETの恩恵で、/bin のアセンブリは自動的にロードされるのですが、

/bin のモジュール変更に際しては、プロセスがリサイクルされるので、

/bin以外の場所に配置して、リサイクルされないようにします。

これにより、動作中のWEBサイトを無停止でモジュール変更をします。


コントローラーをインスタンス化しているのは、コントローラーファクトリなので、

コントローラーファクトリをカスタマイズし、コントローラーをMEFに対応させ、

MEFによってインスタンス化させます。


次回へ・・・



関根:



Sign In