Similar questions have been asked over and over but for some reason none of the things I have tried have worked. I have an ajax application and when the user request an invalid url I would like to return a JsonResult that looks something like this:
[ error: true, status: 404, message: 'The requested page could not be found... or some other message... ']
I don't want the user to just be redirected to the error page defined in the web.config file as that would take them out of the ajax application.
I have tried the following things:
Custom HandleErrorAttribute - Turns out these only handle 500 errors due to exceptions thrown by my code
Custom FilterAttribute that extends FilterAttribute and implements IExceptionFilter - Same issue as before
Override HandleUnknownAction in the Base Controller - Couldn't figure out how to return a JsonResult from the method
Added a catch all route - My other routes are being used before the catch all route is found
Any thoughts would be appreciate.
•Override HandleUnknownAction in the Base Controller - Couldn't figure out how to return a JsonResult from the method
new JsonResult()
{
Data = your_404_data,
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
}.ExecuteResult(ControllerContext);
Updated to include the JsonRequestBehavior - which you will need.
For requests which don't match an existing controller or resource you have two choices - to ditch the standard catch-all route "{controller}/{action}/{id}" and replace it with one that sends the request to a fixed controller that returns this 404-like response that you want.
Or use standard Asp.Net error handling to redirect to a route that will do the same thing. Personally I prefer this solution because it means that you don't have to code loads of routes into your global.
You could give https://github.com/Buildstarted/Errlusion a whirl and create whatever handling you want for a 404. You can probably return a special ajax message if it's an ajax request or return html if it's a standard web browser request if you'd like.
Related
One of my POST actions is throwing HTTP 404 error. I've been at it for last couple of hours and have no idea what's wrong! All other actions in the same controller work just fine. I'm pretty sure spellings are correct. When in debug mode, this method is never hit (well, obviously I guess). Please help.
Here's the action method -
public class LookupTableController : Controller
{
[HttpPost]
public ActionResult AjaxLookupTableCRUDDelete(int lookupId)
{
LookupTable lookuptable = db.LookupTables.Find(lookupId);
if (lookuptable != null)
{
db.LookupTables.Remove(lookuptable);
db.SaveChanges();
}
return Index();
}
No matter how I try to access it, it throws the 404 error!
I tried directly on the browser - http://localhost:60328/LookupTable/AjaxLookupTableCRUDDelete/?lookupId=111 ,
It shows -
>Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its >dependencies) could have been removed, had its name changed, or is temporarily >unavailable. Please review the following URL and make sure that it is spelled >correctly.
Requested URL: /LookupTable/AjaxLookupTableCRUDDelete/
Fiddler -
Headers - POST /LookupTable/AjaxLookupTableCRUDDelete?lookupId=13 HTTP/1.1
[HttpException]: A public action method 'AjaxLookupTableCRUDDelete' was >not found on controller 'GBPReconTool.Controllers.LookupTableController'.
Routing is standard -
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Resolved - Thanks guys, it turned out to be the GET vs POST problem. - When I first compiled it, it did not have a HTTPGet or HTTPPost attribute. I'm not sure how ASP.NET MVC4 determines what it is supposed to be. In any case, explicit POST ajax calls to that failed and started testing it directly in the browser. The browser was apparently posting it as a GET. I'm still not clear on how this is determined but I guess adding the HTTPPost attribute to the controller is what finally fixed it. Guess I'll be reading up on how ASP.NET and browsers determine whether an action is GET or POST.
Try using this ...
[HttpPost]
public ActionResult AjaxLookupTableCRUDDelete(int id)
{
LookupTable lookuptable = db.LookupTables.Find(id);
if (lookuptable != null)
{
db.LookupTables.Remove(lookuptable);
db.SaveChanges();
}
return Index();
}
then try using ...
POST http://localhost:60328/LookupTable/AjaxLookupTableCRUDDelete/111
As others have stated, you need to make sure that the request is being sent as a POST.
Another thing you might want to check is that ContentType is set correctly in Fiddler.
Content-Type: application/json; charset=utf-8
change the action signature to the following:
public ActionResult AjaxLookupTableCRUDDelete(int? id)
{
}
The idea here is to have the action variable name match what you already have in your routes.
the POST url will need to look like this:
http://localhost:60328/LookupTable/AjaxLookupTableCRUDDelete/111
you don't need to supply the variable name since mvc will gleam it from the url based on your route
I know this sounds simple but:
Make sure you saved the class file
Make sure you built the project
Then use Chrome or FireFox development tools (F12) and look at the Network tab. You can get a look at the actual url and type (POST, GET) that is being passed to your application. You will also see any querystring parameters and form data as well.
Generally if there's something wrong it will become apparent by this point.
FYI - You can also use Fiddler2 to get a look at the actual http request as well.
From http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
HttpResponseException
What happens if a Web API controller throws an exception? By default,
most exceptions are translated into an HTTP response with status code
500, Internal Server Error.
The HttpResponseException type is a special case. This exception
returns any HTTP status code that you specify in the constructor of
the exception.
Except that it doesn't. Fiddler shows me a 500 is returned.
However, HttpException seems to do what that article says.
Is the documentation wrong or am I missing something?
UPDATE
While typing that out, I had an idea. I tried both from two controllers, an ApiController and a standard MVC controller.
The two exception work in reverse to each other depending on the type of controller they're thrown from.
Use HttpResponseException to return a proper HTTP code from an API
controller.
Use HttpException to return a proper HTTP code from an
MVC controller.
[Moved my update to an answer]
While typing that out, I had an idea. I tried both from two controllers, an ApiController and a standard MVC controller.
The two exception work in reverse to each other depending on the type of controller they're thrown from.
Use HttpResponseException to return a proper HTTP code from an API controller.
Use HttpException to return a proper HTTP code from an MVC controller.
I have action methods that do things like this:
productTable.AddOrUpdate(viewModel.Account);
I don't currently test for if it works. Is there an easy way I can check if this works and if it doesn't then give an error message and return to my view?
This is not strictly related to MVC, I think it's a more general issue on how to design code properly to managed unexpected situations (or expected errors generated by user input) and giving appropriate feedback.
For unexpected behaviours we talk about Exception handling. You put your code in a try/catch block, and handle the exception in the catch.
For errors that may be generated by user input we're talking about validation. It means you check your object against a set of rules, and if one or more does not pass, you return a set of errors to your view and display it to the user.
Now for some MVC specific on how to do this. Let's have a bit of sample code:
[HttpPost]
[ModelStateToTempData]
public ActionResult SaveMyObject(MyObject myObject)
{
try
{
if (ModelState.IsValid)
{
MyRepository.Save(myObject);
return RedirectToAction("Success");
}
return RedirectToAction("MyForm");
}
catch (Exception ex)
{
//handle/log your exception as you see fit
ModelState.AddModelError("Name", "Message");
return RedirectToAction("MyForm");
}
}
In this sample we're assuming a few things:
We're following the PRG pattern (you can google for it) and implementing it using the ModelStateToTempData attribute from MvcContrib;
We're using a validation method that kicks in during modelbinding (like dataannotations)
This is a sample method that will receive the post of your form. The data comes from the modelbinder already validated. If the data is valid, you store it and redirect the user to the success page.
If the data is not valid, you redirect him back to the actionmethod that diplays the form. Careful here: do not render the view directly from here: see the PRG pattern for a full explanation of this.
The whole thing is in a try/catch block so if something fails, you can trap/handle the error, and then add a custom error message to the page, then redirect the user to the actionmethod that renders the form and display the error to him.
I have a JSON method that accepts a GET request and returns a JSON object (not array). I'm aware of JSON Hijacking and the implications. I've read the Phil Haack post. The problem is that the method works 98% of the time for GET and POST. The other times I'm recording this error:
This request has been blocked because sensitive information could be disclosed to
third party web sites when this is used in a GET request. To allow GET requests, set
JsonRequestBehavior to AllowGet.
My method is simple and takes a single integer parameter...
[Authorize]
public ActionResult MyMediaJSON(int? id) {
<get data & return result>
}
What conditions trigger the message? What should look for as I debug this?
I've just looked at the MVC source code and it do not add up with what you are saying in your question.
To me it looks like JsonRequestBehavior.DenyGet is used for all JSON results per default. Hence you should get the error message each time you try to return JSON from a controller using a GET request (without specifying JsonRequestBehavior.AllowGet).
The actual control is done in JsonResult.ExecuteResult and looks like:
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
}
What conditions trigger the message? What should look for as I debug this?
Any actions that are getting invoked through GET that returns JsonResult without specifying JsonRequestBehavior.AllowGet. (the Json method in the controller uses JsonResult)
What options do I have for error handling in ASP.NET MVC?
Do I call GetLastError on the error page and send out the email or log it?
Why not an error handling filter?
In your Global.asax you can implement something like the following:
protected void Application_Error()
{
Exception lastException = Server.GetLastError();
GetLogger().Fatal(lastException); // your custom loggin code
}
McvContrib has "Rescue" attributes -- an idea borrowed from Monorail. You can do like so:
[Rescue("default", AutoLocate = true)]
public class MyController : ConventionController
You then create rescue views based on convention like so:
Views/Shared/Rescues/Exception.aspx
Views/Shared/Rescues/MyCustomException.aspx
Etc. When an unhandled exception occurs in a controller, the rescue filter will render the page with the same name as the exception type. If no exact match is found it renders Exception.aspx. (I think maybe it works up the inheritence hierarchy till if finds a match.)
These rescue views implement ViewPage<HandleErrorInfo> so in the view page you access the exception as ViewData.Model.Exception. This is the one place where I put code in the codebehind -- to log the exception -- because it makes for a nice application boundary.
If you don't want to use MvcContrib, you can study the code to implement your own solution.