Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controller to Return an Error Message Instead of a View?

Tags:

asp.net-mvc

I'm fairly new to ASP.NET MVC and am not sure how best to handle the following situation.

A method in my controller needs to load some data based on an ID argument. Under normal circumstances, this ID argument will be set to a valid ID of an entity within my database. I construct some data and place it in ViewBag, which the view uses to render the page.

However, I would like some basic error handling just in case the ID argument is not valid. Although I could write a bunch of error handling code in the view, it would be much simpler not to display the view if there is a major misuse or malfunction of the site.

Is there a way the controller could simply return a "Item not found" string or something like that, and display that rather than the normal view? Or perhaps someone can suggest a better idea?

like image 614
Jonathan Wood Avatar asked Dec 07 '11 06:12

Jonathan Wood


People also ask

How do you return a message in controller?

We can return the custom message from controller by throwing exception and handling it at client side using the ActionFailure event of the Grid. Grid Rendering Code. 2. Handle the returned message in the ActionFailure event of the Grid.

Can I return action result instead of view result?

When you set Action's return type ActionResult , you can return any subtype of it e.g Json,PartialView,View,RedirectToAction.

How do I bypass a view from a controller?

The other way of passing the data from Controller to View can be by passing an object of the model class to the View. Erase the code of ViewData and pass the object of model class in return view. Import the binding object of model class at the top of Index View and access the properties by @Model.


4 Answers

if (itemId == null)
{
    return Content("Item not found");
}

Or if you want to return an HTTP 404 instead:

throw new HttpException(404, "Item Not Found");
like image 191
Chris Fulstow Avatar answered Sep 21 '22 21:09

Chris Fulstow


In case fetching model from database (as described in Darin's answer) is somehow complicated and cannot be made generic, this is how I deal with resources not found.

Implement your base controller

    public abstract class MyBaseController : Controller
    {
        [NonAction]
        protected virtual void EnsureResourceFound(object resource)
        {
            if (resource == null)
            {
                HttpStatusCode statusCode = HttpStatusCode.NotFound;
                throw new HttpException((int)statusCode, statusCode.ToString());
            }
        }
    }

And in derived controllers - use that method

    [HttpGet]
    public virtual ActionResult Edit(int id)
    {
        SomeModel model = null;
        EnsureResourceFound(model = _someModelService.Get(id));
        return View(question);
    }

And what will you do with resulting http exception, return custom view, log the error, depends on configured HandleErrorAttribute.

like image 34
archil Avatar answered Sep 18 '22 21:09

archil


You can do it by simply throwing exception from the Controller.

You need to write following code and need to add ErrorController and it's respective view.

Global.asax

Exception exception = Server.GetLastError();

Response.Clear();

HttpException httpException = exception as HttpException;

//Add controller name
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");

//we will add controller's action name 
routeData.Values.Add("action", "Index");

// Pass exception details to the target error View.
routeData.Values.Add("error", exception.Message);

// Clear the error on server.
Server.ClearError();

// Call target Controller and pass the routeData.
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));


//and throw the exception from the Controller ny simply writing
throw new Exception()

ErrorController:

public class ErrorController : BaseController
    {
        #region Function Declaration
        /// <summary>
        /// Shows application error
        /// </summary>
        /// <param name="error">error description</param>
        public ActionResult Index(string error)
        {
            ViewBag.Description = Resources.ErrorMessage.GeneralError;
            if (string.IsNullOrEmpty(error))
            {
                ViewBag.DetailError = error;
            }
            else
            {
                ViewBag.DetailError = string.Empty;
            }

            return View("ErrorIndex");
        }
        #endregion
    }

Another Approach :

If you want to write a view for a particular error than you have to write followig code. You have to just add DivByZero view.

    [HandleError(View = "DivByZero", ExceptionType = typeof(System.DivideByZeroException))]
    public ActionResult About()
    {
        List<Alok> alokList = new List<Alok>();
        var al = from aa in alokList.Distinct()
                 select aa;

        ViewData["Errorname"] = "Divide By Zero Exception";
        ViewBag.ErrorName = "Divide By Zero Exception";
        //throw new DivideByZeroException();

        return View();
    }

DivByZero View :

@model System.Web.Mvc.HandleErrorInfo
@{
    ViewBag.Title = "Error";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@*<h2>
    @ViewData["Errorname"].ToString()
    @ViewBag.ErrorName</h2>*@
<p>
    Controller : @Model.ControllerName
</p>
<p>
    Action : @Model.ActionName
</p>
<p>
    Error Message : @Model.Exception
</p>
like image 23
alok_dida Avatar answered Sep 18 '22 21:09

alok_dida


public ActionResult Foo(int id)
{
    MyModel model = ...
    if (model == null)
    {
        return HttpNotFound();
    }
    return View(model);
}

And since writing this same code over and over again in your actions could quickly become boring a better solution is to write a custom model binder which will fetch the model from the database and if not found will simply throw a new HttpException and set the status code to 404. Then your controller action will simply look like this:

public ActionResult Foo(MyModel model)
{
    return View(model);
}

and the model binder itself:

public class MyModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var id = bindingContext.ValueProvider.GetValue("id");
        if (id == null)
        {
            throw new HttpException(404, "Not found");
        }

        MyModel model = FetchTheModel(id.AttemptedValue);
        if (model == null)
        {
            throw new HttpException(404, "Not found");
        }

        return model;
    }

    private MyModel FetchTheModel(string id)
    {
        throw new NotImplementedException();
    }
}

This model binder could obviously be made more generic.

like image 44
Darin Dimitrov Avatar answered Sep 21 '22 21:09

Darin Dimitrov