MVC Return view from another method - asp.net-mvc

i am unable to end response in case of some condition
eg below (in Upload Action Method), if Logerror method invoked i just want to return view(browser) without further action. i.e return from Upload Action Method.
Plase find modified question what i am trying to achive,
In case of error i want to return view by stopping all further opeartion
public ActionResult Index()
{
return View();
}
public ActionResult Upload()
{
int i=1;
DoSomethingFirst();
//if LogError i dont want execute code below, rather it should end responce
//should not reach here
string s="This should not be executed in case of LogError()";
return View("Index");
}
public void DoSomethingFirst()
{
try{
DoSomethingSecond();
}
catch(exception ex)
{
LogError();
}
}
public void DoSomethingSecond()
{
try{
DoSomethingThird();
}
catch(exception ex)
{
LogError();
}
}
public void DoSomethingThird()
{
try{
DoSomethingother();
}
catch(exception ex)
{
LogError();
}
}
private LogError()
{
Viewbag.Error="Error details";
return View("Index");
}

This doesn't return a result from the current method:
DoSomething();
But this does:
return DoSomething();
If you want to end execution of the current method, you need to do something which exits the method. Basically, either return from the method or throw an exception. Since DoSomething returns a result, presumably you want to return that result. So simply add a return statement when invoking the method.
i tried wit RedirectToAction("Index");
Same issue. You'd need to return the result:
return RedirectToAction("Index");
Edit: Based on your edit to the question, the overall concept still remains. Focusing on this part of your code here:
var s = DoSomethingFirst();
//if true i dont want execute code below, rather it should end responce
//should not reach here
In order to exit the method, any method in C#, you need to either return or throw. So the first question is... Which do you want to do here? If you want to return a redirect, for example, then return a redirect:
return RedirectToAction("SomeAction");
If you want to return the default view, return that:
return View();
If you want to throw an exception:
throw SomeException("Some Message");
The choice is yours. You just need to define:
What you want this method to return or throw under this condition.
How will you know the condition.
For that second point, your code comment says:
//if true ...
Does this mean DoSomethingFirst() returns a bool indicating success or failure? Then that would be a simple if statement:
if (!DoSomethingFirst())
return View();
Another Edit: Based on your comment below:
Inside LogError mehod called by any child method in action method, i want to update view with error message and end the operation without further operation
How will your Update method know that something it invoked internally called LogError()? What information does DoSomethingFirst() return to indicate this fact? Currently it doesn't. Your various DoSomething methods are all swallowing exceptions, which means they are internally handling exceptions so that consuming code doesn't know about them.
If you want consuming code to know about an exception, re-throw that exception. For example:
public void DoSomethingFirst()
{
try
{
DoSomethingSecond();
}
catch(exception ex)
{
LogError();
throw; // <-- this will re-throw ex without modifying it
}
}
This returns information from DoSomethingFirst(), specifically the fact that an error occurred. Your consuming code can then check for that error:
try
{
DoSomethingFirst();
}
catch (Exception ex)
{
// You should *probably* do something with ex too. So far all of your "logging" has been ignoring the actual error.
return View();
}
Regardless of the structure you build, the basics don't change. In order for consuming code to know something about the code it invokes, that invoked code has to expose that information. In order to end execution of a method, you have to either return or throw. Don't hide exceptions from consuming code if you want consuming code to respond to those exceptions.

Related

Template method for MVC Controller

I have a lot of controllers in an application.
Controllers called from UI with JS/ajax.
Almost all of them use similar "template". Please see the code sample.
Check income model for null.
Check model is valid.
Try catch with action.
Difference only in error messages, names and which injected helper used.
Is it possible to reduce repeat code?
Some kind of DRY?
How to achieve this? Template method, delegates, use MVC specific things?
[HttpPost]
public async Task<IActionResult> TestMethod([FromBody] TestModel model)
{
if (null == model)
{
return StatusCode((int)HttpStatusCode.BadRequest, "Empty model provided");
}
if (false == ModelState.IsValid)
{
var message = "Not all parameters provided correctly.";
_logger.WriteWithCallerAndMethodName(LogLevel.Debug, nameof(TestController), nameof(TestMethod), message);
return StatusCode((int)HttpStatusCode.BadRequest, message);
}
try
{
var result = await _testHelper.CreateDataAsync(model);
return Ok(result);
// return PartialView("_TestPart", result);
}
catch (Exception ex)
{
var message = $"Can't retrieve data. Error: ({ex.Message})";
_logger.WriteWithCallerAndMethodName(ex, nameof(TestController), nameof(TestMethod), message);
return StatusCode((int)HttpStatusCode.InternalServerError, message);
}
}

Return to original view from MVC action filter

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.

Error propagation in controllers MVC

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.

Catching Exceptions in the Controller

It seems like if an Exception occurs inside a controller, the view-engine won't go to the intended view, even if the "exception" is caught inside a try-catch block?
or I'm missing something here:
public ActionResult MyController(int? param1, DateTime? param2)
{
MyModel model = new MyModel();
try
{
model = new MyModel();
//AN ERROR HAPPENS HERE (so the code goes to catch the Exception):
model.Connection.Initialize();
}
catch (Exception ex)
{
ViewBag.ErrorMessage = ex.Message;
}
//when I put a break point I get to this following line, however, "MyView" is never displayed!?
return PartialView("MyView", model);
}
You might be getting another exception afterwards, somewhere in your view. Look at the stack trace that displays on the browser page and fix that.

Asp.Net MVC3 Redirect

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);

Resources