つい先日、
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を、わざわざ参照する必要はないので、
これ以上の調査は今回は見送ります。
5bfc5c0f-b09c-468a-9036-5e6d6e208074|0|.0
タイトル通り、ASP NET MVC のコントローラーをDIします。
MVCとは?という人のために、ASP.NETで至極簡単にコントローラーとビューを作成します。
作成プロジェクト名 MvcApp
C#、MVC3、Razorテンプレート使用、空のアプリケーションとして作成
作成直後は、こんな構成
空のアプリケーションなので、このまま実行しても、表示するものが無いため、
エラーとなります。
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 右クリック、追加、コントローラー を選択
こんな画面が出るので、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メソッドのところで、右クリックしてサブメニューを出すと、
ビューの追加という項目があるので、選択・クリックします。
ビュー名には、メソッド名と同じく Index を指定します。
レイアウトまたはマスターページを使用する、には空白でも良いです。
追加ボタン押下で、Views/Home/Index.cshtml が作成されます。
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
これで、Homeコントローラーと、その表示ビューが作成できました。
実行します。Homeコントローラーがあるので今度はエラーにはなりません。
さて、
コントローラーとビューの関係は、コントローラー名とメソッド名、
それに、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をプロジェクト参照します。
作成したソリューションは、以下のようになります。
実行してみます。
/Aboutへアクセスし、AboutControllerが呼ばれて、About/Index.cshtml が表示されます。
これで、コントローラーを別のプロジェクトに分離することができます。
保守性もグッと良くなります。
次に、動的DIへ向けて
生成されるモジュールは、MVCプロジェクトのモジュール(本体)と、ImplLibrary.dllですが、
ImplLibrary.dll は、/bin 配下にあります。
ASP.NETの恩恵で、/bin のアセンブリは自動的にロードされるのですが、
/bin のモジュール変更に際しては、プロセスがリサイクルされるので、
/bin以外の場所に配置して、リサイクルされないようにします。
これにより、動作中のWEBサイトを無停止でモジュール変更をします。
コントローラーをインスタンス化しているのは、コントローラーファクトリなので、
コントローラーファクトリをカスタマイズし、コントローラーをMEFに対応させ、
MEFによってインスタンス化させます。
次回へ・・・
関根:
a8f71839-09e0-4917-8538-c4e251257fe2|0|.0