4

アクションメソッドが呼び出される前にアクションメソッドパラメーター(ビューモデル)にアクセスできるASP.NET MVC(MVC4)に便利なフックはありますか(たとえば、アクションメソッドでチェックしたものの値に応じて)つまり、代わりに、ビュー モデル オブジェクト (アクション メソッドのパラメーター) を別のアクション メソッドに転送するか、何らかのビューに直接転送します (つまり、アクション メソッドでさらに処理を行うことなく)。

質問が理解できない場合は、以下のコード例を参照してください。これは、私が探しているコードの種類を示しているはずです... (ただし、そのような種類のインターフェイスが実際に存在し、実装をフックする可能性があるかどうかはわかりません) MVC フレームワーク)

これが実際に可能である場合は、その方法についてのコード例を含む回答を確認したいと思います (たとえば、「メソッド 'ActionFilterAttribute.OnActionExecuting' または 'IModelBinder.BindModel' を使用してみてください」などと誰かが主張する応答だけではありません。なぜなら、私はすでにそれらを試してみましたが、うまくいきませんでした)。また、このスレッドがそれを行う理由についての議論になることを望んでおらず、それを行う方法を見たいと思っていることを尊重ください. (つまり、「あなたは実際に何を達成しようとしていますか?」または「あなたがやりたいことをするよりも良いことがあるかもしれません...」などの回答で議論することに興味はありません)

以下の私自身のコードサンプルが説明しようとしているように、質問は3つのサブ質問/コード例に分割できます:(しかし、実際の既存の型を使用してREALコードに「リファクタリング」したい)一部」は私がでっち上げたもので、該当する実物を探しています…)

(1) 実際のアクション メソッドがビュー モデル オブジェクト パラメータで呼び出される前に、一般的な場所でビュー モデル オブジェクト (アクション メソッド パラメータ) へのアクセスを取得する (および場合によっては変更する) 方法の例。

私が探している種類のコード例は、おそらく以下に似ていますが、使用するインターフェイスの種類と、以下のようなことができるように登録する方法がわかりません。

public class SomeClass: ISomeInterface { // How to register this kind of hook in Application_Start ?
  public void SomeMethodSomewhere(SomeActionMethodContext actionMethodContext, object actionMethodParameterViewModel) {
    string nameOfTheControllerAboutToBeInvoked = actionMethodContext.ControllerName;
    string nameOfTheActionMethodAboutToBeInvoked = actionMethodContext.MethodName;
    // the above strings are not used below but just used for illustrating that the "context object" contains information about the action method to become invoked by the MVC framework
    if(typeof(IMyBaseInterfaceForAllMyViewModels).IsAssignableFrom(actionMethodParameterViewModel.GetType())) {
        IMyBaseInterfaceForAllMyViewModels viewModel = (IMyBaseInterfaceForAllMyViewModels) actionMethodParameterViewModel;
        // check something in the view model:
        if(viewModel.MyFirstGeneralPropertyInAllViewModels == "foo") {
            // modify something in the view model before it will be passed to the target action method
            viewModel.MySecondGeneralPropertyInAllViewModels = "bar";
        }
    }
  }
}

(2) 対象のアクションメソッドを実行させず、代わりに別のアクションメソッドを呼び出す例。この例は、上記の例を次のように拡張したものである可能性があります。

    public void SomeMethodSomewhere(SomeActionMethodContext actionMethodContext, object actionMethodParameterViewModel) {
        ... same as above ...
        if(viewModel.MyFirstGeneralPropertyInAllViewModels == "foo") {
            actionMethodContext.ControllerName = "SomeOtherController";
            actionMethodContext.MethodName = "SomeOtherActionMethod";
            // The above is just one example of how I imagine this kind of thing could be implemented with changing properties, and below is another example of doing it with a method invocation:
            SomeHelper.PreventCurrentlyTargetedActionMethodFromBecomingExecutedAndInsteadExecuteActionMethod("SomeOtherController", "SomeOtherActionMethod", actionMethodParameterViewModel);
            // Note that I do _NOT_ want to trigger a new http request with something like the method "Controller.RedirectToAction"
        }           

(3) 通常のアクション メソッドが実行されないようにし、代わりにビュー モデル オブジェクトをそれ以上処理せずにビューに直接転送する方法の例。

この例は、上記の最初の例を次のように拡張したものです。

    public void SomeMethodSomewhere(SomeActionMethodContext actionMethodContext, object actionMethodParameterViewModel) {
        ... same as the first example above ...
        if(viewModel.MyFirstGeneralPropertyInAllViewModels == "foo") {
            // the below used razor view must of course be implemented with a proper type for the model (e.g. interface 'IMyBaseInterfaceForAllMyViewModels' as used in first example above)
            SomeHelper.PreventCurrentlyTargetedActionMethodFromBecomingExecutedAndInsteadForwardViewModelToView("SomeViewName.cshtml", actionMethodParameterViewModel);
        }
4

2 に答える 2

8

OnActionExecutingアクション フィルターを使用して、イベントをオーバーライドできます。

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        ...            
    }
}

filterContext次に、このメソッドに渡されるこの引数から抽出できる有用な情報を見てみましょう。探しているプロパティは呼び出されActionParameters、 を表しますIDictionary<string, object>。その名前が示すように、このプロパティには、名前と値によってコントローラー アクションに渡されるすべてのパラメーターが含まれます。

したがって、次のコントローラー アクションがあるとします。

[MyActionFilter]
public ActionResult Index(MyViewModel model)
{
    ...
}

モデル バインド後にビュー モデルの値を取得する方法は次のとおりです。

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var model = filterContext.ActionParameters["model"] as MyViewModel;
        // do something with the model
        // You could change some of its properties here
    }
}

それでは、質問の 2 番目の部分を見てみましょう。コントローラーのアクションをショートサーキットして別のアクションにリダイレクトする方法は?

Resultこれは、プロパティに値を割り当てることで実行できます。

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    {
        ... some processing here and you decide to redirect:

        var routeValues = new RouteValueDictionary(new
        {
            controller = "somecontroller",
            action = "someaction"
        });
        filterContext.Result = new RedirectToRouteResult(routeValues);
    }
}

または、たとえば、コントローラー アクションの実行をショートサーキットして、ビューを直接レンダリングすることにします。

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var viewResult = new ViewResult
        {
            ViewName = "~/Views/FooBar/Baz.cshtml",
        };
        MyViewModel someModel = ... get the model you want to pass to the view
        viewResult.ViewData.Model = model;
        filterContext.Result = viewResult;
    }
}

または、JSON の結果をレンダリングすることもできます。

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        MyViewModel someModel = ... get the model you want to pass to the view
        filterContext.Result = new JsonResult
        {
            Data = model,
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
        };
    }
}

ご覧のとおり、できることには無限の可能性があります。

于 2013-01-21T21:38:01.840 に答える
0

ユーザー Darin Dimitrov から提供された回答のコードを試してみましたが、回答の最初と 3 番目の部分は正しいです。

(ただし、このスレッドを見つけて興味を持っている可能性のある他の人にとっては、最初の回答では、「モデル」はモデルに常に使用されるハードコードされたキーワードではないように見えることを明確にすることができますが、選択された名前に対応する必要があるようですアクション メソッド パラメータ. つまり、代わりにメソッド シグネチャがある場合

public ActionResult Index(MyViewModel myViewModel)

次に、アクションフィルターで使用する必要があります

var model = filterContext.ActionParameters["myViewModel"] as MyViewModel;

)

2 番目の回答に関しては、「RedirectToRouteResult」を使用すると、新しい http リクエストがトリガーされます (これは、私の 2 番目のコード例で述べたように望ましくありませんでした)。実際に明示的に呼び出すことで、アクションメソッドを「変更」する別の方法を見つけました。

var controller = new SomeController();
ActionResult result = controller.SomeAction(model);
filterContext.Result = result;

上記のコードは実際には、最初に対象としていたアクション メソッドが呼び出されるのを防いでいるようです。通常、上記のようにコントローラーをハードコーディングすることはおそらく望ましくありませんが、代わりにリフレクションを使用することができます。

string nameOfController = ... 
string nameOfActionMethod = ... 
// both above variables might for example be derived by using some naming convention   and parsing the refering url, depending on what you want to do ...
var theController = this.GetType().Assembly.CreateInstance(nameOfController);
ActionResult result = (ActionResult)theController.CallMethod(nameOfActionMethod, model);
filterContext.Result = result;

(現在のターゲット コントローラーとアクション メソッドの名前を抽出したい場合は、呼び出すコントローラーを決定するロジックを実装するときに、フィルターで次のコードを使用できます。

var routeValueDictionary = filterContext.RouteData.Values;
string nameOfTargetedController = routeValueDictionary["controller"].ToString();
string nameOfTargetedActionMethod = routeValueDictionary["action"].ToString();

)

上記のようにコントローラーをインスタンス化して呼び出すのは少し面倒だと思います。可能であれば、ターゲットコントローラーとアクションメソッドを別の方法で変更したいと思いますか? したがって、残っている問題は、(MVC 4 の最終バージョンでは) サーバーで「内部的に」(「RedirectToAction」のように新しい http 要求が発生することなく) 実行をリダイレクト/転送する方法がまだないかどうかです。

基本的に、ここでは、ASP.NET Web フォーム (および同じものを使用できると信じている古いクラシック ASP) で使用される"Server.Transfer" のようなものを探しているだけだと思います。

独自の「TransferResult」クラスを使用してこの動作を自分で実装している人々について、この問題に関する古い質問/回答を見てきましたが、MVC バージョンが異なると壊れる傾向があるようです。(たとえば、MVC 4 ベータ版については、こちらを参照してください: 301 を返さずに MVC アクションをリダイレクトする方法 (MVC 4 ベータ版を使用) )。

(RedirectToAction のように)新しい http リクエストなしで「内部リダイレクト」を行う方法について、(MVC 4 final で実装された) シンプルな標準ソリューションがまだありませんか?

于 2013-01-22T00:47:47.070 に答える