To preserve the original URL you could go the route described here and handle errors explicitely inside global.asax instead of using web.config customerrors or httperrors sections (which can still be configured for fallback) but change the IHttpHandler.ProcessRequest part on linked websites example with HttpContext.Server.TransferRequest(path, true). I've implemented the Application_Error event like this in one of my projects:
protected void Application_Error()
{
Exception error = Server.GetLastError();
var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;
if (!Context.IsDebuggingEnabled
&& code != 404)
{
// persist error to error log db
//Log.Logger.Error("unhandled exception: ", exception);
}
if (WebConfigurationManager.AppSettings["EnableCustomExceptionPage"].Equals("false"))
{
return;
}
Response.Clear();
Server.ClearError();
string path = this.Request.Path;
string action;
switch (code)
{
case 401:
action = "Unauthorized";
break;
case 403:
action = "Forbidden";
break;
case 404:
action = "NotFound";
break;
default:
action = "Generic";
break;
}
string newPath = string.Format("~/Error/{0}?source={1}&message={2}", action, path,
HttpUtility.UrlEncode(error.Message));
Context.Server.TransferRequest(newPath, true);
}
Internally it transfers the request to a new path handled i.e. by an ErrorController like in the described example above which actions could look like this:
public ViewResult Generic(string source, string message)
{
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = 500;
ViewBag.Source = source;
ViewBag.Message = HttpUtility.UrlDecode(message);
return View();
}
public ViewResult NotFound(string source, string message)
{
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = 404;
ViewBag.Source = source;
ViewBag.Message = HttpUtility.UrlDecode(message);
return View();
}
...
The TrySkipIisCustomErrors = true
prevents IIS from redirecting to the default custom error pages.
You can handle thrown Exceptions (like special Exceptions thrown in business/service layer) other than HttpException differently inside Application_Error override method.