I have an action like shown below. In GetAvailableBookList, I get the list and if there is not any available book redirect to a message page. But in action part code continues to execute and gets an exception and I find myself in error page.
I don't want to use return RedirectToAction or something like that because there are a lot of places where we use this redirect logic in our application.
public ActionResult ActionName()
{
List<BookType> bookList = GetAvailableBookList();
// some code
return View("RelatedView");
}
private List<BookType> GetAvailableBookList()
{
....
list = GetList();
if(list.Count == 0)
{
System.Web.HttpContext.Current.Response.Redirect(messagePageUrl, true);
}
else return list;
}
Unfortunately, Response.Redirect() isn't really friendly with ASP.NET MVC. My rule of thumb is if it comes from HttpContext I don't want to touch it in the controller (of course there are many exceptions to that rule) -- especially since it improves testability.
My suggestion is to use RedirectToAction, but since you don't want to repeat code you can do it in such a way that you don't have to repeat code (although in this case I don't see a problem with repeating code).
public ActionResult LoadBookListAndContinue(
Func<List<BookType>, ActionResult> continuation)
{
var list = LoadBooklist();
if(list.Any())
{
return action(continuation);
}
return new RedirectResult(messagePageUrl);
}
// in your controller
public ActionResult ActionName()
{
return LoadBookListAndContinue(
list => {
// some code
return View("RelatedView");
});
}
Is it pretty? No, but it works better than the Redirect exception.
Use
return RedirectToAction("NoListAvailable");
if you have a specific action you would like to execute. The NoListAvailable action can return a view indicating the problem.
Alternatively, you could return the view directly
return View("NoListAvailable");
The exception you are getting is probably ThreadAbortException and this is something you cannot avoid unless you allow the thread to continue (2nd argument in Response.Redirect).
On a side note your current solution is generally flawed. You should use RedirectToAction in each action when your method returns an empty list.
Throwing a specific exception and redirect where you catch it may be solution
Try to write
System.Web.HttpContext.Current.Response.Redirect(messagePageUrl, false);
Related
I have a MVC method that currently returns an ActionResult - I have had to make some changes and based on the business logic I want to do a response.redirect instead.
So I want to do something like this:
public ActionResult Index(CountryHomePageType currentPage)
{
if (someVar = true)
{
return View();
}
else
{
Response.redirect("www.website.com")
}
}
but I can't becuase Resonse.Redirect is not a ActionResult....
How can I get round this?
If you are redirecting outside of your current mvc application you can use
return Redirect("<your external url>"); // like "https://www.google.com"
if you want to redirecto back you your homepage you can use
return RedirectToAction("Index", "Home");
assuming you are using the default mvc setup
You might want to also look at ActionFilters if you are making this check in multiple places.
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'm not sure if this is the correct way to go about the problem I need to solve... however in an OnActionExecuting action filter that I have created, I set a cookie with various values. One of these values is used to determine whether the user is visiting the website for the very first time. If they are a new visitor then I set the ViewBag with some data so that I can display this within my view.
The problem I have is that in some of my controller actions I perform a RedirectToAction. The result is OnActionExecuting is fired twice, once for the original action and then a second time when it fires the new action.
<HttpGet()>
Function Index(ByVal PageID As String) As ActionResult
Dim wo As WebPage = Nothing
Try
wp = WebPages.GetWebPage(PageID)
Catch sqlex As SqlException
Throw
Catch ex As Exception
Return RedirectToAction("Index", New With {.PageID = "Home"})
End If
End Try
Return View("WebPage", wp)
End Function
This is a typical example. I have a data driven website that gets a webpage from the database based on the PageID specified. If the page cannot be found in the database I redirect the user to the home page.
Is it possible to prevent the double firing in anyway or is there a better way to set a cookie? The action filter is used on multiple controllers.
Had the same issue. Resolved by overriding property AllowMultiple:
public override bool AllowMultiple { get { return false; } }
public override void OnActionExecuting(HttpActionContext actionContext)
{
//your logic here
base.OnActionExecuting(actionContext);
}
You can save some flag value into TempData collection of controller on first executing and if this value presented, skip filter logic:
if (filterContext.Controller.TempData["MyActionFilterAttribute_OnActionExecuting"] == null)
{
filterContext.Controller.TempData["MyActionFilterAttribute_OnActionExecuting"] = true;
}
You could return the actual action instead of redirecting to the new action. That way, you dont cause an http-request, thereby not triggering the onactionexecuting (i believe)
Old question, but I just dealt with this so I thought I'd throw in my answer. After some investigating I disovered this was only happening on endpoints that returned a view (i.e. return View()). The only endpoints that had multiple OnActionExecuting fired were HTML views that were composed of partial views (i.e. return PartialView(...)), so a single request was "executing" multiple times.
I was applying my ActionFilterAttribute globally to all endpoints, which was working correctly on all other endpoints except for the view endpoints I just described. The solution was to create an additional attribute applied conditionally to the partial view endpoints.
// Used specifically to ignore the GlobalFilterAttribute filter on an endpoint
public class IgnoreGlobalFilterAttribute : Attribute { }
public class GlobalFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Does not apply to endpoints decorated with Ignore attribute
if (!filterContext.ActionDescriptor.GetCustomAttributes(typeof(IgnoreGlobalFilterAttribute), false).Any())
{
// ... attribute logic here
}
}
}
And then on my partial view endpoints
[HttpGet]
[AllowAnonymous]
[IgnoreGlobalFilter] //HERE this keeps the attribute from firing again
public ActionResult GetPartialView()
{
// partial view logic
return PartialView();
}
I want to be able to do semething in the likes of:
#ManagedBean
class MyBackingBean {
public void processRequest() {
String viewName;
if (condition1)
viewName = "page1";
else if (condition2)
viewName = "pagexx";
invokeAndRenderXHTML(viewName);
}
}
thanks
Just in case anyone stumbles upon this old question:
you can programatically invoke navigation handler like this
FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(), null, "YOUR_NAVIGATION_CASE_DEFINED_IN_FACES_CONFIG");
JSF provides programmatic navigation by default. You do NOT need a third party library to effect navigation. To use JSF navigation, your method should simply return the name of the view you're trying to access and it'll navigate to that page. You could also include an optional redirect parameter to the return value to instruct the JSF context to redirect the response in full to the destination view. For your needs, just change processRequest to
public String processRequest() {
// String viewName unnecessary
if (condition1) {
return "page1";
}
else if (condition2) {
return = "pagexx";
}
return null;
// invokeAndRenderXHTML(viewName) becomes unnecessary
}
If you choose to have the redirect option like I indicated above just change the return String to
return "page1?faces-redirect=true"
the faces-redirect=true is the parameter that does the redirect magic
as you need a request to trigger a response i think there are two ways to go. either polling or push.
www.primefaces.org/showcase/ui/poll.jsf
www.primefaces.org/showcase/push/index.jsf
Check out primefaces showcase of both to get an understanding on which suits your need best. Even if you dont use the library you can look at the sources to get a better understanding of how to do this in jsf2
I am trying to clean up my code. I have a grid screen that gets refreshed with the following:
public ActionResult Details(string pk)
{
IEnumerable<ContentDetail> model = null;
try
{
model = _content.Details(pk);
if (model.Count() > 0)
{
return PartialView(getView(pk) + "Details", model);
}
}
catch (Exception e)
{
log(e);
}
return Content("No records found");
}
All the rest of my code uses json and I would like to return something like this:
public JsonResult JsonDetails(string pk)
But what should I do about the PartialView? I can't find anything about how to do this. Also is there any advantage / disadvantage to doing this? I was thinking if the code fails then I would return something like the following which the new ASP MVC4 code uses:
return Json(new { errors = GetErrorsFromModelState() });
Can someone help me with this? I'm looking to find any suggestions in particular for MVC4.
I've previously used the approach outlined in this answer, which was successful for me.
I can't think of any disadvantages of returning HTML within JSON, although the payload would likely be much larger than if you were returning data alone.
An alternative would be to return the model as JSON, and use a templating library, e.g. Handlebars.js, to generate the markup on the client. This is a common approach in single page applications.
Your idea around returning errors is good. GetErrorsFromModelState is only used where there are validation errors in the model state - in the example above, you're not performing any validation that would require you to use this method. So you'd probably want to output some friendly message within your catch-block, e.g.
try
{
...
}
catch (Exception e)
{
log(e);
return Json(new { errors = "An error occurred, please try again later" });
}
I've used the code from this answer before and it worked out great for me, I haven't tried returning specific errors, but it is possible to access controller.ModelState to check.