I've implemented an ActionFilterAttribute responsible for NHibernate transaction management. Transactions are committed in the OnResultedExecuted override, which occasionally will result in an exception being thrown.
I'm able to successfully intercept these exceptions in the controllers OnException override, however the page still redirects as if the transaction were successful.
What I'd like to be able to do is return the same view action that caused the error with the exceptions message added to the ModelState.
I've tried a number of different things, none of which seem to work.. here's my latest attempt:
[HttpPost]
[Transaction]
[HandleError]
public ActionResult Enroll(EnrollNewEmployeeCommand command)
{
if(command.IsValid())
{
try
{
_commandProcessor.Process(command);
}
catch(Exception exception)
{
ModelState.AddModelError("", exception.Message);
return View(command);
}
return this.RedirectToAction(x => x.Index()); // redirects to index even if an error occurs
}
return View(command);
}
protected override void OnException(ExceptionContext filterContext)
{
//dont interfere if the exception is already handled
if (filterContext.ExceptionHandled)
return;
ModelState.AddModelError("", filterContext.Exception.Message);
filterContext.ExceptionHandled = true;
// want to return original view with updated modelstate
filterContext.Result = new ViewResult
{
ViewName = filterContext.RequestContext.RouteData.Values["action"].ToString(),
ViewData = filterContext.Controller.ViewData
};
}
What I'd like to be able to do is return the same view action that caused the error with the exceptions message added to the ModelState
You can't. OnResultedExecuted happens too late. The view rendering has ended and you can no longer modify what will be sent to the client at this stage.
Your last chance if you want to still be able to modify the returned result to the client is OnResultExecuting. So you could commit your transactions there. Wouldn't be so penalizing I guess.
At the contrary, I would even commit transactions in the OnActionExecuted event, as at this stage all you've got should be a fully initialized view models passed to the view for rendering. That's where your transaction boundaries should end. The process of rendering of the views should be excluded from any transactions and DB stuff. It's just HTML (or something) rendering from a view model, plain and simple.
Related
I am trying to fetch Customer data to parse them into customer object to display on TableView. The following code sometimes works, sometimes not. Whenever it does crash, it shows Customer data is empty in the foreach loop even though I run the same code every time. I do not have clue what could be wrong in this circumstances. I am quite new on this platform. If I am missing anything/ extra information, please let me know.
namespace TableViewExample
{
public partial class MyDataServices : ContentPage
{
private ODataClient mODataClient;
private IEnumerable <IDictionary<string,object>> Customers;
public MyDataServices ()
{
InitializeComponent ();
InitializeDataService ();
GetDataFromOdataService ();
TableView tableView = new TableView{ };
var section = new TableSection ("Customer");
foreach (var customers in Customers) {
//System.Diagnostics.Debug.WriteLine ((string)customers ["ContactName"]);
var name = (string)customers ["ContactName"];
var cell = new TextCell{ Text = name };
section.Add (cell);
}
tableView.Root.Add (section);
Padding = new Thickness (10, 20, 10, 10);
Content = new StackLayout () {
Children = { tableView }
};
}
private void InitializeDataService(){
try {
mODataClient = new ODataClient ("myURL is here");
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
private void GetDataFromOdataService (){
try {
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
}
}
Its hard helping out here, however here are some things to consider:-
It sounds like the dataservice could either be not contactable / offline; too busy or it could even be throwing an exception itself and returning a data response that you are not expecting to receive, that then triggers an exception and crash in your application as your always expecting an exact response without catering for any abnormal responses / events.
If you are contacting an external service over the internet it may just be your internet connection is slow / faulty and not returning the information fast enough as other possibilities.
In your code you are assuming that you always get a response from the server - and that this response will always be of an anticipated structure that your expecting to decode - without factoring in any possibility of abnormal responses returned by the dataservice. I have not used ODataClient personally, so not sure how it behaves in the event of maybe no data received / timeout or in your case the dataservice and how it behaves internally in the response to a bad-request etc.
I am assuming an exception would get thrown, and you do get your debug line executed indicating a failure.
You may want to also adjust this statement so that you write out the exception as well, i.e.:-
private void GetDataFromOdataService ()
{
try
{
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR!" + ex.ToString());
}
}
If there was a bad response, then the line at Customers = ..... would throw the exception as there may be no Customers returned or some other information packaged in the response from the dataservice.
The Customers variable would also be null at this point I am assuming due to this failing.
So when you get back to your code at foreach (var customers in Customers) { it will then throw a null reference exception as Customers is infact null.
As all your current code executes in the constructor without any try and catch block around this, it will also crash your application at this point as well.
Also you are doing all of this work in the constructor. Try seperating this out. I haven't investigated exactly where the constructor gets called in an iOS page life-cycle, however, if it is in the viewDidLoad, then you have something like 10 seconds for everything to complete, otherwise it will exit automatically. I imagine in your case, this isn't applicable however.
Going forward also try putting your layout controls in the constructor, and move your data task to maybe the OnAppearing override instead.
Using async would definitely be advisable as well, but remember you need to inspect the response from your dataservice, as the error could be embedded within the response also and you will need to detect when it is OK to process the data.
I've read the question Handle ViewExireException/ajax and display a Primefaces dialog and the answer from BalusC. I'd want to handle the ViewExpiredException by showing the alert with information to refresh the page. I've taken the suggestion from BalusC to user RequestContextto put JavaScript to execute, and I've removed the JSF redirection because I'm not using it:
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof ViewExpiredException) {
ViewExpiredException vee = (ViewExpiredException) t;
try {
log.info("Catched ViewExpiredException for view {}", vee.getViewId());
RequestContext.getCurrentInstance().execute("handleViewExpired("+vee.getViewId()+")");
return;
} finally {
i.remove();
}
}
}
// At this point, the queue will not contain any ViewExpiredEvents.
// Therefore, let the parent handle them.
getWrapped().handle();
}
The problem is, I got NullPointerException when executing the handle method from wrapped handler. I've added the return clause, and after adding it, the effect was the same:
[30.01.13 15:45:59:140 CET] 0000002e ErrorPageWrit E An exception
occurred
javax.faces.FacesException: java.lang.NullPointerException at
org.apache.myfaces.shared_impl.context.ExceptionHandlerImpl.wrap(ExceptionHandlerImpl.java:241) at
org.apache.myfaces.shared_impl.context.ExceptionHandlerImpl.handle(ExceptionHandlerImpl.java:156)
at
my.project.web.handler.ViewExpiredExceptionExceptionHandler.handle(ViewExpiredExceptionExceptionHandler.java:59)
at
org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:191)
at
org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
So, the parent handle method is executed, thought there should be the return from method (The info string is logged).
I'm using PrimeFaces 3.4 and MyFaces 2.0.7, everything on WebSphere 7.
I don't understand what is happening here. Is it possible to achieve what I want, and if so, what I'm doing wrong?
The best way was to handle that exception on client side. It's very simple few-liner and it's completly transparent:
var originalPrimeFacesAjaxResponseFunction = PrimeFaces.ajax.AjaxResponse;
PrimeFaces.ajax.AjaxResponse = function(data, status, xhr) {
var errorName = $(data.documentElement).find("error-name").text();
if (errorName == 'javax.faces.application.ViewExpiredException') {
alert('View has expired, redirection will follow');
window.location.reload();
} else {
originalPrimeFacesAjaxResponseFunction.apply(this, arguments);
}
};
No 2 new classes on server, no faces-config.xml changes, this is what I love in programming.
I have an exception handling in my application very similar to this solution:
http://www.devcurry.com/2012/06/aspnet-mvc-handling-exceptions-and-404.html
There is a nasty bug in my app where it is possible for the sql to deadlock with an other process. This happens rarely (1-2 requests fail daily because of this), but it still happens.
How can I automatically refresh the page on sql deadlock (and hide the error this way from the end user on get requests)?
Can I do it in the Application_Error function? Or in the overridden OnException in HandleErrorAttribute?
EDIT:
I mocked up some code in the BaseController I created:
protected override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
SqlException sex = ex as SqlException;
if (sex != null && sex.Number == 1205)
{
Log.Error("Transaction deadlocked with the following exception:");
Log.Exception(sex);
//I need to write the logic that refreshes the page here.
}
else
{
Log.Error("Application error with the following exception:");
Log.Exception(ex);
}
base.OnException(filterContext);
}
I need help on the refresh part.
I would deal with it by overriding the OnException() method of the controller. It would be best if you inherit all your controllers from a custom base one in which the override is done to maintain uniformity and DRYness of the solution.
just add bellow code, before base.OnException(filterContext);
// Stop any other exception handlers from running
filterContext.ExceptionHandled = true;
I'm working on a team-project and I am in the following situation:
I created my own Exception class, and I want all the thrown exceptions of type myException to be handled and automatically redirected to the Error view where I would nicely display the error, which is ok to do. This is what I added in my Web.config:
<customErrors mode="On" defaultRedirect="Error" />
The issue is I want all the rest of the exceptions to be thrown normally, seeing all the information about it, including the stack trace, the source file and the line error, which would be really good for the team-project.
I've tried the [HandleError(ExceptionType=typeof(myException)], but it is no use.
I also tried to override the OnException function of the controller and if the exception is not myException then i would throw it again, but i still get in the Error view.
protected override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() != typeof(myException)) {
throw filterContext.Exception;
}
base.OnException(filterContext);
}
Any idea which could work?
Thanks.
You may get the result you want by leaving custom errors Off (so that for all the errors you get the stack trace displayed), and redirecting the exceptions you want to the controller/view you need (so that a friendly-looking page will be displayed).
You could define a base controller for all your controllers, and override its OnException method with something like below:
if (filterContext.Exception.GetType() == typeof(YourCustomException))
{
filterContext.ExceptionHandled = true;
filterContext.Result = RedirectToAction("ActionName", "ControllerName", new { customMessage = "You may want to pass a custom error message, or any other parameters here"});
}
else
{
base.OnException(filterContext);
}
I'm recieving the following error message,
A public action method 'RenderMenu'
was not found on controller
'Web.Controllers.SiteController'.
However this action DOES exist and the controller does exist (As it work everywhere on the site) I looked at the inner exception.
Execution of the child request failed.
Please examine the InnerException for
more information.
(This is the inner exception...)
Stack Trace
at
System.Web.Mvc.HttpHandlerUtil.ServerExecuteHttpHandlerWrapper.Wrap[TResult](Func`1
func) at
System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler
handler, TextWriter writer, Boolean
preserveForm, Boolean setPreviousPage,
VirtualPath path, VirtualPath
filePath, String physPath, Exception
error, String queryStringOverride)
Now, we have a website set-up with a dynamic menu system so we are using RenderAction() on a generic controller to build this menu system up.
<% Html.RenderAction("RenderMenu", "Site"); %>
This call is made from the MasterPage and it works fine until there was a validation error like so,
[HttpPost]
public ActionResult Register(UserModel UserToAdd)
{
if(!ModelState.IsValid)
{
return View(UserToAdd);
}
//Run some validation
if (_UserService.DoesEmailExist(UserToAdd.EMail))
{
TempData["error"] = "Email Address Already in use!";
return View(UserToAdd);
}
//Add the user
TempData["info"] = "User Added - " + UserO.ID;
return View("Success");
}
It works fine when there this is a new user, but if someone enters an email that already exist we get the above error. THis RenderAction Method works all over the site (This is the first form we have added)
Any suggestions?
Fixed:
The RenderAction() Method is below
[HttpGet]
public ActionResult RenderMenu()
{
//Do Stuff
}
Removing the HttpGet Attribute has resolved the issue.
public ActionResult RenderMenu()
{
//Do Stuff
}
Would love to know why?
This is because your parent request is an [HttpPost], and the child request operates in the same verb as the parent. If your method is marked as [HttpGet], it will not respond to [HttpPost] requests. Hitting the action directly through your browser works because that is a GET. Hitting the action as a child action in the context of a POST will not work.