I am coding an MVC 5 internet application, and I have a question in regards to handling exceptions globally.
I have my Application_Error setup in my global.asax file. This caters to errors such as 404 HttpExceptions.
How can I send all errors that occur in a controller to the Application_Error function? An example is the following exception:
System.Web.HttpRequestValidationException: A potentially dangerous
Request.Form value was detected from the client (name="").
I have written a OnException(ExceptionContext filterContext) for my controller, but am not sure on how to get the Application_Error function to handle these errors. Do I need to pass the exception from the OnException function, or is this the wrong approach?
Thanks in advance.
You can create a global filter by adding the following class to your App_Start folder:-
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
HandleErrorAttribute can be replaced with your own custom Exception Filter.
All you then need to do is make sure you add the following line of code to the App_Start method of your Gloabal.asax :-
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//AreaRegistration.RegisterAllAreas();
//RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}
}
Hope this helps.
I'm using some kind of http-module which gives me exactly what you are asking for:
public class MyModule : IHttpModule {
public void Init(HttpApplication context) {
context.Error += OnRequestError;
}
private void OnRequestError(object sender, EventArgs e) {
var context = ((HttpApplication)sender).Context;
var error = context.Error;
if (error == null)
return;
var errorType = error.GetType();
if (errorType == typeof(HttpException))
// do something
// this is what you are looking for
if (errorType = typeof(HttpRequestValidationException))
// do something, whatever you want
// works for me, so should work to you too
}
}
To get the module to work, you can use web.config or DynamicModuleHelper:
Install Microsoft.Web.Infrastructure and WebActivatorEx via nuget
Add a Bootstrapper class to your project
Register module at PreApplicationStartMethod
Sample:
// File: Bootstrapper.cs (contains class Bootstrapper)
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using WebActivatorEx;
using WhatEver.It.Is;
[assembly: PreApplicationStartMethod(typeof(Bootstrapper), "Bootstrap")]
namespace WhatEver.It.Is {
public class Bootstrapper {
public static void Bootstrap() {
// Do what do you need just before the application get started
// like registering modules, etc...
DynamicModuleUtility.RegisterModule(typeof(MyModule));
}
}
}
Related
How can I catch all exceptions in dialogs? Is there something like ASP.NET Exception Filter?
I want to send different messages to user depending on exception type.
Thank you
You are right about the fact that you can use ExceptionFilter.
You just have to do the following:
Create your ExceptionFilter class, for example to force the tracking of the exception in Application Insights (or in your case handle specific exception types):
using Microsoft.ApplicationInsights;
using System.Net.Http;
using System.Web.Http.Filters;
namespace BotDemo.App_Start
{
public class ExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext ctx)
{
HandleError(ctx);
}
private static void HandleError(HttpActionExecutedContext ctx)
{
ctx.Response = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError)
{
Content = new StringContent(ctx.Exception.Message)
};
var client = new TelemetryClient();
client.TrackException(ctx.Exception);
}
}
}
Don't forget to define your exception filter in your Application_Start():
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new ExceptionFilter());
...
That's it.
In fact Bot Framework template is using ASP.Net, so you have all the normal features.
I have gotten myself into an interesting situation, and i am confused since i think i am doing all the right stuff here... I am getting the following error:
The current type, Services.Interfaces.IKenticoService, is an interface and cannot be constructed. Are you missing a type mapping?
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The current type, Services.Interfaces.IKenticoService, is an interface and cannot be constructed. Are you missing a type mapping?
Source Error:
Line 113: throw new InvalidOperationException("Container on Global Application Class is Null. Cannot perform BuildUp.");
Line 114:
Line 115: container.BuildUp(this as T);
Line 116: }
Line 117:
Source File: c:\PROJECTS\CMS\CurentSprint\currentsprint\Source\WebProject\App_Code\Global\BasePage.cs Line: 115
and the error is generated by base page:
protected override void OnPreInit(EventArgs e)
{
InjectDependencies();
base.OnPreInit(e);
}
/// <summary>
/// This method is used to inject any controller related dependencies from
/// our existing web page.
/// </summary>
protected virtual void InjectDependencies()
{
HttpContext context = HttpContext.Current;
if (context == null)
return;
IContainerAccessor accessor = context.ApplicationInstance as IContainerAccessor;
if (accessor == null)
return;
IUnityContainer container = accessor.Container;
if (container == null)
throw new InvalidOperationException("Container on Global Application Class is Null. Cannot perform BuildUp.");
container.BuildUp(this as T);
}
I have the mappings in place:
namespace Core.DI
{
public static class UnityHelper
{
public static void ConfigureContainer(IUnityContainer container)
{
container.RegisterType<IPersonRegistrationService, PersonRegistrationService>();
container.RegisterType<ILoginService, LoginService>();
container.RegisterType<IKenticoCMSOfferService, KenticoCMSOfferService>();
container.RegisterType<IKenticoService, KenticoService>();
and then i have some other...
}
}
}
This method is called in side global Application Start method:
public void Application_Start(object sender, EventArgs e)
{
// Azure Application start init
AzureInit.Current.ApplicationStartInit();
CMSAppBase.CMSApplicationStart();
//CustomCode: Added for DI (Unity block)
try
{
//CustomCode: Create the unity container.
Container = new UnityContainer();
UnityHelper.ConfigureContainer(Container);
//mappings
EntityMapper.MapEntities();
}
catch (Exception ex)
{
//TODO: add call to error logger.
}
}
My KenticoService class is setup properly as well:
namespace BusinessLogic.Services
{
public class KenticoService : IKenticoService
{
#region User API Calls
public void HandleCmsUser(Person person, string userName)
{
...
}
public void HandleCmsUser(Person person, string userName, string oldUserName)
{
...
}
#endregion
}
}
Now the kentico service methods are called inside LoginService and PersonRegistrationService only. So in both the classes i have:
[Dependency]
public IKenticoService KenticoServiceInstance { get; set; }
Now we have two sites, our custom MVC solution and a CMS site. The services referenced above are in the projects that are inside our MVC solution. For CMS use, we copy the dlls over to the CMS solution. The MVC solution compiles and runs great. The CMS site is throwing this error and i have double checked that the correct dlls are being referenced here. Are you seeing something here that i may be missing?
I've only recently started to develop in ASP.NET, though I've been using C# for several years now. My first approach to ASP.NET was using MVC 4, but now I find myself working on a project that uses plain ASP.NET pages. I'm at a loss as to how the general framework works.
Specifically, I have no idea how to implement the Decorator Pattern in an ASP.NET page. I have a Product.aspx page and I have to add a feature to it. I thought that Decorator Pattern would be best based on the task requirements, and I immediately figured out how I would use it in MVC, since the actual logic that is executed lies in the Controller Action: there I would instantiate my decorator object.
But I have no idea how to do it in ASP.NET. As far as I can see, when the browser requests Product.aspx "something" creates an object of class Product (derived from Page), and then it's too late to decorate it.
Is it therefore possible to decorate a whole ASP.NET page (not just an object used by the code behind)? How would I do that?
I am not exactly sure what you wanna decorate, but;
You can create an HttpHandler that lets you do your work on a particular request as follows
public class MyHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.RawUrl.Contains("product.aspx"))
{
// may be you can execute your decorate business here
}
}
public bool IsReusable { get { return false; } }
}
Or may be you can use Global.asax 's OnBeginRequest event like as follows
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterOpenAuth();
RouteConfig.RegisterRoutes(RouteTable.Routes);
base.BeginRequest += OnBeginRequest;
}
private void OnBeginRequest(object sender, EventArgs e)
{
if (Request.RawUrl.Contains("product.aspx"))
{
//execute your business here..
}
}
First of all, according to your comment below the question, I can say that you should implement your showcase as a web user control (ascx)
You can decorate your ShowCase as follows,
public partial class ShowCase : System.Web.UI.UserControl, IShowCase
{
protected void Page_Load(object sender, EventArgs e){}
public void ApplyConfiguration(IConfiguration configuration)
{
throw new NotImplementedException();
}
}
public interface IShowCase
{
void ApplyConfiguration(IConfiguration configuration);
}
public abstract class Decorator : IShowCase
{
protected IShowCase ShowCase;
protected Decorator(IShowCase showcase)
{
ShowCase = showcase;
}
public virtual void ApplyConfiguration(IConfiguration configuration)
{
ShowCase.ApplyConfiguration(configuration);
}
}
public class ShowCaseDecoratorA : Decorator
{
public ShowCaseDecoratorA(IShowCase showcase) : base(showcase){ }
public override void ApplyConfiguration(IConfiguration configuration)
{
base.ApplyConfiguration(configuration);
//depending on the configuration, do something..
ShowCase.Visible = false;
}
}
public interface IConfiguration
{
//configuration
}
Then, from inside the page that uses ShowCase user control, you do something like this,
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
IConfiguration configuration = ConfigurationFactory.Get();
new ShowCaseDecoratorA(this.ShowCase).ApplyConfiguration(configuration);
}
}
I hope this gives you some inspiration..
I'm creating an attribute so that whenever an exception occurs on my site, I'll receive an email detailing the exception. I've got so far but my Attribute code doesn't seem to fire if an exception occurs:
public class ReportingAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
// This will generate an email to me
ErrorReporting.GenerateEmail(filterContext.Exception);
}
}
Then above my Controller I'm doing:
[ReportingAttribute]
public class AccountController : Controller
The other way to do it is ofcourse putting ErrorReporting.GenerateEmail(ex) inside my catch blocks? There must be a simpler way? Thats why I thought of creating the Attribute to handle this
For the purpose of logging all uncaught exceptions, you can define the following method in your Global.asax.cs file:
private void Application_Error(object sender, EventArgs e)
{
try
{
//
// Try to be as "defensive" as possible, to ensure gathering of max. amount of info.
//
HttpApplication app = (HttpApplication) sender;
if(null != app.Context)
{
HttpContext context = app.Context;
if(null != context.AllErrors)
{
foreach(Exception ex in context.AllErrors)
{
// Log the **ex** or send it via mail.
}
}
context.ClearError();
context.Server.Transfer("~/YourErrorPage");
}
}
catch
{
HttpContext.Current.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
Attribute just by itself can not define a behaviour, but its used for make some marks over your code data. You should write a code, where you
get an exception
check for given attribute presence in the method that raised an exception
if it is present, collect and send the data you need.
Why not create an base controller:
public ApplicationBaseController : Controller
{
public override void OnException(ExceptionContext context)
{
//Send your e-mail
}
}
And derive your controller from ApplicationBaseController
public HomeController : ApplicationBaseController
{
//.....
}
I have simple HttpApplication class:
public class MvcApplication : HttpApplication
{
public void Application_Start()
{
// register areas
AreaRegistration.RegisterAllAreas();
// register other stuff...
}
}
My unit tests initialise HttpApplication, invoke ApplicationStart and verify application start-up behaviour.
This approach worked well until I had to integrate MVC areas. When AreaRegistration.RegisterAllAreas() is invoked by a unit test, the following exception gets thrown:
System.InvalidOperationException: This method cannot be called during the application's pre-start initialization stage.
Is there a good approach for testing area initialisation logic?
Temporary workaround:
1) In MvcApplication, expose virtual method RegisterAllAreas()
public class MvcApplication : HttpApplication
{
public void Application_Start()
{
// register areas
RegisterAllAreas();
// register other stuff...
}
public virtual void RegisterAllAreas()
{
AreaRegistration.RegisterAllAreas();
}
}
2) In specification, implement a proxy:
[Subject(typeof(MvcApplication))]
public class when_application_starts : mvc_application_spec
{
protected static MvcApplication application;
protected static bool areas_registered;
Establish context = () => application = new MvcApplicationProxy();
Because of = () => application.Application_Start();
It should_register_mvc_areas = () => areas_registered.ShouldBeTrue();
class MvcApplicationProxy : MvcApplication
{
protected override void RegisterAllAreas()
{
areas_registered = true;
}
}
}
3) Test AreaRegistration implementations individually
4) Exclude MvcApplication.RegisterAllAreas() from test coverage
I don't like this approach, but can't think of a better solution right now.
Ideas and comments are welcome…