MVC3 What Level Should I Catch Errors - asp.net-mvc-3

I have a website which is utilzing MVC3. I have an n-tier architecture and I am curious at what level it is best to catch errors.
For isntance let's say I have a Students table I have a StudentRepository with a function such as:
StudentRepository.GetHightestGrade(studentId)
So should I have my Repositry function have a try/catch block - or should I put the try/catch directly into the ActionResult function. OR would I be better served adding in a business class and then my ActionResult function would do something such as
Business.GetHighestGrade(studentId) and that function simply has a try/catch and calls the Repository function?

You should really only wrap methods in try/catch if you are trying to prevent errors from bubbling up the stack. Generally it's best to put the try/catch at the top layers, to shield the users from the errors. Lower layers should generally throw exceptions during exceptional circumstances, you should only try to catch them in higher layers.
You will find your code is much more readable without a ton of try/catch blocks, I personally try to avoid them when I can, and let the MVC3 HandleError filter attribute take care of displaying error messages. However sometimes you may want to retry an operation if it throws an exception, which makes a good try/catch candidate.
Take a look at ELMAH -- using it should help you write code that avoids exceptions in the first place. But you should only explicitly try/catch if you are expecting an exception, and want to take some action in response to it.

it depends on what you wanna do in the catch, do you just wanna fail silently? do you wanna log the error? do you wanna return a view to notify the user that something went wrong?
if you just wanna notify the user, then catch it in the controller.
if you wanna log it, then catch it in your repo, and have your repo do the logging through some logging service, you could use some higher service that does the call catch the error and log it, but that might be an over kill.

you should write a global filter attribute for custom exceptions. Catch all exception over that filter and return to view. Something like that
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
namespace Filters
{
public sealed class HandleException : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
throw new ArgumentException("filterContext");
else if (typeof(AjaxException).IsInstanceOfType(filterContext.Exception) && !filterContext.ExceptionHandled)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.ContentEncoding = Encoding.UTF8;
filterContext.HttpContext.Response.HeaderEncoding = Encoding.UTF8;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = 400;
filterContext.Result = new ContentResult
{
Content = "Unexpected error",
ContentEncoding = Encoding.UTF8,
};
}
}
}
}
and add this filter to global filter atributes in global.asax

Repository level must have try-catch construction that handle sql exceptions and write errors to Log. If your business logic is complicated and can produce errors or inconsistent states - you should check it and write errors to Log too. If you want to notify user about something gonna wrong (if business logic provide exceptions) - you should use try-catch constructions in your controller actions and use ModelState.AddModelError(), as one of ways to notify user, or use other way to pass info about error into view. And finally you must have class CustomErrorFilter : IExceptionFilter registered in global.asax in Application_Start:
GlobalFilters.Filters.Add(new LoggingFilter());
That filter should write to Log all unhandled errors that occurs on controller level.
Remember about your code provide normal workflow and can provide error workflow (throw exceptions as one of ways to organize error workflow, or return some error codes). You should count on it and handle both workflows.

Related

How to handle Optional from Service to Controller in Spring?

Spring JPA returns an Optional. I return the Optional from the Service. There if Optional is not present I pass error to model. The other case if there is a database error for example database not available, I do not catch these exceptions. If that happens user will see this exception in browser. I do not know how to handle this very rare error. For me this should never happen and if it does, ok . I do not want to handle this exception all the time. What do you think about my architecture.
Service:
#Override
public Optional<Client> findClientById(Long id) {
return clientRepository.findById(id);
}
Controller:
Optional<Client> client= clientService.findClientById(id);
if(client.isPresent())
{
model.addAttribute("client", client.get());
}
else
{
model.addAttribute("error", "No clientfound with this ID!!");
}
Firstly, you shouldn't pass error as an attribute in your model - that's REST anti-pattern. The best way to hand this is to use HTTP codes, e.g. return 502. To do so, you may wrap your exceptions up your code into HttpResponse. To catch your exception, you may approach similar to method that explained here, i.e. catch spring data exception, wrap it up a way you wanted and throw on higher level for processing.

Respect\Validation\Validator - using an array while catching errors

I am attempting to catch errors utilizing the Respect\Validation\Validator opensource PHP class. I used their example to create an array of checks. Although that seems to work ok, I then attempted to catch any error messages so that I could display it to the user. I saw no method to do so as a full array (check everything, store all messages in an array). So instead, I tried to cycle through using the check method in Validator.
This is inside of a class method, using the F3 (Fat Free) Framework.
I end up with the following error:
Cannot use object of type Respect\Validation\Validator as array
The code is below. What is the proper way to perform this task using arrays here? Thank you for the assistance!
$registerValidator =
Respect\Validation\Validator::attribute('email', Respect\Validation\Validator::email()->length(1,null)->notEmpty())
->attribute('address', Respect\Validation\Validator::stringType()->length(3,null)->notEmpty())
->attribute('city', Respect\Validation\Validator::alpha()->length(2,60)->notEmpty())
->attribute('state', Respect\Validation\Validator::alpha()->length(2,2)->notEmpty())
->attribute('zip', Respect\Validation\Validator::intType()->length(5,5)->notEmpty());
foreach($this->f3->get('POST') as $key => $value){
try{
$registerValidator[$key]->check($value);
} catch (\InvalidArgumentException $e) {
$errors = $e->getMainMessage();
$this->userMessage($errors, 'warning');
$this->f3->reroute('/register');
}
}
I have also tried to use the assert method as found in their docs, but utilizing the below change, I get a different error at a 500 Server Internal Error, instead of seeing my echo:
try{
$registerValidator->assert($this->f3->get('POST'));
} catch (Respect\Validation\Validator\NestedValidationException $e) {
$errors = $e->getMessages();
echo($errors); // I can't even get here.
foreach($errors as $error){
$this->userMessage($error, 'warning');
}
$this->f3->reroute('/register');
}
With this 500 Error, rather than seeing my Echo, so the page stops loading entirely.
All of the required rules must pass for ...
You cannot really use the Validator class as an array like you're doing on $registerValidator[$key]->check($value). The object in $registerValidator variable contain the chain of rules to validate an input.
In your case I believe the input is the array coming from the POST, so first of all you should use the Key validator instead of Attribute.
However the real reason why you cannot catch the errors is because you have a typo on your catch statement, the class name should be Respect\Validation\Exceptions\NestedValidationException like it's stated in the documentation, not Respect\Validation\Validator\NestedValidationException.

Response.Clear() equivalent in MVC

I'm very new to MVC. How do I perform a Response.Clear() from within an MVC3 aspx page?
I have tried:
Response.Clear();
ViewContext.HttpContext.Response.Clear();
Neither of them seem to function at all, in that any HTML content before the statements remain in the output. The only thing that seems to work is Response.Close(), but of course that doesn't allow me to output anything afterwards.
The reason for wanting this simply a testing/debugging exercise - I just want to be able to clear the buffered output, from inline code in the aspx page, and output something else afterwards. Similar to what one could do with web forms.
Note, I don't want to perform this within the controller, as it means re-compiling every time I make a change, as well as losing session state. The point of this is to fiddle within the aspx page to avoid re-compiling each time.
Taken from your comment, that you want to mess with your code before an Action is executed and afterwards, the most ideal solution is to use custom ActionFilters:
public interface IActionFilter
{
void OnActionExecuting(ActionExecutingContext filterContext);
void OnActionExecuted(ActionExecutedContext filterContext);
}
OnActionExecuting is used to execute code before your Controller action is called, while OnActionExecuted is used after the action method has done its job.
So you hook up a custom filter like this:
public class MyCustomFilter : IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//put your custom response and shenanigans here
...
return;
}
}
After this you can decorate your controller method with this filter:
[MyCustomFilter]
public ActionResult ListSomething()
{
/* magic happens here */
}
There's a lot you can achieve here, but I suggest some further reading into this:
http://www.dotnet-tricks.com/Tutorial/mvc/b11a280114-Understanding-ASP.NET-MVC-Filters-and-Attributes.html
http://msdn.microsoft.com/en-us/magazine/gg232768.aspx
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
http://msdn.microsoft.com/en-us/library/gg416513(vs.98).aspx
Sidenote: If this is just for learning and debugging purposes, I'd take a look at newer mvc versions (4, 5).
In stead of focussing on Response, perhaps you should focus on what you wish to return in your controller method.
If you wish to return a full rendered View
return View("myView");
If you wish to return a PartialView
return PartialView("myPartialView");
If you wish to return a FileStream
return File(myBytes, "filename.ext");
If you wish to return a Json string,
return Json(myObject);
Try
ViewContext.HttpContext.Response.Flush();
Istead of :
ViewContext.HttpContext.Response.Clear();

Is it possible to RenderPartial a default or error View if the specified View is not found?

I am using MVC3.
I am wondering whether it is possible to render an error View if the specified View is absent.
ie if "MyTableX" is absent:
RenderPartial("MyTableX");
would return "Error.cshtml" as the Partial View, saying something like "Partial View not found" in the page.
MVC got an attribute called [HandleError] which you should set on your BaseController (or on each controller). There is no need to specify any of the options for the attribute.
The problem with [HandleError] is that it can’t handle 404 (not found), thus we need to create a custom error controller and tell ASP.NET to use it (by configuring web.config and creating and ErrorController):
http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/#.UTknoxyfjmA
You can do something based off of this - the trick is in getting the view path.
A missing view returns an InvalidOperationException. So we really need to determine if the view is missing or if it's caused from something different. One way is to figure out how to get the IView in the filter, cast it to a RazorView and grab the path off of it - or the 'hacky' way is to do the below code, but actually look for "the view" and "was not found" in the exceptions message. Its ugly, I know, but if you want something that works tonight, thats all I got before I head to bed, otherwise try to get the view info from that filter.
This code from Phil Haack in this link may help in trying to get the path name, a quick test yielded I wasn't able to get the IView because my filterContext.ParentActionViewContext was null.
Retrieve the current view name in ASP.NET MVC?
So I wrote this basic one, but again, anything throwing an InvalidOperationException will cause this.
Also note a missing 'MissingView.cshtml" could cause an infinite loop here (untested assumption)
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ViewCheckFilterAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var exception = filterContext.Exception;
if (exception is System.InvalidOperationException)
{
//ideally here we check to ensure view doesn't exist as opposed
//to something else raising this exception
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/MissingView.cshtml"
};
filterContext.ExceptionHandled = true;
}
}
}

Handle Potentailly dangerous request-MVC-ASP.NET

How do I handle potentially dangerous request in MVc?
I am using the following in the global.asax to handle the error, but its not catching those errors.
Its catching other error types.`
else if (httpException is HttpRequestValidationException)
{
routeData.Values.Add("action", "General");
}`
any one done this in MVc?
In mvc, in general HandleErrorAttribute Class is used to handle exceptions and return specific view based on exception type
[HandleError(ExceptionType = typeof(HttpRequestValidationException), View = "RequestIsNotValid")]
public class SomeController : Controller {}
This will redirect to RequestIsNotValid view, when customErrors mode in web.config is 'on' or 'remote'. Of course, during 'remote', it will be shown only to remote users. If you want to test it during development, set customErrors mode= 'on'
I assume you want to allow HTML for certain properties? That will stop the error from being thrown.
In MVC 3, the AllowHtml attribute does the job.
http://davidhayden.com/blog/dave/archive/2011/01/16/AllowHtmlAttributeASPNETMVC3.aspx

Resources