Procedure for calling an Action
IIS receives an http request, enters the w3wp process (if it is webgarden, first find a low-pressure w3wp), finds the applicationpool, and enters the global. asax, enter the route, and find a controller from controllerfactory. If we use the default controller, the action name and parameter will be parsed to call the action (you can also manually process the request in the controller ), return and execute result,
Custom controller factZ restart? Http://www.bkjia.com/kf/ware/vc/ "target =" _ blank "class =" keylink "> placement/WSUNvbnRyb2xsZXK907/ao6mjrM/placement + vdO/2qO6PC9wPgo8cD48YnI + placement =" brush: java; "> public interface IControllerFactory {IController CreateController (RequestContextrequestContext, string controllerName); SessionStateBehavior behavior (RequestContext requestContext, string controllerName); void ReleaseController (IControllercontroller );}
Note:
CreateController: receives request context and controller name (excluding the "Controller" suffix), returns an IController object, used to create a Controller object
GetControllerSessionBehavior: Same as the parameter. A SessionStateBehavior is returned to control the sessionstate object of the created controller.
ReleaseController:
If the controller contains resources that need to be manually released, you can release them in this function.
Sample Code:
public class CustControllerFactory : IControllerFactory { private string_nsFind; private string_assembly; public CustControllerFactory(string namespaceFind, string assemblyName) { if(namespaceFind.Last() == '.') namespaceFind =namespaceFind.Remove(namespaceFind.Length - 1); _nsFind =namespaceFind; _assembly= assemblyName; } public IController CreateController(RequestContext requestContext, string controllerName) { var fullName = _nsFind + "." + controllerName + "Controller"; var type =GetType().Assembly.GetTypes().FirstOrDefault(t => t.FullName == fullName); if (type== null) { return new TestController(); } return (IController)Activator.CreateInstance(_assembly, fullName).Unwrap(); } public SessionStateBehavior GetControllerSessionBehavior(RequestContextrequestContext, string controllerName) { return SessionStateBehavior.Default; } public void ReleaseController(IController controller) { var disposable = controller as IDisposable; if(disposable != null) { disposable.Dispose(); } } }
Code Description: factory receives a namespace name and assembly name. It retrieves the controller object from a specified range and dynamically creates an object through reflection. If the object cannot be found, a default controller is returned.
Next, register the factory in the global file:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); ControllerBuilder.Current.SetControllerFactory(newCustControllerFactory("MVCRouteStudy.Controllers","MVCRouteStudy")); }
As you can see, on the last line, I registered the factory that I manually created.
Test the factory and first access a controller that does not exist:
Access an existing:
Note: This example serves only as a simple implementation without capturing exceptions. In practical applications, scenarios are much more complex. When a custom controller factory creates a controller object, it generally not only creates an instance, but may consider maintaining a cache for performance, manually maintain the session Status, exception Control, and security considerations.
Two objects obtained when a factory is created:
HttpContext |
Current Http request object |
RouteData |
The route information that matches the current request. |
Controller Reset:
The controller of the current request can be overwritten,
requestContext.RouteData.Values["controller"] ="Test";
In this way, the MVC Framework searches for the new View and overwrites the original View name.
Use the built-in controllerfactory
If you do not implement controller factory by yourself, the factory that comes with MVC will be used by default, but pay attention to the following points. To ensure that the controller will be correctly located and instantiated by the factory:
Public must not be an abstract class. It cannot be a generic class. It must end with "Controller" and implement the IController interface. (If it inherits the Controller, it will not be used. It is only applicable to the custom Controller)
Search for namespaces first
Open the global File and add:
ControllerBuilder.Current.DefaultNamespaces.Add("MyControllerNamespace");ControllerBuilder.Current.DefaultNamespaces.Add("MyProject.*");
The purpose of the code is obviously that the namespace added by the MVC framework is preferentially searched when the controller is created. Note that a SearchPattern of MyProject. * is passed at the end, instead of a regular expression. In addition, the added namespaces are not sequentially searched, and their priority is parallel (just like the restricted Route namespace described in the previous chapter ).
Customize: Default ControllerFactory action
Controller Activator
For a factory, the primary responsibility is to create objects. In the MVC Framework, the default factory supports the creation process.
Interface:
public interface IControllerActivator {IController Create(RequestContext requestContext, Type controllerType);}
Parameters:
RequestContext object: context information of the current http request. As described in the previous chapter, this object contains a wealth of information and can be used to perform many tasks (Client ip address, cookie, session, querystring, form, files, Request, Response, etc)
Controller Type: different from the factory created manually. Here we get a Type directly, so you don't have to worry about it or reflect the Type. It is much easier to create an object.
Implementation of the example:
public class CustomControllerActivator :IControllerActivator {public IController Create(RequestContext requestContext,Type controllerType) {if (controllerType == typeof(HomeController)) {controllerType = typeof(TestController);}return (IController)DependencyResolver.Current.GetService(controllerType);}}
Code Description: If you enter Home, rewrite it to Test and call DependencyResolver to create and return to the instance.
Configure the following in the Application_Start method of the global file:
ControllerBuilder.Current.SetControllerFactory(newDefaultControllerFactory(new CustomControllerActivator()));
If you still cannot meet the requirements, inherit defacontrocontrollerfactory and rewrite one or more of the following functions:
public override IController CreateController(RequestContextrequestContext, string controllerName) { return base.CreateController(requestContext, controllerName); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return base.GetControllerInstance(requestContext, controllerType); } protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType) { return base.GetControllerSessionBehavior(requestContext, controllerType); } protected override Type GetControllerType(RequestContext requestContext, string controllerName) { return base.GetControllerType(requestContext, controllerName); } public override void ReleaseController(IController controller) { base.ReleaseController(controller); }
Customize Action Invoker
We know that after MVC finds and creates a controller instance through the controller factory, the next step is to find and call action. This step also supports customize.
Interface:
public interface IActionInvoker {bool InvokeAction(ControllerContext controllerContext,string actionName);}
Example:
public class CustomActionInvoker : IActionInvoker { public bool InvokeAction(ControllerContext controllerContext, string actionName) { if(actionName == "Index") { controllerContext.HttpContext. Response.Write("I'd like over write all the Index ActionBehavior."); return true; } return false; }}
Code Description: rewrite all actions named index.
In the Controller, point to this actioninvoker:
public TestController() { ActionInvoker = new CustomActionInvoker(); }
View results:
It is found that the IndexAction behavior in TestController has been overwritten.
Use the built-in ActionInvoker
The method must be public and does not support the static method. It cannot be the same as the function name in System. Web. Mvc. Controller.
Customize Action name
You can use [ActionName] to specify the Action name:
[ActionName("Enumerate")]public ViewResult List() {return View("Result",new Result {ControllerName ="Customer",ActionName = "List"});}}
Note: Once you change the name, the old name will be overwritten, meaning it will not be found. If you access the service, 404 will be returned.
If a scenario requires the same Action name to appear in the same Controller multiple times, for example, if the actions that support httppost and httpget are to be divided into different actions, you can use the ActionName attribute:
[ActionName("Index2")] [HttpPost] public ActionResult Index1() { return Content("FromPost"); } [ActionName("Index")] [HttpGet] public ActionResult Index2() { return Content("FromGet"); }
Prevents an Action from being accessed.
[NonAction]public ActionResult MyAction() {return View();}
When you access this Action, 404 is returned.
Customize Action Selector
You can customize Selector to determine whether this Action is selected
Abstract class:
[AttributeUsage(AttributeTargets.Method,AllowMultiple = false, Inherited = true)]public abstract class ActionMethodSelectorAttribute : Attribute {public abstract bool IsValidForRequest(ControllerContext controllerContext,MethodInfo methodInfo);}
Note:
The obtained information is ControllerContext and MethodInfo.
ControllerContext contains the http Request Information
Method information that can be retrieved by MethodInfo
Example:
public class LocalAttribute :ActionMethodSelectorAttribute {public override bool IsValidForRequest(ControllerContext controllerContext,MethodInfo methodInfo) {return controllerContext.HttpContext.Request.IsLocal;}}
The purpose of the code is clear, and this Action can only be accessed by local requests.
Usage:
[Local]public ActionResult LocalIndex() {return Content("From Local ");}
UnkownAction Control
If you want to send a non-existent action to the controller in this request, you may want to return a special error page or View. In this case, you can rewrite the HandleUnkownAction function in the Controller:
protected override void HandleUnknownAction(string actionName) { Response.Write(string.Format("You requested the {0} action", actionName)); }
View results:
Controller performance considerations
1. Set the controller session to SessionStateLess.
If you do not need to enable the Session, you can disable the Session Status:
[SessionState(SessionStateBehavior.Disabled)]Public class FastController :Controller {Public ActionResult Index() {return View("Result",new Result {ControllerName = "Fast ",ActionName = "Index"});}}
2. asynchronous Controller
Scenario: interacts with a third-party system, and the client can perform the current activity without waiting for the result. You can use TPL together:
public class RemoteDataController : AsyncController{public async Task Data(){string data = await Task
.Factory.StartNew(() => {return new RemoteService().GetRemoteData();});return View((object)data);}}
RemoteService Simulation Implementation
public class RemoteService {public string GetRemoteData() {Thread.Sleep(2000);return "Hello from the other side of the world";}}