1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
5 * This software is subject to the Microsoft Public License (Ms-PL).
\r
6 * A copy of the license can be found in the license.htm file included
\r
7 * in this distribution.
\r
9 * You must not remove this notice, or any other, from this software.
\r
11 * ***************************************************************************/
\r
13 namespace System.Web.Mvc {
\r
15 using System.Diagnostics.CodeAnalysis;
\r
16 using System.Globalization;
\r
18 using System.Reflection;
\r
19 using System.Threading;
\r
21 using System.Web.Mvc.Async;
\r
22 using System.Web.Mvc.Resources;
\r
23 using System.Web.Routing;
\r
24 using System.Web.SessionState;
\r
26 [SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields", Justification = "There's nothing secret about the value of this field.")]
\r
27 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState {
\r
28 private static readonly object _processRequestTag = new object();
\r
29 private ControllerBuilder _controllerBuilder;
\r
31 internal static readonly string MvcVersion = GetMvcVersionString();
\r
32 public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
\r
34 public MvcHandler(RequestContext requestContext) {
\r
35 if (requestContext == null) {
\r
36 throw new ArgumentNullException("requestContext");
\r
39 RequestContext = requestContext;
\r
42 internal ControllerBuilder ControllerBuilder {
\r
44 if (_controllerBuilder == null) {
\r
45 _controllerBuilder = ControllerBuilder.Current;
\r
47 return _controllerBuilder;
\r
50 _controllerBuilder = value;
\r
54 public static bool DisableMvcResponseHeader {
\r
59 protected virtual bool IsReusable {
\r
65 public RequestContext RequestContext {
\r
70 protected internal virtual void AddVersionHeader(HttpContextBase httpContext) {
\r
71 if (!DisableMvcResponseHeader) {
\r
72 httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);
\r
76 protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) {
\r
77 HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
\r
78 return BeginProcessRequest(iHttpContext, callback, state);
\r
81 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) {
\r
82 IController controller;
\r
83 IControllerFactory factory;
\r
84 ProcessRequestInit(httpContext, out controller, out factory);
\r
86 IAsyncController asyncController = controller as IAsyncController;
\r
87 if (asyncController != null) {
\r
88 // asynchronous controller
\r
89 BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
\r
91 return asyncController.BeginExecute(RequestContext, asyncCallback, asyncState);
\r
94 factory.ReleaseController(asyncController);
\r
99 EndInvokeDelegate endDelegate = delegate(IAsyncResult asyncResult) {
\r
101 asyncController.EndExecute(asyncResult);
\r
104 factory.ReleaseController(asyncController);
\r
108 SynchronizationContext syncContext = SynchronizationContextUtil.GetSynchronizationContext();
\r
109 AsyncCallback newCallback = AsyncUtil.WrapCallbackForSynchronizedExecution(callback, syncContext);
\r
110 return AsyncResultWrapper.Begin(newCallback, state, beginDelegate, endDelegate, _processRequestTag);
\r
113 // synchronous controller
\r
114 Action action = delegate {
\r
116 controller.Execute(RequestContext);
\r
119 factory.ReleaseController(controller);
\r
123 return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
\r
127 protected internal virtual void EndProcessRequest(IAsyncResult asyncResult) {
\r
128 AsyncResultWrapper.End(asyncResult, _processRequestTag);
\r
131 private static string GetMvcVersionString() {
\r
133 // This code originally used Assembly.GetName(), but that requires FileIOPermission, which isn't granted in
\r
134 // medium trust. However, Assembly.FullName *is* accessible in medium trust.
\r
135 return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
\r
138 protected virtual void ProcessRequest(HttpContext httpContext) {
\r
139 HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
\r
140 ProcessRequest(iHttpContext);
\r
143 protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
\r
144 IController controller;
\r
145 IControllerFactory factory;
\r
146 ProcessRequestInit(httpContext, out controller, out factory);
\r
149 controller.Execute(RequestContext);
\r
152 factory.ReleaseController(controller);
\r
156 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
\r
157 AddVersionHeader(httpContext);
\r
158 RemoveOptionalRoutingParameters();
\r
160 // Get the controller type
\r
161 string controllerName = RequestContext.RouteData.GetRequiredString("controller");
\r
163 // Instantiate the controller and call Execute
\r
164 factory = ControllerBuilder.GetControllerFactory();
\r
165 controller = factory.CreateController(RequestContext, controllerName);
\r
166 if (controller == null) {
\r
167 throw new InvalidOperationException(
\r
169 CultureInfo.CurrentUICulture,
\r
170 MvcResources.ControllerBuilder_FactoryReturnedNull,
\r
176 private void RemoveOptionalRoutingParameters() {
\r
177 RouteValueDictionary rvd = RequestContext.RouteData.Values;
\r
179 // Get all keys for which the corresponding value is 'Optional'.
\r
180 // ToArray() necessary so that we don't manipulate the dictionary while enumerating.
\r
181 string[] matchingKeys = (from entry in rvd
\r
182 where entry.Value == UrlParameter.Optional
\r
183 select entry.Key).ToArray();
\r
185 foreach (string key in matchingKeys) {
\r
190 #region IHttpHandler Members
\r
191 bool IHttpHandler.IsReusable {
\r
197 void IHttpHandler.ProcessRequest(HttpContext httpContext) {
\r
198 ProcessRequest(httpContext);
\r
202 #region IHttpAsyncHandler Members
\r
203 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {
\r
204 return BeginProcessRequest(context, cb, extraData);
\r
207 void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) {
\r
208 EndProcessRequest(result);
\r