0

I'm learning MVC and am using the following example from the sample application to show details of a book. In the code I see this:

// GET: Book/Details/5
public ActionResult Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Book book = db.Books.Find(id);
    if (book == null)
    {
        return HttpNotFound();
    }
    return View(book);
}

Should a book not be found, the code however is forcing the browser to display HTTP error codes. How do we return a more friendly message that is based on the object context (and not simply a generic (e.g. 404) page)?

The View CSHTML code is fairly straightforward supposing that a book has been found:

@model BookStore.Models.Book
@{
    ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
    <h4>Book</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Name)
        </dt>
    ... rest removed for brevity
</div>

How do we swap this for something like the following:

<h2>No book found</h2>
<p>That book was not found.  Check the ISBN code, or contact an awesome librarian</p>
EvilDr
  • 8,943
  • 14
  • 73
  • 133
  • 1
    Rather than `return HttpNotFound();` you can always `return View("NoBook");` where `NoBook.cshtml` contains the html above –  Aug 30 '14 at 08:55
  • What if I have a number of issues that I need to detect, e.g. No book, no permission to view book, no ISBN provided by user, etc. Does that warrant a new view for each file? That's a lot of views/files! – EvilDr Aug 30 '14 at 09:08
  • 1
    Well you could always have a generic 'error' view and pass it some properties (say 'title' and 'message') via a view model (or ViewBag) –  Aug 30 '14 at 09:14
  • 1
    Define an ErrorController and Action methods for each error and show appropriate view based on kind of error. Check this http://stackoverflow.com/questions/13905164/how-to-make-custom-error-pages-work-in-asp-net-mvc-4 – malkam Aug 30 '14 at 10:12

1 Answers1

1

How about something like this?

public class ErrorViewModel
{
    public string Summary { get; set; }
    public string Description { get; set; }
}

Then:

if (book == null)
{
    return View("Error", new ErrorViewModel
        {
            Summary = "No book found",
            Description = "That book was not found.  Check the ISBN code, or contact an awesome librarian"
        }
}

Then you can create a single generic error view and pass any error messages to it that you need. If you want aggregated errors you can always modify the model to use collections instead of single strings (or pass a collection of ErrorViewModel).

Ant P
  • 24,820
  • 5
  • 68
  • 105
  • Thanks Ant, that seems straightforward. Could you please advise how this is better/different from using `ModelState.AddModelError`? I assume that `AddModelError` simply adds an error onto the "book" model and shows it in the "book" view? – EvilDr Sep 01 '14 at 11:29
  • 1
    @EvilDr Yep - Model errors are generally intended as metadata on an existing view model for presenting validation errors in the same view, while what you're after is a custom error view, so the error effectively becomes a model in its own right. You could still argue that model errors are a valid approach if your errors pertain directly to model properties but this seems contrived to me if you're not actually interested in the values of those properties. – Ant P Sep 01 '14 at 14:35
  • Great stuff, thanks. Implemented your suggestion earlier today and it works great. Keep up the good work. – EvilDr Sep 01 '14 at 15:22