MVC3 HandleError not working with ASP.NET MVC3 EmptyTemplate project - asp.net-mvc-3

In Visual Studio 2012 I created a new ASP.NET MVC3 Project using the Empty template. Then I created a HomeController with the following ActionResult:
public ActionResult Index()
{
throw new Exception("oops!");
ViewBag.Message = "hello world";
return View();
}
Next, I added a simple view for my HomeController:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
and inserted the following in the {root}/web.config:
<customErrors mode="On"/>
Finally, I modified /Views/Shared/Error.cshtml to look like:
#model System.Web.Mvc.HandleErrorInfo
#{
//Layout = null;
ViewBag.Title = "Error";
}
<h2>
Sorry, an error occurred while processing your request.
</h2>
When I run the project I get:
500 Internal Server Error. The website cannot display the page...
Then I decided to create another ASP.NET MVC3 Project using the Internet template. Inside the HomeController I throw an Exception exactly like I did above and I turned on customErrors again in the Web.config. When I run the project I get the correct results:
Sorry, an error occurred while processing your request.
What could I be missing between the two projects?
I've went line by line through the Web.config and didn't see any differences. The Global.asax file was untouched with both projects.

I can't believe this...
Thanks to Cosmin Onea comment below he had me inherit from HandleErrorAttribute and OnException was being fired as it should. After making that change I ran across this SO answer and tested my page in Chrome where I see the appropriate message that has been there the whole time.
Although I'm still confused why one runs just fine in IE and the other application doesn't.

You need to register the HandleErrorAttribute as a global filter.
In your Global.asax, in the Application_Start event register the filter:
protected void Application_Start()
{
...
RegisterGlobalFilters(GlobalFilters.Filters);
....
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
You can also apply the filter to a controller or an action directly. What the filter does, in case of an exception, it sets the Result on the filterContext to point to your Error view.

Related

asp.net webapi 2 attribute routing not working

I have visual studio 2012 installed with mvc4 using .net framework 4.5. Now I want to use webapi2 with attribute writing and i want my hlep page show all the endpoints properly.
In my solution i added a new mvc4 base emtpy project and using nuget i upgraded to mvc5 and then i have installed webapi2 packages. lastly i have installed help package for webapi2.
now when i use routeprefix I cant see any content on help page and when i try to access my webapi endpoint in browsers it throws following error.
http://expressiis.com/api/v1/
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://expressiis.com/api/v1/'.
</Message>
<MessageDetail>
No type was found that matches the controller named 'v1'.
</MessageDetail>
</Error>
namespace WebApi.Controllers
{
[RoutePrefix("api/v1")]
public class SubscribersController : ApiController
{
// GET api/<controller>
[Route("")]
[HttpGet]
public IQueryable<string> Get()
{
return new string[] { "value1", "value2" }.AsQueryable();
}
}
}
Based on your information, it looks like you are not calling the httpConfig.MapHttpAttributeRoutes() (Make sure to call this before any traditional routing registrations)
Since you haven't called MapHttpAttributeRoutes, your request seems to be matching a traditional route, for example, like api/{controller}. This will not work because routes matching traditional routes will never see controllers/actions decorated with attribute routes.
A problem I ran into was related to the ordering in Application_Start(). Note the order of Web API configuraton below:
This does NOT work
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
This does work
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I had this problem too and after a long search I realized that I was using System.Web.Mvc.RouteAttribute instead of System.Web.Http.RouteAttribute
After correcting this and using config.MapHttpAttributeRoutes() everything worked fine.
This was not your case (as is apparent from your sample code), but please do remember to end the Controller class name with Controller.
Else it won't be picked up by config.MapHttpAttributeRoutes();.
This question already has a selected answer. But I had a different solution for myself and think it would be helpful to reply if the selected answer doesn't help.
For me it was a silly mistake. I had two controllers but only one was working. The solutions was that my controller class was named improperly!
My working controller-
public class FooController : ApiController { }
My non-working controller-
public class BarControllers : ApiController { }
Be sure your controller class ends in Controller. The trailing s got me!
Make sure you don't have two controllers with the same name! I was moving some controllers from one assembly I was throwing away into the website... whilst the website no longer had references to the old assembly other assemblies did which meant it was copied in to the WebSite bin folder. The route discovery process then seemed to fail silently when it came across two occurrences of the same controller!
In my case, VS create my controller with the name
TestController1
I dont know why he put this number "one" in the end of name, but remove and will work.
In my case, I was missing full custom path in attributes. I was writing only custom action name without 'api/'. So that was my mistake. My scenario was,
WebApiConfig template code:
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
my incorrect way of route
[RoutePrefix("myapps")] // wrong code
public class AppsController : BaseRestAPIController
{
[HttpPost]
[Route("getapps")]
public ResponseData GetAppList()
{
Correct way
[RoutePrefix("api/myapps")] // correct way. full path start from 'api/'
public class AppsController : BaseRestAPIController
{
[HttpPost]
[Route("getapps")]
[Route("api/myapps/getapps")] // you can use full path here, if you dont want controller level route
public ResponseData GetAppList()
{
In my case following line was creating problem, just commented it and everything start working
config.MapHttpAttributeRoutes();
Comment it in WebApiConfig.cs file

ASP.NET MVC 3 won't recognise .cshtml view files

I have ported an mvc 3 app from vs 2010 to vs2012.
The ported app is using .NET 4.
All the old bits work, but with a new view, created in vs 2012, the view engine is not looking for .cshtml files for the view.
For example, when the user requests the index action on the Welcome controller in the Solicitors area, the url is:
mysite.com/solicitors/welcome/gg
(where gg is the user name). In that case, the error that comes back is:
The view 'Index' or its master was not found or no view engine
supports the searched locations. The following locations were
searched: ~/Areas/Solicitors/Views/Welcome/Index.aspx
~/Areas/Solicitors/Views/Welcome/Index.ascx
~/Areas/Solicitors/Views/Shared/Index.aspx
~/Areas/Solicitors/Views/Shared/Index.ascx ~/Views/Welcome/Index.aspx
~/Views/Welcome/Index.ascx ~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx ~/Areas/Solicitors/Views/Welcome/gg.master
~/Areas/Solicitors/Views/Shared/gg.master ~/Views/Welcome/gg.master
~/Views/Shared/gg.master ~/Areas/Solicitors/Views/Welcome/gg.cshtml
~/Areas/Solicitors/Views/Welcome/gg.vbhtml
~/Areas/Solicitors/Views/Shared/gg.cshtml
~/Areas/Solicitors/Views/Shared/gg.vbhtml ~/Views/Welcome/gg.cshtml
~/Views/Welcome/gg.vbhtml ~/Views/Shared/gg.cshtml
~/Views/Shared/gg.vbhtml
I have already added the following key to appsettings in web.config, but it makes no difference.
<add key="webpages:Version" value="1.0" />
EDIT:
Route in SolictorAreaRegistration.cs:
context.MapRoute(
"Solicitors_Welcome",
"Solicitors/Welcome/{nameUser}",
new { controller = "Welcome", action = "Index", nameUser = UrlParameter.Optional }
);
EDIT 2:
Using RouteDebug, I can see that the correct controller and action are found.
Route Data
Key Value
nameUser: gg
controller: Welcome
action: Index
Data Tokens
Key Value
Namespaces: System.String[]
area: Solicitors
UseNamespaceFallback: False
EDIT 3:
The route is found correctly, as I can see from debugging: the Index action is hit.
The problem happens when the line call the view is called:
namespace MyApp.Areas.Solicitors.Controllers
{
[Authorize]
public partial class WelcomeController : Controller
{
//
// GET: /Solicitors/Welcome/
public virtual ActionResult Index(string nameUser)
{
return View("Index", nameUser);
}
}
}
OK, got to the bottom of it:
The Problem:
The problem is that the model of my view is of type string. In my action, I was passing in a string as the model parameter:
public virtual ActionResult Index(string nameUser)
{
return View("Index", nameUser);
}
This will clash with one of the overloads of Controller.View(...):
View(string, string)
The second parameter expects the name of a layout file. When you do this, MVC goes off looking for a layout file with a name of the value of your string, which could be, for example:
"Hello, World. I'm an idiot, but if you give me a decent error message, I might be able to fix the bug."
Obviously, a layout file with that name doesn't exist. Nor does a layout file called "gg" either (my (test) solicitor's username).
The Solution:
The solution is simple:
Specify that the second parameter is the model, not the layout.
public virtual ActionResult Index(string nameUser)
{
return View("Index", model: nameUser);
}
Useful Article:
To view an extended discussion of this very issue, see the following article:
MVC Gotcha: Beware when using your view's model is a string
Many thanks to heartysoft.com for the enlightenment.
It is looking as you can see from the error message:
~/Areas/Solicitors/Views/Welcome/gg.cshtml
If you need to look for the Index view then you need to specify it:
http://mysite.com/solicitors/welcome/index/gg

500 internal server error in an ASP.NET MVC 3 web app

I have a web application in ASP MVC 3. In development environment, everything works fine, in a production environment there is a page which throws a 500 error but not every time. For example when open this page for the first time it works, but when I do it 3 times it throws this 500 error.
I thought that maybe it could be a timeout issue, so I added this code in the web.config file:
<httpRuntime executionTimeout="3600" maxRequestLength="2147483647" />
but it didn't solve the problem.
Any suggestions ?
EDIT
This is the action that calls the partial view :
public ActionResult _PopupDiscussion(int? id)
{
ViewBag.id = id == null ? null : id;
return PartialView("PartialViews/_PopupDiscussion");
}
then inside the partial view there is an AJAX request which calls this action:
public ViewResult PopupDiscussion_Content(DiscussionModelView model, int? id)
{
ViewBag.id = id;
// some code here
return View(model);
}
I tried to turn off custom errors like this :
<system.web>
<customErrors mode="Off"/>
</system.web>
but it didn't work.
This was caused by calling an action inside the controller from a partial view.
I modified this by using #Html.Partial() instead of #Html.Action() and everything works just fine

ASP.NET MVC 3 - Setting up routes

I'm migrating an ASP.NET web forms application to ASP.NET MVC 3. I kind of understand routing, but I sort of don't. In my application, I have created three .cshtml files in the directory located at /internal/products/find/. For the sake of demonstration, those .cshtml files are named "view1.cshtml", "view2.cshtml", and "view3.cshtml".
I have a controller named "InternalController". My goal is to use InternalController for all of the locations inside the /internal path. I'm not sure if what I'm trying to do is allowed. I assume it is. Either way, at this time, I have the following in InternalController:
public ActionResult View1()
{
return View();
}
public ActionResult View2()
{
return View();
}
public ActionResult View3()
{
return View();
}
In my global.asax.cs file, I'm trying to register the routes to these views as follows:
routes.MapRoute(
"View1",
"{controller}/products/find/view1",
new { controller = "Internal", action = "View1" }
);
routes.MapRoute(
"View2",
"{controller}/products/find/view2",
new { controller = "Internal", action = "View2" }
);
routes.MapRoute(
"View3",
"{controller}/products/find/view3",
new { controller = "Internal", action = "View3" }
);
Whenever I try to visit /internal/products/find/view1 in my browser, I see the ASP.NET error screen and it says:
The view 'View1' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/internal/View1.aspx
~/Views/internal/View1.ascx
~/Views/Shared/View1.aspx
~/Views/Shared/View1.ascx
~/Views/dashboard/View1.cshtml
~/Views/dashboard/View1.vbhtml
~/Views/Shared/View1.cshtml
~/Views/Shared/View1.vbhtml
What am I doing wrong? The path /internal/products/find/view1 is the most important part for me. Ideally, I would like to expose that in InternalController everytime. But I'm having a rough go at it. What am I doing wrong?
Thanks!
When you write
routes.MapRoute(
"View1",
"{controller}/products/find/{action}",
new { controller = "Internal", action = "View1" }
);
it means that whenever user writes into his browser:
http://mysite.com/blahblah/products/find/blahblahview
it will activate action view1 inside controller blahblahview. But it doesn't mean that view1.cshtml file is at that path. Actually, asp.net mvc looks for views at directories defined by convention...and convetion is:
~/Views/ControllerName/ViewName
so, your view should be in a folder:
~/Views/Internal/View1.cshtml
Unlike ASP.NET WebForms you are used to, ASP.NET MVC is pretty much driven by naming conventions as you could probably see (you always name your controllers like BlahBlah*Controller*, you always place your views inside Views folder etc... Read some tutorials here and catch up with basics.

Returning 404 Error ASP.NET MVC 3

I have tried the following 2 things to have a page return a 404 error:
public ActionResult Index()
{
return new HttpStatusCodeResult(404);
}
public ActionResult NotFound()
{
return HttpNotFound();
}
but both of them just render a blank page. How can I manually return a 404 error from within ASP.NET MVC 3?
If you inspect the response using fiddler, I believe you'll find that the blank page is in fact returning a 404 status code. The problem is no view is being rendered and thus the blank page.
You could get an actual view to be displayed instead by adding a customErrors element to your web.config that will redirect the user to a specific url when a certain status code occurs which you can then handle as you would with any url. Here's a walk-through below:
First throw the HttpException where applicable. When instantiating the exception, be sure to use one of the overloads which takes a http status code as a parameter like below.
throw new HttpException(404, "NotFound");
Then add an custom error handler in your web.config file so that you could determine what view should be rendered when the above exception occurs. Here's an example below:
<configuration>
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/404"/>
</customErrors>
</system.web>
</configuration>
Now add a route entry in your Global.asax that'll handle the url "404" which will pass the request to a controller's action that'll display the View for your 404 page.
Global.asax
routes.MapRoute(
"404",
"404",
new { controller = "Commons", action = "HttpStatus404" }
);
CommonsController
public ActionResult HttpStatus404()
{
return View();
}
All that's left is to add a view for the above action.
One caveat with the above method: according to the book "Pro ASP.NET 4 in C# 2010" (Apress) the use of customErrors is outdated if you're using IIS 7. Instead you should use the httpErrors section. Here's a quote from the book:
But although this setting still works with Visual Studio’s built-in test web
server, it’s effectively been replaced by the <httpErrors> section in IIS 7.x.
I'm successfully using this:
return new HttpNotFoundResult();
throw new HttpException(404, "NotFound"); along with a custom error handler works fine for me.
You should use
// returns 404 Not Found as EmptyResult() which is suitable for ajax calls
return new HttpNotFoundResult();
when you are making AJAX calls to your controllers and don't find any content.
When you are making classic calls to controller actions and returning Views you should use:
// throwing new exception returns 404 and redirects to the view defined in web.config <customErrors> section
throw new HttpException(404, ExceptionMessages.Error_404_ContentNotFound);
You can personalize 404 result with
return new HttpStatusCodeResult(404, "My message");

Resources