a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
213 lines
8.4 KiB
C#
213 lines
8.4 KiB
C#
namespace System.Web.Mvc {
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using System.Web;
|
|
using System.Web.Mvc.Async;
|
|
using System.Web.Mvc.Resources;
|
|
using System.Web.Routing;
|
|
using System.Web.SessionState;
|
|
using Microsoft.Web.Infrastructure.DynamicValidationHelper;
|
|
|
|
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState {
|
|
private static readonly object _processRequestTag = new object();
|
|
private ControllerBuilder _controllerBuilder;
|
|
|
|
internal static readonly string MvcVersion = GetMvcVersionString();
|
|
public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
|
|
|
|
public MvcHandler(RequestContext requestContext) {
|
|
if (requestContext == null) {
|
|
throw new ArgumentNullException("requestContext");
|
|
}
|
|
|
|
RequestContext = requestContext;
|
|
}
|
|
|
|
internal ControllerBuilder ControllerBuilder {
|
|
get {
|
|
if (_controllerBuilder == null) {
|
|
_controllerBuilder = ControllerBuilder.Current;
|
|
}
|
|
return _controllerBuilder;
|
|
}
|
|
set {
|
|
_controllerBuilder = value;
|
|
}
|
|
}
|
|
|
|
public static bool DisableMvcResponseHeader {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
protected virtual bool IsReusable {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public RequestContext RequestContext {
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
protected internal virtual void AddVersionHeader(HttpContextBase httpContext) {
|
|
if (!DisableMvcResponseHeader) {
|
|
httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);
|
|
}
|
|
}
|
|
|
|
protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) {
|
|
HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
|
|
return BeginProcessRequest(iHttpContext, callback, state);
|
|
}
|
|
|
|
protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) {
|
|
return SecurityUtil.ProcessInApplicationTrust(() => {
|
|
IController controller;
|
|
IControllerFactory factory;
|
|
ProcessRequestInit(httpContext, out controller, out factory);
|
|
|
|
IAsyncController asyncController = controller as IAsyncController;
|
|
if (asyncController != null) {
|
|
// asynchronous controller
|
|
BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
|
|
try {
|
|
return asyncController.BeginExecute(RequestContext, asyncCallback, asyncState);
|
|
}
|
|
catch {
|
|
factory.ReleaseController(asyncController);
|
|
throw;
|
|
}
|
|
};
|
|
|
|
EndInvokeDelegate endDelegate = delegate(IAsyncResult asyncResult) {
|
|
try {
|
|
asyncController.EndExecute(asyncResult);
|
|
}
|
|
finally {
|
|
factory.ReleaseController(asyncController);
|
|
}
|
|
};
|
|
|
|
SynchronizationContext syncContext = SynchronizationContextUtil.GetSynchronizationContext();
|
|
AsyncCallback newCallback = AsyncUtil.WrapCallbackForSynchronizedExecution(callback, syncContext);
|
|
return AsyncResultWrapper.Begin(newCallback, state, beginDelegate, endDelegate, _processRequestTag);
|
|
}
|
|
else {
|
|
// synchronous controller
|
|
Action action = delegate {
|
|
try {
|
|
controller.Execute(RequestContext);
|
|
}
|
|
finally {
|
|
factory.ReleaseController(controller);
|
|
}
|
|
};
|
|
|
|
return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected internal virtual void EndProcessRequest(IAsyncResult asyncResult) {
|
|
SecurityUtil.ProcessInApplicationTrust(() => {
|
|
AsyncResultWrapper.End(asyncResult, _processRequestTag);
|
|
});
|
|
}
|
|
|
|
private static string GetMvcVersionString() {
|
|
// DevDiv 216459:
|
|
// This code originally used Assembly.GetName(), but that requires FileIOPermission, which isn't granted in
|
|
// medium trust. However, Assembly.FullName *is* accessible in medium trust.
|
|
return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
|
|
}
|
|
|
|
protected virtual void ProcessRequest(HttpContext httpContext) {
|
|
HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
|
|
ProcessRequest(iHttpContext);
|
|
}
|
|
|
|
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
|
|
SecurityUtil.ProcessInApplicationTrust(() => {
|
|
IController controller;
|
|
IControllerFactory factory;
|
|
ProcessRequestInit(httpContext, out controller, out factory);
|
|
|
|
try {
|
|
controller.Execute(RequestContext);
|
|
}
|
|
finally {
|
|
factory.ReleaseController(controller);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
|
|
// If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks
|
|
// at Request.Form) to work correctly without triggering full validation.
|
|
bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current);
|
|
if (isRequestValidationEnabled == true) {
|
|
ValidationUtility.EnableDynamicValidation(HttpContext.Current);
|
|
}
|
|
|
|
AddVersionHeader(httpContext);
|
|
RemoveOptionalRoutingParameters();
|
|
|
|
// Get the controller type
|
|
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
|
|
|
|
// Instantiate the controller and call Execute
|
|
factory = ControllerBuilder.GetControllerFactory();
|
|
controller = factory.CreateController(RequestContext, controllerName);
|
|
if (controller == null) {
|
|
throw new InvalidOperationException(
|
|
String.Format(
|
|
CultureInfo.CurrentCulture,
|
|
MvcResources.ControllerBuilder_FactoryReturnedNull,
|
|
factory.GetType(),
|
|
controllerName));
|
|
}
|
|
}
|
|
|
|
private void RemoveOptionalRoutingParameters() {
|
|
RouteValueDictionary rvd = RequestContext.RouteData.Values;
|
|
|
|
// Get all keys for which the corresponding value is 'Optional'.
|
|
// ToArray() necessary so that we don't manipulate the dictionary while enumerating.
|
|
string[] matchingKeys = (from entry in rvd
|
|
where entry.Value == UrlParameter.Optional
|
|
select entry.Key).ToArray();
|
|
|
|
foreach (string key in matchingKeys) {
|
|
rvd.Remove(key);
|
|
}
|
|
}
|
|
|
|
#region IHttpHandler Members
|
|
bool IHttpHandler.IsReusable {
|
|
get {
|
|
return IsReusable;
|
|
}
|
|
}
|
|
|
|
void IHttpHandler.ProcessRequest(HttpContext httpContext) {
|
|
ProcessRequest(httpContext);
|
|
}
|
|
#endregion
|
|
|
|
#region IHttpAsyncHandler Members
|
|
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {
|
|
return BeginProcessRequest(context, cb, extraData);
|
|
}
|
|
|
|
void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) {
|
|
EndProcessRequest(result);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|