Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActionResult from non-controller classes

I have decided that my controllers are getting a little to cluttered, and decided to adopt a pipeline-style system as I have used in a WebAPI project. The pipeline consists of actions, that get more and more general, i.e: ViewAccountDetailsAction > AccountAction > AuthenticatedAction > EmptyAction. The Actions both add to the pipleline in order of inheritance, and expose members, or abstract methods for different scenarios.

My problem lies in how to return views from pipeline elements. In the WebAPI example, it was as easy as returning an IHttpActionResult which didn't have to perform any view rendering, however, MVC is required to render its responses differently, with the additional step of Razor.

As Controllers expose internal protected helper methods like View() or RedirectToAction, I cant use these outside the controllers themselves.

Is there an elegant way to render these out? I have seen a few ways to do this, each being either cumbersome, or giving me uncomfortable feelings.

My most favoured way at the moment is to make an internal base class hiding the protected methods, and making them internal, whilst calling the base methods. The controller instance will then be provided to the instantiated action. Is there anything overly wrong with this? I can't think of any abusable cases, but wanted to see if there was any community consensus on the matter.

like image 609
Daniel Park Avatar asked Nov 08 '22 23:11

Daniel Park


1 Answers

I would recommend taking your approach a step further.

This took a little bit of research, but I based it on an approach I did for a client with Web API 2. Basically the idea was we created a custom ControllerSelector, ActionSelector and ActionDescriptors and a controller base class that exposed a strongly typed business layer. Then through reflection/custom attributes, we marshalled the call to the business layer, handling transformations to an HttpResponseResponse message generically, including errors.

  1. Controller: http://pastebin.com/iK8ieBKD
  2. ControllerSelector: http://pastebin.com/qvEbggrP
  3. ActionSelector: http://pastebin.com/CEFNeKZZ

The first thing you'll need to do is look at: http://www.dotnet-tricks.com/Tutorial/mvc/LYHK270114-Detailed-ASP.NET-MVC-Pipeline.html

Unfortunately ASP.NET MVC5's pipeline is much less flexible than Web API 2's. However you can do three things:

  1. Custom Controller Factory: https://msdn.microsoft.com/en-us/library/system.web.mvc.icontrollerfactory(v=vs.118).aspx
  2. Custom ControllerDescriptor
  3. Custom ActionInvoker that interprets the ControllerDescriptor: https://msdn.microsoft.com/en-us/library/system.web.mvc.iactioninvoker(v=vs.118).aspx

This way you leave your controller to do what controllers do best, and create a contract for your controller to generically interpret using your new pipeline. This is really the right way to do it.

High jacking/bastardizing the controller as you suggested I don't think is a great plan, this is a much more robust solution, however it would take significant effort. Best of luck!

like image 164
JasonLind Avatar answered Nov 15 '22 09:11

JasonLind