I am developing an ASP.NET site with C# on IIS 7, but I hope for an answer that will apply to IIS 6 as well. Part of this site is the ability to upload up to 5 images at a time. I have a nice algorithm to resize the image that is uploaded to my optimal size and ratio.
So the only real size limitation I have is during the initial upload. I have modified my web.config to raise the packet limit from 4MB to 32MB. For the most part this takes care of my issues.
My question comes in the rare cases that a user tries to load more than my limit. I can raise the limit, but there is always a chance a user can find 5 files that are bigger. If a user selects files that are bigger, my try/catch block does not handle the error. The error is coming from IIS.
So how can I catch the error in C# code where I can make modifications to my ASP.NET interface to inform the user to select smaller files instead of them seeing a nasty error screen?
You can get access to the exception when the request length is exceeded. Use an HttpModule, and add a handler for the Error event.
The exception is of type: System.Web.HttpUnhandledException (with an InnerException of type: System.Web.HttpException).
To catch this exception, add this to your web.config:
<httpModules>
<add name="ErrorHttpModule" type="ErrorHttpModule"/>
</httpModules>
And add this class to your App_Code folder:
public class ErrorHttpModule : IHttpModule
{
private HttpApplication _context;
public ErrorHttpModule() {
}
private void ErrorHandler(Object sender, EventArgs e)
{
Exception ex = _context.Server.GetLastError();
//You can also call this to clear the error
//_context.Server.ClearError();
}
#region IHttpModule Members
public void Init(HttpApplication context)
{
_context = context;
_context.Error += new EventHandler(ErrorHandler);
}
public void Dispose()
{
}
#endregion
}
If you're using the traditional asp:FileUpload control, then there isn't a way to check the size of the files before. However, you can use a Flash or Silverlight approach. One option that has been suggested to me is Uploadify
I don't know for sure that this will work, but at least in IIS 7 you might try catching the Error event in an HttpModule that's configured to run for static files. From there, you could redirect to an appropriate error page.
You can catch these in Global (the global.asax.cs file). Add an Application_Error handler - you will get an HttpUnhandledException. Its InnerException will be an HttpException with the message "Maximum request length exceeded".
However, these errors are handled before your page code ever gets loaded or executed, so there is no way for your page to catch the exception or to know it ever happened. After catching this exception, you could stick a message in your Session for later display. You could also call response.Redirect from Global to display a new page, or redisplay the original with the error message from Session.
Related
In the site settings of our DotNetNuke installation, we set the Default Page for "500 Error Page" as you can see below.
After setting this, we were expecting to be redirected when an error occurs. Instead we're still redirected the the "Default.aspx?tabid=..." page.
Why isn't the correct page shown?
What do we need to change for it to work?
(We're using v9.02.00 366, .NET Framework 4.6)
EDIT: Here's how I force the error to occur using a custom module.
public partial class TriggerError500 : PortalModuleBase
{
protected void Page_Load(object sender, EventArgs e)
{
if(UserId == -1)
{
throw new NotImplementedException();
}
}
}
This module has been placed on a public page to test the error 500 page.
The 500 error page will most likely only be used when an exception is completely unhandled. For example, if an exception is handled by a developer, then a friendly message will be shown on the page with part of the exception in the URL. This may account for the URL thing you're seeing. It's the same page as the module in question, but in a different format.
When the exception is not handled, a visitor would ordinarily be shown the infamous "yellow screen of death" (YSOD) with error details. Depending on the settings in the web.config, the level of detail will be generic or detailed. I believe that this is the use case intended for the 500 error page. This is when you should see it.
If my memory serves me right, you may want to try this in your web.config:
<customErrors mode="On" defaultRedirect="500" />
A setting that will affect error handling at a platform level can be toggled in the security section
Screen Shot here
Due to the way that DNN handles the loading of modules, and pages, the code that you are writing does not actually trigger an HTTP 500 error, as the page itself is loaded properly. The module loading error is captured by the framework and the error is logged to the Admin Logs, but the page itself is rendered.
You typically get an HTTP 500 error when you cannot connect to the database or otherwise, in those cases the DNN will adhere to the rules.
It is possible, that you could set Response.StatusCode = 500; and then end the response and get the desired behavior, but i have NOT tested this.
I have a com dll built in .net and referred it as a interop.mycomlib.dll in my ASP.NET application. I initialize a class in the com object then call some functions and finally when the user signs off or closes the browser I release the com object.
Below is the code I am using. Initially, for the first user, the InitInstance() is called but when the user signs off the ExitInstance() of the com is not called.
If any other user signs on the InitInstance() is not called again because the same instance of com object is used for all the users. ExitInstance() is called only when an iisreset is performed or the w3wp process is terminated.
Is this the default behavior of how com interop works with asp.net, or is there something I am missing to do to completely dispose the com object?
public class ComFacade : IDisposable
{
public ComFacade()
{
myComObj_ = new MyCOMLib.MyClientClass();
}
..............................
public void Dispose()
{
Dispose(true);
myComObj_ = null;
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
try
{
Marshal.ReleaseComObject(myComObj_);
}
catch (Exception ex)
{
throw;
}
}
this.disposed = true;
}
}
}
Thanks
You don't mention what the scope of the variable containing the ComFacade instance is. If the variable is static then this would be the behavior I would expect.
I would suggest you understand the ASP.NET page lifecyle and the implications of that with variables of different scopes. Unless the COM reference is supposed to be a singleton then you will need to create a new instance each time the page loads and dispose of it as appropriate (probably when the page is rendered).
Update (based on comment)
Note: This answer applies to any object in .NET that you try to keep around longer that a single page request. Eventually all objects are disposed / garbage collected.
You mention that the object is created when the user logs in and disposed when they log off. The only way you could do this is to cache the object in something static to keep reference to it. Keep in mind that every time the user does something in their browser a request goes from the browser back to IIS/ASP.NET for processing and invokes a page life-cycle (over-simplification, but good enough). Each time the user does this the page may be handled by a different thread in the App Pool each time. If more than one user is interacting with the site then over a period of time the same thread may (and most likely will) get used by more than one user. In short this is why with ASP.NET/IIS you must be extremely cautious with using singletons / static members.
On a side note, my question would be why do you need a reference to the COM object for more than a single page request?
What is the best way to set up authentication against a custom database of users, in ASP.NET? My current setup is not great, and I am sure that there is a better way to do authentication, but all I can find are some articles that are seven or eight years old. My current setup is as follows:
Application uses the Windows username (via Windows Authentication), to work out whether a user is in a database of allowed users. This is done via a call to an Authenticate method in Page_Load.
If the user isn't in the allowed users, then the page redirects to a login screen.
The flaw with this method is that it calls:
Response.Redirect("~/login.aspx", false)
Which executes the entire body of the Page_load method. Is there a better way of doing authentication? Would something like custom Page classes, or HTTPModules do the job?
You could do your check earlier in the request, like in OnInit, or you could do something a little more robust, like implement your own membership provider: MSDN article / Video tutorial
Okay, so this is basically how I done it. I wrote this class that inherits from System.Web.UI.Page. I override the OnInit event and this is where the authentication happens (looks up the Windows username against the database of users). If the user doesn't get authenticated, isTerminating gets set to true, and the OnLoad event only runs if isTerminating is false. I tried leaving a Response.Redirect with the second parameter set to false on its own, but this still ran all the subsequent page events. (even with a call to HttpApplication.CompleteRequest())
public class BasePageClass : Page
{
private bool isTerminating = false;
protected override void OnInit(EventArgs e)
{
isTerminating = !AuthenticationManager.Authenticate();
base.OnInit(e);
}
protected override void OnLoad(EventArgs e)
{
if (!isTerminating)
{
base.OnLoad(e);
}
}
}
I have no idea whether not running the OnLoad event is the best thing to do, but it "seems" to work fine.
I have made a custom error page for my ASP.NET 4 application. I put the exception object in HttpContext.current.Session["CustomError"] but when the user is redirected to the error page HttpContext.current.Session["CustomError"] is null.
I do it in CustomError class constructor like this:
public CustomError(enExceptionType ExceptionType) : base(ExceptionMessage(ExceptionType)) {
HttpContext.Current.Session["CustomError"] = this;
}
when I step over the code Session["Error"] contains the error object.
any idea?
UPDATE:
I removed custom error page from web.config and added this to glabal.asax:
void Application_Error(object sender, EventArgs e)
{
if (Context.IsCustomErrorEnabled)
{
Response.Redirect("~/Error.aspx");
}
}
by stepping through this function I noticed that when an exception is thrown this function is called two time, the first time Session["CustiomError"] contains the error object but the second time its null.
Instead of using Response.redirect(URL) (which I assume you have in your code) use
Server.Transfer(URL)
or
Response.redirect(url, false)
Why Server.Transfer(url)?
Transferring to another page using
Server.Transfer conserves server
resources. Instead of telling the
browser to redirect, it simply changes
the "focus" on the Web server and
transfers the request. This means you
don't get quite as many HTTP requests
coming through, which therefore eases
the pressure on your Web server and
makes your applications run faster.
Source here.
Please let me know if one of these works for you.
UPDATE:
If you use a web config setting can you try adding ResponseWrite value to redirectmode var?
<customErrors mode="RemoteOnly" defaultRedirect="~/errors/GeneralError.aspx" redirectMode="ResponseRewrite" />
If this is still not working I suggest to implement this (I've done it in my application to log the errors in log files (for me as admin) and present a generic error to the user).
This solved the problem, but I would appreciate it if someone tells me why :)
void Application_Error(object sender, EventArgs e)
{
if (Context.IsCustomErrorEnabled)
{
Response.Redirect("~/Error.aspx");
**Server.ClearError();**
}
}
In this scenario I wish too bypass my normal error logging, which wont work, and simply request the Error view and and send an email. I don't wish to duplicate this special case handling in all controllers, and DB access might be attempted before any action is requested. Where should I place this special handler, and if not in a controller, how do I call up the Error view?
Oh yes, I'm using Elmah for routine logging of unhandled exceptions.
Try using something along these lines in your controller
[HandleError(ExceptionType = typeof(SqlException), View = "SqlError")]
Public Class ProductController: Controller {
public ViewResult Item(string itemID)
{
Item item = ItemRepository.GetItem(itemID);
return View(item);
}
}
Now in your Views/Shared/ folder you can create a View called "SqlError.aspx" that will be returned if there's a SQL Exception.
I would also recommend handling all of your Application Error "stuff" in the Global.asax file. IE: the part that does the emailing of the error, logging of the error, etc.
Check out this SO question for an idea
ASP.NET MVC Custom Error Handling Application_Error Global.asax?
I don't know what you code is but assuming the logging is done in some global error handling thing then edit the error handling to be like this should do it:
try
{
//logging
}
catch(SqlException)
{
//send email
return View("Error");
}