Is there any way to set a custom error page in IIS 7 without creating a web.config?
Unfortunately researching this particular topic has been very difficult because there are SO many articles on how to do it with a web.config. What I'm looking for is either buried beneath the 8 million results I don't want or it's not possible.
Yes, there is. It involves either subscribing to the Application_Error event in Global.asax or by writing a custom ErrorHandlerAttribute.
Darin already gave the correct answer, but I want to go into a little more depth.
In any ASP.NET application, given it is Web Forms, MVC, or raw ASP.NET, you can always use Application_Error Global.asax. If your ASP.NET application does not have a Global.asax, all you need to do is right-click your project in Solution Explorer, Add New Item, and choose Global Application Class. You should only have this option available if you don't already have one.
In your Global.asax, if you don't already see it, you can add Application_Error as shown below:
protected void Application_Error(object sender, EventArgs e) {
}
This will be called automatically by ASP.NET whenever there is an error. But as stated here, this is not perfect. Specifically:
An error handler that is defined in the Global.asax file will only
catch errors that occur during processing of requests by the ASP.NET
runtime. For example, it will catch the error if a user requests an
.aspx file that does not occur in your application. However, it does
not catch the error if a user requests a nonexistent .htm file. For
non-ASP.NET errors, you can create a custom handler in Internet
Information Services (IIS). The custom handler will also not be called
for server-level errors.
In Application_Error you can process the uncaught exception with Server.GetLastError(). This will provide you the Exception that was thrown, or null. I am not sure why this handler would be called if an exception didn't occur, but I believe that it is possible.
To redirect the user, use Response.Redirect(). Whatever you pass for the url is going to be sent directly to the browser without any further processing, so you can't use application-relative paths. To do that I would use this method in combination with VirtualPathUtility.ToAbsolute(). For example:
Response.Redirect( VirtualPathUtility.ToAbsolute( "~/Error.aspx" ) );
This redirect will be a 302 (temporary redirect) rather than a 301 (permanent), which is what you want in the case of handling errors. It's worth noting that this overload of Response.Redirect is the same as calling the overload Response.Redirect(url, endResponse: true). This method works by throwing an exception, which is not ideal in terms of performance. Instead, call Response.Redirect(url, false) immediately followed by Response.CompleteRequest().
If you're using ASP.NET MVC, [HandleError] is also an option. Place this attribute on your Controller or on an Action within a controller. When this attribute is present, MVC will display the Error view, found in the ~/Views/Shared folder.
But you can make this even easier for yourself. You can automatically add this attribute to call Controllers in your project by creating a FilterConfig class in your project. Example:
public class FilterConfig {
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new HandleErrorAttribute());
}
}
And then add FilterConfig.RegisterGlobalFilters( GlobalFilters.Filters ); to your Application_Start() in Global.asax.
You can read more about the HandleErrorAttribute at https://msdn.microsoft.com/en-us/library/system.web.mvc.handleerrorattribute(v=vs.118).aspx.
But as stated above, both of these methods will never cover absolutely all errors that can occur during the processing of your application. It's not possible to provide the best user experience for all possible errors without using Web.config or configuring IIS manually.
Related
We are creating webevents in a DB other than Kentico. These webevents are then used for enterprise reporting. I need to implement the same inside Kentico project.
Is there an event that can fire after the page has loaded so that i can create my web event with page name and user information if logged in.
I have also seen in the past that with events, the Request and Session objects are not available. However, HTTPContext.Current is available. I need the Request and Session objects.
We are using Kentico version 7.0.92 and have a portal template site.
Now, i don't want to use portal template page to create events since this code executes multiple times with each request for a page.
Basically, i am interested in the PageName, Session and Request objects.
I have looked around Kentico 7 documentation. Looks like we have CMSRequestEvents but haven't been able to find sample code.
Update:
Looks like the missing piece is CMSContext class. Now just trying to find the right event for CMSRequestEvents, where i have the Session object available.
I'd suggest modifying Kentico\CMS\Global.asax.cs in the following way:
public override void Init()
{
base.Init();
CMSRequestEvents.AcquireRequestState.After += AcquireRequestState_After;
}
void AcquireRequestState_After(object sender, EventArgs e)
{
// Do your stuff...
}
By that time the HttpContext.Current.Session should already be initialized. Page name can be retrieved from the HttpContext.Current.Request which should never be null.
I'm trying to setup MiniProfiler on my web api site, and having a hard time getting MiniProfiler.Current to work.
I followed the directions at miniprofiler.com, and have the following in global.asax:
protected void Application_Start()
{
MiniProfilerEF6.Initialize();
// other setup
}
protected void Application_BeginRequest() {
// need to start one here in order to render out the UI
MiniProfiler.Start();
}
protected void Application_EndRequest() {
MiniProfiler.Stop();
}
This uses the default WebRequestProfilerProvider, which stores the actual profile object in HttpContext.Current.Items.
When I ask for MiniProfiler.Current, it looks to HttpContext.Current.
When I make a request for one of my web api URLs:
Application_BeginRequest creates the profiler, store it in HttpContext.Current
in a web api MessageHandler, I can see HttpContext.Current
in a web apu IActionFilter, HttpContext.Current is now null, and my attempt to MiniProfiler.Current.Step("controller:action") fails
my EF queries run from various services do not get recorded, as that miniprofiler hook relies MiniProfiler.Current, which relies on HttpContext.Current, which is null right now
Application_EndRequest fires, and HttpContext.Current is magically back, and so it wraps up the profiler and tells me how long it's been since the request began
I dug through the code, and I can create my own IProfileProvider, to store the profiler object somewhere more reliable than HttpContext.Current, but I don't know where that could be.
I spent a few hours trying things out, but couldn't find a workable solution. The problems:
the IProfileProvider is a global variable; all worker threads in either the MVC or Web API pipeline all have to use the same IProfileProvider
I can dig around in web api RequestContext.Properties to pull out the HttpContext for that request, but that doesn't really help because my IProfileProvider is global across the entire app; If I tell it to store the profile in HttpContext A, then any simultaneous requests for other HttpContexts are going to pollute the profile
I can't rely on any kind of threadstorage because of async/await re-using threads dynamically
I can't stick the profiler object in an Ninject binding with InRequestScope because InRequestScope doesn't seem to work with web api 2.1, but even if I could
everyone says HttpRequestMessage.Properties is the new HttpContext.Current.Items, but again, IProfileProvider is a global variable and I don't know of a way to ensure each request is looking at their version HttpRequestMessage. MiniProfiler.Current can be called from anywhere, so I guess a global IProfileProvider would have to somehow inspect the call stack to and find an HttpRequestMessage there? That sounds like madness.
I'm at a loss. What I really want is a special variable.
The process of putting the question together I figured it out. HttpContext.Current can get lost when you async/await things: Why is HttpContext.Current null after await?
I had to make the web.config change listed there, and adjusted my filters to use Miniprofiler.Current before any awaiting.
Also discussed at https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/
I have installed Glimpse (Glimpse MVC4) and MiniProfiler (with EF support).
I also installed the MiniProfiler plugin for Glimpse.
I have that all wired up and working. I want to allow the configuration of Glimpse to determine if MiniProfiler should start profiling. That is, if Glimpse is enabled (through Glimpse.axd not via a config setting) I want to call MiniProfiler.Start() in the Application_BeginRequest() method. So, something like this:
protected void Application_BeginRequest()
{
if (Glimpse.IsRunning)
{
MiniProfiler.Start();
}
}
Is there a way to determine if Glimpse is enabled?
Technically there is a way, but I'd call it hacky at best. I'll let you decide if it is a good fit for your purposes.
var policyString = HttpContext.Current.Items["__GlimpseRequestRuntimePermissions"].ToString();
RuntimePolicy glimpsePolicy;
RuntimePolicy.TryParse(policyString, out glimpsePolicy);
if (!glimpsePolicy.HasFlag(RuntimePolicy.Off))
{
MiniProfiler.Start();
}
The reason I call it a hack is because while Glimpse may be On at the beginning of request, it may be later turned Off.
An example of this behavior is when Glimpse automatically shuts off once ASP.NET begins to report an unsupported media type, like an image. ASP.NET does not have the ability to know the media type until after the HTTP Handler has run. In this case, Glimpse will say that it is on at the beginning of the request, but then will be off at the end of it.
When a new MVC3 project is created, [HandleError] attribute is by default registered as GlobalFilter in GLobal.asax. However, if I comment it and execute following (with custom error mode on), it still works. I do see ErrorView with ErrorInfo model populated. Then what is the need for registering HandleError in Global.asax?
[HandleError(ExceptionType = typeof(NullReferenceException),View = "ErrorView")]
public ActionResult Index()
{
throw new NullReferenceException();
return View();
}
That is setting the default MVC exception handling policy. It will render the /Views/Shared/Error.cshtml view when an unhandled exception arises, without you having to explicitly add the HandleError attribute on every controller or action.
You can then add more specific HandleError attributes to your controller and\or actions, so you could display another error view than the default view or handle a more expecific exception type.
For the HandleError filters to work (global or not), you just need to make sure the custom errors are enabled in the web.config, as in <customErrors mode="On" /> (The default is RemoteOnly, so during development they won´t be executed)
This is nicely explained (following an example) here
Let's say I have some ajax based component, whose handler in server throws for some reason an exception (e.g. programming error, can't access database). And basically server responds with internal server error or some such. How can I catch this situation in browser and display for examplen an error message somewhere.
When user clicks this link, the server will show an error page and the browser should detect based on http status code that something went wrong and somehow to react to it.
new AjaxLink<Link>("link") {
public void onClick(AjaxRequestTarget target) {
throw new RuntimeException();
}
}
See org.apache.wicket.settings.IExceptionSettings#getAjaxErrorHandlingStrategy.
There is an example of this at http://www.wicket-library.com/wicket-examples/ajax/links (failure and exception links).
Normally, Wicket Ajax classes like AjaxFallbackLink for example, have a method
updateAjaxAttributes(AjaxRequestAttributes attributes)
where you can register error handlers, that execute javascript within the browser.
Override this method, create an AjaxCallListener and call
AjaxCallListener listener = new AjaxCallListener();
listener.onFailure(ADD_JAVASCRIPT_TO_EXECUTE_HERE);
attributes.getAjaxCallListeners().add(listener);
see the documentation of
org.apache.wicket.ajax.attributes.AjaxCallListener.getFailureHandler(Component component)