I am using MvcContrib and the TestControllerBuilder to write tests for my controller.
I am writing the tests for my Error Handling Controller that looks like this:
public JsonResult HttpError()
{
Exception ex = null;
try
{
ex = (Exception)HttpContext.Application[Request.UserHostAddress.ToString()];
}
catch
{
}
if( ex != null )
{
return Json( new ErrorViewModel() { Message = ex.Message, Source = ex.Source, StackTrace = ex.StackTrace, Type = ex.GetType().Name}, JsonRequestBehavior.AllowGet );
}
else
{
return Json( new ErrorViewModel() { Message = "An error has occured." }, JsonRequestBehavior.AllowGet );
}
}
Basically, my global error handling puts the last exception into the Application store and this controller tries to pull it back out, convert it to Json, and return it (we are returning everything as Json because these methods are only getting called as Web Services).
To fully test this, I need for UserHostAddress to contain something predictable, but the objects setup by TestControllerBuilder leave that property null.
How can I make this work? I'm not sure how I can make this work in my test.
TestControllerBuilder uses Rhino.Mocks for mocking the HttpContext. Knowing this, you could put the Request object back into "record" mode and stub out a response:
controller.Request.BackToRecord();
controller.Request.Stub(r => r.UserHostAddress).Return("75.142.12.45");
controller.Request.Replay();
Do this after you've initialized the controller, but before your method call.
Related
Im working on a asp.net core website and im trying to make som global validation exception handling using Filters. The backend can at random places throw fluentapi ValidationException and I want to catch these and show the error messages to the user. This filter only cares about ValidationExceptions. All other exceptions will be handled later..
Instead of using a try/catch in every post action in all my controllers, I want to use a filter that catches only ValidationExceptions, add the errors to the ModelState and then return to the original view with the updated ModelState.
I have tried many things but every time I just get a blank page after the filter finishes. I can easily set a new RedirectToRouteResult witht the controller and action from the context. But then I dont have the ModelState and values the user entered..
public class PostExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.Exception is FluentValidation.ValidationException)
{
var ex = context.Exception as FluentValidation.ValidationException;
context.Exception = null;
context.HttpContext.Response.StatusCode = 200;
context.ExceptionHandled = true;
foreach (var item in ex.Errors.ToList())
{
context.ModelState.AddModelError(item.PropertyName, item.ErrorMessage);
}
// Done with the stuff I want.
// Now please go back to the original view with the updated modelstate and values
}
else if (context.Exception is UnauthorizedAccessException)
{
// Do something else...
}
else
{
// Do something else...
}
base.OnException(context);
}
}
You cannot access the particlar Model(related to Action Method) in Exception Filters. So you have to handle the error at Controller level if you want to add Errors to model.
try
{
//Do something
}
Catch(Exception e)
{
ModelState.AddModelError(string key, string errorMessage);
Return View(model)
}
The error message will present itself in the <%: Html.ValidationSummary() %> in your View
Without try-catch blocks you won't know if exception occured in Action Method, So that you can add Custom Errors to Model.
When I try to propagate an exception and pass it as parameter into my ErrorController, it is always null.
Controller
public ActionResult Test()
{
try
{
throw new Exception("ALGO");
//
return View();
}
catch (Exception ex)
{
return RedirectToAction("Error", "Error",
new
{
exception = ex,
controller = this.ControllerContext.RouteData.Values["controller"],
action = this.ControllerContext.RouteData.Values["action"]
});
}
}
ErrorController
public ActionResult Error(Exception exception, string controller, string action)
{
// exception is always null...
Response.StatusCode = 500;
ViewBag.exception = new HandleErrorInfo(exception, controller, action);
return View();
}
Any idea how to get the exception properly?
Is there a better approach for error handling?
I also tried this one but I got several errors because of parameteless constructor for handleerrorinfo
Whenever you use RedirectToAction, it performs an HTTP redirect. Any of the values you pass have to be primitive types, since they will be appended to the redirect URL. That means that you cannot pass an entire object, like you are trying to do with the exception. The easiest thing that you can do is to replace the RedirectToAction with
return Error(ex, this.ControllerContext.RouteData.Values["controller"], this.ControllerContext.RouteData.Values["action"]);
This approach will still call your Error method and display the View properly, but it will not change the URL like a redirect would. If you wanted to use this method, then you could try using javascript to change the URL.
Also, do you really want to display all of the error details to your end user? If you are just using this to display a plain error page without details then you could look into simply using the customErrors attribute in your web config to redirect to an error page. That way all that your end user knows is that some error occured.
I am working on an asp.net MVC 3.0 Application. I am using using my own CustomRoleProvider
and CustomErrorHandler by overriding default attributes.
Every thing is working fine. But ,the problem is with the exception handling.
While testing the application , tester has given invalid DB connection to test.
The result is , Custom Error Handler is not rendering Error View , instead it is routing the original path
For ex:
I am running my application as
Home/Index
It is first hitting Custom Role Provider to fetch the roles for the application
Since , the Db Connection is not correct , it is raising exception that "Not able to Connect"
Now , Instead of routing to Error View along with this error message. It is routing to Home Controller and Index action.
**The code for my Custom Error Handler is as Follows**
public class CustomHandleErrorAttribute : HandleErrorAttribute // Error handler
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
if (new HttpException(null, filterContext.Exception).GetHttpCode() != 500)
{
return;
}
if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
{
return;
}
// if the request is AJAX return JSON else view.
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.Result = AjaxError(filterContext.Exception.Message, filterContext);
}
else
{
filterContext.ExceptionHandled = true;
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult
{
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
}
}
protected JsonResult AjaxError(string message, ExceptionContext filterContext)
{
if (String.IsNullOrEmpty(message))
message = "Something went wrong while processing your request. Please refresh the page and try again.";
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
return new JsonResult { Data = new { ErrorMessage = message }, ContentEncoding = System.Text.Encoding.UTF8, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
}
In the above code , after setting up filterContext.Result . It is not rendering Error View as Expected.
Please correct/suggest me, where i am going wrong..
Updated:
public class CustomRoleProvider : RoleProvider // Custom role provider
{
public override string[] GetRolesForUser(string username)
{
// Fetching roles for user from database
}
// Some other Methods
}
This is method is generating exception , since it is trying to connect to wrong connection
Updated2:
1) I am using Custom Error Handler for the entire controller.
2) I need to catch all the exceptions including Ajax Errors
3) I have included my code for Custom Error Handler Above
4) I am also using CustomRole Provider for entire controller
5) Here, I am trying to generate exception , by giving wrong database connection
6) I am running the URL : Home/Index
7) Before going to thatr URL, it is hitting the methods in Role Provider class since i am using it as a attribute
8) Since, i have gave wrong DB Connection , It is generating exception
9) Then, it fires on exception method of Custom error handler
10) Building the Error Model for the error view
11) But, here is the problem. Instead of rendering Error View , it is going to index method of the Home Controller.
12) But, i need Error View to be rendered here, because it has failed to connect to database and getting roles . I want furthuer execution of URL Home/Index to be stopped here.
Hope this clarifies the problem..i am running in to. please feel free to ask me for furthuer details/Clarification
HandleError is designed to be able to register multiple filters (for example for different exceptions). One filter can handle only some specific exceptions or error cases and another unhandle cases can be handled by another HandleError. I suppose that currently both standard and your [CustomHandleError] filter are applied. You can set the Order property to an integer value that specifies a priority from -1 (highest priority) to any positive integer value. The greater the integer value is, the lower the priority of the filter is. You can use Order parameter for example (see here) to make your filter working before. More full description of the order you can find in the MSDN documentation.
The answer, this one and the article for example provide small examples of usage Order property of HandleError.
I have the following Delete Action method, which mainly perform two separate tasks:-
Delete a record from a Third party application using API call.
Delete a record from the database on our own system using entity framework.
My action method looks as follow:-
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
var message = "";
var status = "";
var tag = "";
Resource resource = new Resource();
try
{
Rack rack = repository.FindRack(id);
tag = rack.Technology.Tag;
resource = repository.GetResource(rack.Technology.IT360ID.Value);
}
catch (NullReferenceException)
{
return Json(new
{
IsSuccess = "False"
}, JsonRequestBehavior.AllowGet);
}
catch (DbUpdateException)
{
return Json(new
{
IsSuccess = "AlreadyUsed"
}, JsonRequestBehavior.AllowGet);
}
using(var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
query["username"] = "testuser";
query["assetType"] = resource.ComponentDefinition.ComponentType.COMPONENTTYPENAME;
query["operation"] = "DeleteAsset";
query["assetName"] = resource.RESOURCENAME;
var url = new UriBuilder("http://win-spdev:8400/servlets/AssetServlet");
url.Query = query.ToString();
try
{
string xml = client.DownloadString(url.ToString());
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
status = doc.SelectSingleNode("/operation/operationstatus").InnerText;
message = doc.SelectSingleNode("/operation/message").InnerText;
}
catch (WebException ex)
{}
}
if (status.ToUpper() == "SUCCESS")
{
try
{
repository.DeleteRack(id, User.Identity.Name);
repository.Save();
return Json(new
{
IsSuccess = "True", id = id, description = tag
}, JsonRequestBehavior.AllowGet);
}
catch (NullReferenceException)
{
return Json(new
{
IsSuccess = "False"
}, JsonRequestBehavior.AllowGet);
}
catch (DbUpdateException)
{
return Json(new
{
IsSuccess = "AlreadyUsed"
}, JsonRequestBehavior.AllowGet);
}
}
return RedirectToAction("Delete", new
{
id = id
});
}
As since I am using the entity framework to perform the deletion and also a API call, so I ended up with separate try/catch blocks . so does my action method logic consider a poor design since I am having multiple try/catch blocks inside the same action method? And what better approach I can follow?
Separate error cases are certainly not a bad pracice.
What is bad practice, though, is catching unspecific errors. You are returning "AlreadyUsed" for all DbUpdateExceptions. There might be other causes for this than the one you planned for. If that happens you swallow the error and have a silent bug. You might lack the imagination right now what those cases might be but that is just because you never know bugs before they happen. I advise that you catch even more specific than this by either interpreting the exception object (maybe interpret the message, god forbid) or by tightening the region that the catch covers to exactly the statement that can give the error.
In short, don't swallow exceptions indicating bugs. Bugs happen all the time, you want to know about them and fix them.
Also, for the same reason, never ever catch NullReferenceException. They are always bugs by convention. Insert an if already do deal with the null.
If the code in the try-catch is a "logical-entity", e.g. does an independent functionality that wouldnt affect the remaining code, or it wouldnt cause incorrect logic (incorrect execution) to the following code if it had an error. Then Why not.
But if it would break your program logic then it should be stopped and the error should be handled (scope of your try-catch block. it all depends on your program logic.
There is nothing wrong with limiting the try-catch scope(s) to as little as you possibly know.
(I might get flamed for this.)
I have a controller action that does some work in the database and then exits when it's finished. This action is being called via jQuery's ajax function with the dataType set to 'json'.
If I set the return type of the action to void, everything will function just fine except Firefox will show an error in the console that says: "no element found".
It makes sense that Firefox would throw this error if it was expecting XML to come back. However, even when I change the dataType property of the ajax call to "text", I still receive the error. In order to get rid of the error with the return type void, I would have to set the Response's ContentType to "text/html". Or I could set the return type to JsonResult and return a new [empty] JsonResult object.
I'm sure there are several ways I can make this error go away, but I wanted to know the proper way to handle actions with no return values being called via ajax.
If it matters, I'm also using the async controller action pattern.
public void DoSomethingAsync(SomeJsonObjectForModelBinding model)
{
// do some database things
}
public void DoSomethingCompleted()
{
// nothing to do...
// what should my return type be?
// do I need to set the content type here?
}
I know this doesn't exactly answer your question, but I would argue that you should always have a return value coming back from an AJAX or web service call. Even if only to tell you that the operation was successful, or otherwise return the error (message) back to you.
I often define a class like this:
public class JsonResultData
{
private bool _success = true;
public bool Success
{
get { return _success; }
set { _success = value; }
}
public object Value { get; set; }
public List<string> Errors { get; set; }
public JsonResultData()
{
this.Errors = new List<string>();
}
}
And then use it to return data or any other call meta data in the JsonResultData wrapper like so:
return new JsonResult {
Data = new JsonResultData { Value = returnValue, Success = true }
};
I can't comment because of my reputation but I still wanted to contribute to clear the confusion in Kon's answer.
In an application I caught all exceptions within an ActionMethod, set an HttpStatusCode and added an error message to the response. I extracted the message in the Ajax error function and showed it to the user.
Everything worked out fine until the application got put on the staging server, who had some kind of settings that did not allow a return message within an erroneous response. Instead some standard Html was transmitted resulting in a JS error processing the response.
In the end I had to rewrite all my exception handling returning my application errors as successful Ajax call (which it actually is) and then differ within the Ajax success function, just the way it should be.
You should not mix system-level and application-level feedback. You may not be able to control the system-level feedback the way your application needs.