diff --git a/Disco.Services/Disco.Services.csproj b/Disco.Services/Disco.Services.csproj index e61a026f..4ef369f7 100644 --- a/Disco.Services/Disco.Services.csproj +++ b/Disco.Services/Disco.Services.csproj @@ -77,8 +77,30 @@ - - + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll + + + True + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll + + + True + ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll + @@ -195,12 +217,15 @@ + + + @@ -228,7 +253,7 @@ - + diff --git a/Disco.Services/Plugins/WebPageHelper.cs b/Disco.Services/Plugins/WebPageHelper.cs index 2ac6ba04..25a22cb8 100644 --- a/Disco.Services/Plugins/WebPageHelper.cs +++ b/Disco.Services/Plugins/WebPageHelper.cs @@ -91,13 +91,11 @@ namespace Disco.Services.Plugins { return PartialCompiled(null); } - private void RenderPartialCompiled(TextWriter Writer, object Model) + private void RenderPartialCompiled(TextWriter Writer, object Model) where ViewType : WebViewPage { if (Writer == null) throw new ArgumentNullException("Writer"); WebViewPage page = Activator.CreateInstance(typeof(ViewType)) as WebViewPage; - if (page == null) - throw new InvalidOperationException("Invalid View Type"); page.ViewContext = ViewPage.ViewContext; page.ViewData = new ViewDataDictionary(Model); page.InitHelpers(); diff --git a/Disco.Services/Web/AuthorizedController.cs b/Disco.Services/Web/AuthorizedController.cs index a04b83f2..2ca2b7e5 100644 --- a/Disco.Services/Web/AuthorizedController.cs +++ b/Disco.Services/Web/AuthorizedController.cs @@ -11,7 +11,7 @@ using System.Web.Mvc; namespace Disco.Services.Web { [DiscoAuthorize] - public abstract class AuthorizedController : Controller + public abstract class AuthorizedController : BaseController { public AuthorizationToken Authorization { diff --git a/Disco.Services/Web/BaseController.cs b/Disco.Services/Web/BaseController.cs new file mode 100644 index 00000000..cd5afef3 --- /dev/null +++ b/Disco.Services/Web/BaseController.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Mvc; + +namespace Disco.Services.Web +{ + [HandleError] + public class BaseController : Controller + { + + } +} diff --git a/Disco.Services/Web/DatabaseController.cs b/Disco.Services/Web/DatabaseController.cs index 9380034d..c6e30da7 100644 --- a/Disco.Services/Web/DatabaseController.cs +++ b/Disco.Services/Web/DatabaseController.cs @@ -9,7 +9,7 @@ using System.Web.Mvc; namespace Disco.Services.Web { [OutputCache(Duration = 0, Location = System.Web.UI.OutputCacheLocation.None)] - public abstract class DatabaseController : Controller + public abstract class DatabaseController : BaseController { protected DiscoDataContext Database; diff --git a/Disco.Services/Web/HandleErrorAttribute.cs b/Disco.Services/Web/HandleErrorAttribute.cs new file mode 100644 index 00000000..4cc3232e --- /dev/null +++ b/Disco.Services/Web/HandleErrorAttribute.cs @@ -0,0 +1,55 @@ +using Disco.Services.Authorization; +using Disco.Services.Users; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; + +namespace Disco.Services.Web +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class HandleErrorAttribute : FilterAttribute, IExceptionFilter + { + private readonly object _typeId = new object(); + private const string ViewArea = null; + private const string ViewMaster = "_Layout"; + private const string ViewPage = "Error"; + + public virtual void OnException(ExceptionContext filterContext) + { + if (filterContext == null) + throw new ArgumentNullException("filterContext"); + + if (!filterContext.IsChildAction && (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled)) + { + Exception ex = filterContext.Exception; + HttpException httpException = new HttpException(null, ex); + int httpExceptionCode = httpException.GetHttpCode(); + + switch (httpExceptionCode) + { + case (int)HttpStatusCode.InternalServerError: // 500 + case (int)HttpStatusCode.Forbidden: // 403 + case (int)HttpStatusCode.NotFound: // 403 + + filterContext.HandleException(); + + break; + } + } + } + + public override object TypeId + { + get + { + return this._typeId; + } + } + } +} diff --git a/Disco.Services/Web/HelperExtensions.cs b/Disco.Services/Web/HelperExtensions.cs new file mode 100644 index 00000000..81795de3 --- /dev/null +++ b/Disco.Services/Web/HelperExtensions.cs @@ -0,0 +1,77 @@ +using Disco.Services.Authorization; +using Disco.Services.Users; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; + +namespace Disco.Services.Web +{ + public static class HelperExtensions + { + public static void HandleException(this ExceptionContext filterContext) + { + var ex = filterContext.Exception; + var contextResponse = filterContext.HttpContext.Response; + + LogException(ex); + + HttpException httpException = new HttpException(null, ex); + int httpExceptionCode = httpException.GetHttpCode(); + + string controllerName = (string)filterContext.RouteData.Values["controller"]; + string actionName = (string)filterContext.RouteData.Values["action"]; + HandleErrorInfo model = new HandleErrorInfo(ex, controllerName, actionName); + ViewResult result = new ViewResult + { + ViewName = "Error", + MasterName = "_Layout", + ViewData = new ViewDataDictionary(model), + TempData = filterContext.Controller.TempData + }; + + filterContext.Result = result; + filterContext.ExceptionHandled = true; + contextResponse.Clear(); + contextResponse.StatusCode = httpExceptionCode; + contextResponse.TrySkipIisCustomErrors = true; + } + public static void HandleException(this HttpApplication httpApplication) + { + var ex = httpApplication.Server.GetLastError(); + + LogException(ex); + } + private static void LogException(Exception Exception) + { + + // Log Exception: + try + { + if (Exception is AccessDeniedException) + { + var accessDeniedException = (AccessDeniedException)Exception; + var resource = accessDeniedException.Resource; + var httpContext = HttpContext.Current; + if (httpContext != null && httpContext.Request != null) + resource = string.Format("{0} [{1}]", resource, httpContext.Request.RawUrl); + + AuthorizationLog.LogAccessDenied(UserService.CurrentUserId ?? "[Anonymous]", resource, accessDeniedException.Message); + } + else + { + Disco.Services.Logging.SystemLog.LogException("Global Application Exception Caught", Exception); + } + } + catch (Exception) + { + // Ignore all logging errors + } + } + + } +} diff --git a/Disco.Services/packages.config b/Disco.Services/packages.config index 75c85e41..71cc84df 100644 --- a/Disco.Services/packages.config +++ b/Disco.Services/packages.config @@ -1,8 +1,11 @@  + + + diff --git a/Disco.Web/Global.asax.cs b/Disco.Web/Global.asax.cs index 69e787f7..0da6da4f 100644 --- a/Disco.Web/Global.asax.cs +++ b/Disco.Web/Global.asax.cs @@ -1,6 +1,7 @@ using Disco.Data.Repository; using Disco.Services.Authorization; using Disco.Services.Users; +using Disco.Services.Web; using System; using System.Configuration; using System.Diagnostics; @@ -214,29 +215,7 @@ namespace Disco.Web #region Global Error Logging void DiscoApplication_Error(object sender, EventArgs e) { - try - { - var ex = Server.GetLastError(); - - if (ex is AccessDeniedException) - { - var accessDeniedException = (AccessDeniedException)ex; - var resource = accessDeniedException.Resource; - var httpContext = HttpContext.Current; - if (httpContext != null && httpContext.Request != null) - resource = string.Format("{0} [{1}]", resource, httpContext.Request.RawUrl); - - AuthorizationLog.LogAccessDenied(UserService.CurrentUserId ?? "[Anonymous]", resource, accessDeniedException.Message); - } - else - { - Disco.Services.Logging.SystemLog.LogException("Global Application Exception Caught", ex); - } - } - catch (Exception) - { - // Ignore all logging errors - } + this.HandleException(); } #endregion } diff --git a/Disco.Web/Views/Shared/Error.cshtml b/Disco.Web/Views/Shared/Error.cshtml index db7c8b83..668734f0 100644 --- a/Disco.Web/Views/Shared/Error.cshtml +++ b/Disco.Web/Views/Shared/Error.cshtml @@ -1,10 +1,38 @@ @model System.Web.Mvc.HandleErrorInfo - @{ - ViewBag.Title = "Error"; -} + ViewBag.Title = "Server Error"; -
-

Error.

-

An error occurred while processing your request.

-
+ var ex = Model.Exception; + + + while (ex != null) + { + +
+

@ex.Message

+ + + + + + + + + +
Type: + @ex.GetType().Name +
Stack: + +
@ex.StackTrace
+
+
+ + ex = ex.InnerException; + } +} + diff --git a/Disco.Web/Views/Shared/Error.generated.cs b/Disco.Web/Views/Shared/Error.generated.cs index 6ce4375d..f94c4c19 100644 --- a/Disco.Web/Views/Shared/Error.generated.cs +++ b/Disco.Web/Views/Shared/Error.generated.cs @@ -2,7 +2,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18051 +// Runtime Version:4.0.30319.34003 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -43,26 +43,93 @@ namespace Disco.Web.Views.Shared public override void Execute() { - #line 3 "..\..\Views\Shared\Error.cshtml" + #line 2 "..\..\Views\Shared\Error.cshtml" - ViewBag.Title = "Error"; + ViewBag.Title = "Server Error"; + + var ex = Model.Exception; + + + while (ex != null) + { + #line default #line hidden -WriteLiteral("\r\n\r\n\r\n \r\n Error.\r\n "); -WriteLiteral(">An error occurred while processing your request.\r\n\r\n"); + + #line 12 "..\..\Views\Shared\Error.cshtml" + Write(ex.Message); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n \r\n Type:\r\n \r\n"); + +WriteLiteral(" "); + + + #line 17 "..\..\Views\Shared\Error.cshtml" + Write(ex.GetType().Name); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n \r\n \r\n Stack:\r\n \r\n \r\n "); + + + #line 24 "..\..\Views\Shared\Error.cshtml" + Write(ex.StackTrace); + + + #line default + #line hidden +WriteLiteral("\r\n \r\n \r\n \r\n \r\n"); + + + #line 29 "..\..\Views\Shared\Error.cshtml" + + ex = ex.InnerException; + } + + + #line default + #line hidden +WriteLiteral(@" + +"); } } diff --git a/Disco.Web/Web.config b/Disco.Web/Web.config index dd9ae82f..87e1b506 100644 --- a/Disco.Web/Web.config +++ b/Disco.Web/Web.config @@ -24,7 +24,7 @@ - +