I have an action that is called using requests with application/jsonin the Content-type header. These requests will automatically create a JsonValueProvider that tries to deserialize the request's content. When the json is malformed, the value provider will throw an exception leading to the application's error page.
To reproduce this behavior, simply POST invalid json data to an action sending application/json as the Content-type header. This will trigger the exception.
[Edit]
Not much code is needed. Simply create an empty controller method and use a tool like Firefox "Poster" to send an invalid request to the action.
public class HomeController
{
public ActionResult Index()
{
return this.Json(true);
}
}
Then use Poster:
Set Content-type to application/json
Set Request content to {"This is invalid JSON:,}
Send the request
The result will be the full-blown standard ASP.NET HTML error page (either generic or custom, depending on your application).
[/Edit]
Since my action is called by embedded devices, I would like to send short responses, instead of the HTML error page. I would like to be able to create a response with status code 500, Content-type: text/plain, and the exception's message as it's content.
I have already tried a custom model binder and a custom error handler attribute but neither are called since the exception occurs earlier on in the processing pipeline. Is there a way to handle this error?
As a workaround, I have currently disabled the JsonValueProvider for the whole application and load the values from the request body myself. If there is a way to disable the JsonValueProvider on a per action basis, this would also help.
Thanks in advance for any pointers!
You could subscribe to the Application_Error event in your Global.asax and handle the exception as you want:
protected void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
Response.TrySkipIisCustomErrors = true;
Response.Clear();
Server.ClearError();
Response.StatusCode = 500;
Response.ContentType = "text/plain";
Response.Write("An error occured while processing your request. Details: " + exception.Message);
}
Related
I have an ASP.NET MVC 4 application and I want to redirect all HTTP 400 errors to my custom error page. I was searching an hours to find a solution though HTTP 400 error isn't handled like 404 error. There are many solutions that show how to escape 400(bad request error), i.e. to allow using special characters for example in url. But I wouldn't able to find some solution to catch the exception.
Please help me to catch somehow all HTTP bad requests and redirect them to my error page.
Never redirect users in case of errors, instead return a response body for the failed request. The feature of IIS (and ASP.NET) to redirect to an error page, I believe, is fundamentally wrong, incorrect, and against the HTTP specification (because then the error is being returned for the error page resource itself, not the original request. And if it's a web-browser the user has no way of retrying, because reloading the page will return the error page again, not retrying their original failed request, which is what they want).
Anyway...
A HTTP 400 response must be generated by your application code, it isn't something that will be done automatically. A bad request is typically used when informing non-human agents (i.e. web service clients, not web browsers) that their HTTP request was missing required values or had malformed values.
You can do this in MVC by having a base controller class for all of your controllers like so:
public abstract class BaseController : Controller {
protected ActionResult Http400(String message) {
Response.StatusCode = 400;
return View(message); // you need to define a view file called "Http400.aspx" (or cshtml if you're using Razor) in your application's shared views folder
}
}
so in your application logic:
public ActionResult Foobar() {
if( IsBadRequest() ) return Http400("Bad request, try again");
}
You could do something as simple as adding adding this to your web.config
<customErrors mode="RemoteOnly">
<error statusCode="400" redirect="errorpage.html"/>
</customErrors>
By checking my elmah log, I see I keep receiving "too long url" requests. The error is:
System.Web.HttpException (0x80004005): The length of the URL for this request exceeds the configured maxUrlLength value.
at System.Web.HttpRequest.ValidateInputIfRequiredByConfig()
at System.Web.HttpApplication.PipelineStepManager.ValidateHelper(HttpContext context)
Here is the kind of requests I receive:
/index.php/blog/post/the_ten_deceptions_of_the_datetimepicker/+[PLM=0]+GET+http:/www.visualhint.com/index.php/blog/post/the_ten_deceptions_of_the_datetimepicker/+[0,23778,23037]+->+[N]+POST+http:/www.visualhint.com/index.php/blog/post/the_ten_deceptions_of_the_datetimepicker/+[0,0,2007]
(don't be surprised by the php thing... before being an asp.net mvc site, my site was in php and now I need to redirect this kind of old URLs to my new url format, which works well when the url stops at /index.php/blog/post/the_ten_deceptions_of_the_datetimepicker)
What could generate these requests? Does it sound malicious?
I have custom errors setup, so I though such a request would be redirected to my custom error page, but it's not. Instead, people get the typical yellow screen (firebug mentions this is a 400 Bad Request). If you look at the above stack trace, it is very short and the exception seems to be caught very early (Application_BeginRequest is not even called). Is it possible to show my custom error page or can I at least redirect to my homepage when such an exception occurs?
I tried adding a line for error 400 in my web.config:
<customErrors mode="RemoteOnly">
<error statusCode="400" redirect="/" />
</customErrors>
This redirects to the homepage right, but it adds the complete url in an aspxerrorpath query string value.
Thanks for your help.
A google search helped me find that some other people get this kind of request. Someone answered with:
I am pretty sure it is an automated way of submitting spam. There must
be an error in the configuration because it should not leave such a
juicy trail in the referrer field!
First it tells some script to get an URL, then it instructs to post to
an URL (it is easy to block spam that POSTs directly without getting
first).
The numbers could be relating to what spam message(s) to post (think
of it as indexes in a spam DB).
So, since there are good chances that these requests are not from humans following normal links, I ended up with the following solution. This avoids polluting my elmah log and this serves a blank page to the caller with a 404 code:
public void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
{
HttpException hExc = e.Exception.GetBaseException() as HttpException;
if (hExc != null)
{
if (hExc.ErrorCode == unchecked((int)0x80004005))
{
e.Dismiss();
return;
}
}
// Here I do some stuff if the error has to be logged.
}
void Application_Error(object sender, EventArgs e)
{
Exception exc = Server.GetLastError();
HttpException hExc = exc as HttpException;
if (hExc != null)
{
if (hExc.ErrorCode == unchecked((int)0x80004005))
{
Uri uri = HttpContext.Current.Request.Url;
Response.Clear();
Response.StatusCode = 404;
Response.End();
Server.ClearError();
}
}
}
Instead of returning a 404 code, I would have preferred to not send a response (the caller would have a timeout) but is it possible with asp.net? No idea...
All I need would be just the error message in plain text. But ASP.NET is doing some HTML report output from every error.
I have a jquery ajax call and when an error is thrown I'm getting all that crap over to the client side.
I've created a filter attribute but didn't helped.
public class ClientErrorHandler : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var responce = filterContext.RequestContext.HttpContext.Response;
responce.Write(filterContext.Exception.Message);
responce.ContentType = MediaTypeNames.Text.Plain;
filterContext.ExceptionHandled = true;
}
}
EDIT
I'm seeing this
and I'd like to see just what is in here filterContext.Exception.Message
It looks to me like the reason why you cannot correctly handle the exception is because it happens outside of the MVC pipeline. If you look at the stack trace in the code you posted there is no reference to System.Web.Mvc code (the firing of exception filters when an exception occurs is called from ControllerActionInvoker.InvokeAction).
The stack trace indicates that the exception happens late in the ASP.NET pipeline (OnEndRequest) and that it's coming through the Autofac component.
To capture this error you would have to subscribe to the HttpApplication's Error event. See the following article on creating a global error handler: http://msdn.microsoft.com/en-us/library/994a1482.aspx . In this event you can handle the error and redirect to a custom error page.
you need to return a ContentResult
ContentResult result = new ContentResult();
result.Content = filterContext.Exception.Message;
result.ContentType = MediaTypeNames.Text.Plain;
filterContext.Result = result;
filterContext.ExceptionHandled = true;
Since you're using JQuery and WCF (by the details of your error), you might want to take a look at this article on how to handle service faults elegantly between jQuery and WCF - you might have to rework your service if you are able to do so.
i'm trying to send back a simple error message as Json, with the HTTP code as 404.
So i started out writing my own IExceptionFilter that checks to see the exception. To keep this simple, if the exception throw is of type ResourceNotFoundException then i set the code to 404. Otherwise everything else if 500.
Now, the problem is .. the default IIS7 404 error message is returned :( my code is called .. but it seems to bypass it (later on in the pipeline)...
is there some trick i need to do?
do I need a custom error handling (in the web config) to be turned on or something?
Edit:
I'm trying to do what twitter does. Their Http Response Code documentation shows / explains some examples how they handle 404's, etc.. and i'm wanting to do that in my MVC app.
Edit 2:
The code i've done is listed here, for anyones reference :)
When you are handling your exception, are you setting ExceptionHandled to true?
Here's a quick example...
HandleException(ActionExecutedContext filterContext)
{
Exception exception = filterContext.Exception;
//Check if our exception has been handled.
if (filterContext.ExceptionHandled == false)
{
//Do your exception stuff
filterContext.Result = YourExceptionMessageAsAnActionResult();
//Set it as null.
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
}
}
I created a new class which inherits from the HandleErrorAttribute to handle ajax requests differently. The only override is on the OnException method:
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.Result = new JsonResult { Data = errorText };
return;
}
base.OnException(filterContext);
}
This does work, about half the time. When it works, the status code in the response is 500 and the error message is provided. When it doesn't, the status code is 12031 and the error message is empty.
Apparently status code 12031 is means:
ERROR_INTERNET_CONNECTION_RESET
The connection with the server has been reset.
No idea why this would be occurring.
I'm not sure precisely what is going on here, but I can make an educated guess. We do something very similar, except we call base.OnException first, and then we test filterContext.ExceptionHandled to see if the base filter handled the exception. This has always worked for us, and is different than what you do. Note that the base filter will not always handle the exception (see the source code for details).
Note that the base filter does things which your filter does not do, like test IsCustomErrorEnabled and the specific type of the exception. There are some exceptions which should not be handled; again, refer to the MVC source code for details on this.
So my wild guess is that in some circumstances your filter (which always handles the exception when there is an AJAX request) and the base filter do different things. I would suggest that you try the method we are using (call base first, and only perform your custom AJAX handling if the request is an AJAX request and the base has indicated that the exception should be handled) and see if that doesn't end up working better for you.