Asp.net MVC 3 view engine error - asp.net-mvc-3

I am trying for custom error handling at global level in Application_Error.
Exception ex = Server.GetLastError();
Server.ClearError();
AssortmentDefinitionLogManager.LogException(ex);
Context.RewritePath("/Error/Error");
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(Context);
But i get this error
Error Message - The view '~/Views/Shared/Error' or its master was not found or no view engine supports the searched locations.
I have also tried this
var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
var redirectUrl = urlHelper.Action("Error", "Error");
Response.Redirect(redirectUrl);

create a controller called ErrorController and inside this controller create an action method called Error which returns an ActionResult (or ViewResult).
Then create a view under ~/Views/Error/ called error.cshtml.
This will solve your problem.

Related

Web API: how to read action attribute and parameters from HttpContext

In regular class, I need to read following from the HttpContext:
Controller and action name
Action's attribute (I could get that through HttpActionContext.ActionDescriptor.GetCustomAttributes<type>() but here I don't have HttpActionContext - I only have HttpContext)
Read argument (like actionContext.ActionArguments["paramName"], but again - I only have a HttpContext)
It's not an action filter and not a controller class. But, I can access HttpContext.
From asp.net core 3.0 https://stackoverflow.com/a/60602828/10612695
public async Task Invoke(HttpContext context)
{
// Get the enpoint which is executing (asp.net core 3.0 only)
var executingEnpoint = context.GetEndpoint();
// Get attributes on the executing action method and it's defining controller class
var attributes = executingEnpoint.Metadata.OfType<MyCustomAttribute>();
await next(context);
// Get the enpoint which was executed (asp.net core 2.2 possible after call to await next(context))
var executingEnpoint2 = context.GetEndpoint();
// Get attributes on the executing action method and it's defining controller class
var attributes2 = executingEnpoint.Metadata.OfType<MyCustomAttribute>();
}

WebApi Odata Windows Store App EndSaveChanges exception

I am trying to create a Windows Store App using a WebApi Odata controller. After some effort I have all the Get requests working, I am now moving onto the CRUD methods, and am getting the following Exception on the EndSaveChanges of the Data Service Context.
<?xml version="1.0" encoding="utf-8"?>
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<m:code />
<m:message xml:lang="en-US">No HTTP resource was found that matches the request URI 'http://localhost:56317/odata/ESFClients(guid'f04ad636-f896-4de4-816c-388106cd39ce')'.</m:message>
<m:innererror>
<m:message>No routing convention was found to select an action for the OData path with template '~/entityset/key'.</m:message>
<m:type></m:type>
<m:stacktrace></m:stacktrace>
</m:innererror>
</m:error>
Now I think this is a bug in WebApi from this http://aspnetwebstack.codeplex.com/workitem/822 and its hiding the actual error. To make sure it wasn't my Odata Endpoint I created a quick console app to get an entry, update it and Patch it back, which worked all ok. My WebApi Odata Controller derives from ODataController with
public HttpResponseMessage Patch([FromODataUri] Guid key, Delta<ESFClient> patch)
As the method.
In my windows application I have a extension method on the DataServiceContext for the Save Changes.
public static async Task<DataServiceResponse> SaveChangesAsync(this DataServiceContext context, SaveChangesOptions options)
{
var queryTask = Task.Factory.FromAsync<DataServiceResponse>(context.BeginSaveChanges(options, null, null),
queryAsyncResult =>
{
var results = context.EndSaveChanges(queryAsyncResult);
return results;
});
return await queryTask;
}
And calling the update like so from a blank Windows Store XAML page.
public async Task UpdateWeekNo()
{
var container = new ESFOdataService.Container(new Uri("http://localhost:56317/odata/"));
var clients = (DataServiceQuery<ESFClient>)from p in container.ESFClients where p.UserID == new Guid("f04ad636-f896-4de4-816c-388106cd39ce") select p;
var result = await clients.ExecuteAsync();
var updatedClient = result.Single();
if (updatedClient != null)
{
updatedClient.WeekNo = 19;
container.UpdateObject(updatedClient);
await container.SaveChangesAsync(SaveChangesOptions.PatchOnUpdate); // Use PATCH not MERGE.
}
}
So does anyone come across the same issue, or know how I can find out the actual error. One interesting point is that if I debug the controller while running the Windows App, the patch method does not get called.
Ok, so I have finally solved this. Just a recap for those who could experience the same issue. I have an Odata WebApi controller, Windows 8 Store Application using WCF Client Library, with the reference created from Add Service Reference. When trying to update (patch) a record an exception was being thrown at the EndSaveChanges. This is because for some reason Post Tunneling is enabled by default on my context. Setting this to false allowed everything to work.
Context.UsePostTunneling = false;
Context.IgnoreResourceNotFoundException = true;

MVC Generic error view and Ajax Post and error code 500

I have set up an mvc app with an _error.cshtml that is set to catch exceptions I throw in the controller.
I also have a few ajax posts on some pages that checks for errors and then it does something else.
On the server, I have a filter on all exceptions and then check if it is an ajax request and return something that can be deserialized on the client. The problem is that if I do not set the post response status code to 500 then ajax will not see this error and I can't show a nice message. If I set the status to 500 I get the default IIS error message stating something happened on the server.
I would like to handle some errors on the page in the ajax results but maintain the generic error handling. Is this an IIS setting to allow custom 500 message per site? The web.config Custom Error On|Off makes no difference in my case.
The filter you have on all exceptions that is checking if its an ajax request, is that a filter made on your own?
I had a slightly similar issue, and I had to make sure the flag TrySkipIisCustomErrors was set as true in order to avoid the standard IIS error.
This flag is located on the Response object of the HttpContext.
This is also done by the standard HandleError filter, pay attention to the last line in its implementation of the OnException method:
public virtual void OnException(ExceptionContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}
if (filterContext.IsChildAction) {
return;
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) {
return;
}
Exception exception = filterContext.Exception;
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500) {
return;
}
if (!ExceptionType.IsInstanceOfType(exception)) {
return;
}
string controllerName = (string)filterContext.RouteData.Values["controller"];
string actionName = (string)filterContext.RouteData.Values["action"];
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult {
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
// Certain versions of IIS will sometimes use their own error page when
// they detect a server error. Setting this property indicates that we
// want it to try to render ASP.NET MVC's error page instead.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}

asp.net mvc 3 get exception thrown

I have custom errors turned on in webconfig and redirecting to "/Error/Trouble". This is working as designed. Elmah is logging the error. The error view is being displayed too.
The problem is I want to inspect the thrown error in the Trouble action of my Error controller. When an error is thrown, how do you get access to it after MVC has redirected you to the custom error handler?
I'm throwing an exception if CurrentUser is null:
if (CurrentUser == null)
{
var message = String.Format("{0} is not known. Please contact your administrator.", context.HttpContext.User.Identity.Name);
throw new Exception(message, new Exception("Inner Exception"));
}
I want to be able to access this in my custom error handler ("Error/Trouble"). How do you access the exception?
Here's my trouble action:
public ActionResult Trouble()
{
return View("Error");
}
Here's my view:
#model System.Web.Mvc.HandleErrorInfo
<h2>
Sorry, an error occurred while processing your request.
</h2>
#if (Model != null)
{
<p>#Model.Exception.Message</p>
<p>#Model.Exception.GetType().Name<br />
thrown in #Model.ControllerName #Model.ActionName</p>
<p>Error Details:</p>
<p>#Model.Exception.Message</p>
}
System.Web.Mvc.HandleErrorInfo is the model for my Trouble view and it's empty. Thanks for your help.
I found a workaround:
in Global.asax I do this:
protected void Application_Error()
{
var exception = Server.GetLastError();
HttpContext.Current.Application.Lock();
HttpContext.Current.Application["TheException"] = exception;
HttpContext.Current.Application.UnLock();
}
In Error/Trouble I do this:
var caughtException = (Exception)HttpContext.Application["TheException"];
var message = (caughtException!= null) ? caughtException.Message : "Ooops, something unexpected happened. Please contact your system administrator";
var ex = new Exception(message);
var errorInfo = new HandleErrorInfo(ex, "Application", "Trouble");
return View("Error", errorInfo);
This is working. But it seems like a weird way to go about it. Does anyone have a better solution? Thanks for your help.

MVC3 unit testing response code

I have a controller within MVC3 which needs to return a response code 500 if something goes wrong. I am doing this by returning a view object and setting http response code to equal 500 (I have checked this in firebug and all is working great).
public ActionResult http500()
{
ControllerContext.HttpContext.Response.StatusCode = 500;
ControllerContext.HttpContext.Response.StatusDescription = "An error occurred whilst processing your request.";
return View();
}
The problem I have now is I need to be able to write a unit test which checks the response code. I have tried accessing the response code in several different ways both through the ViewResult object and the Controller context.
Neither way gives me the response code I have set in the controller.
[TestMethod()]
public void http500Test()
{
var controller = new ErrorController();
controller.ControllerContext = new ControllerContext(FakeHttpObject(), new RouteData(), controller);
ViewResult actual = controller.http500() as ViewResult;
Assert.AreEqual(controller.ControllerContext.HttpContext.Response.StatusCode, 500);
}
How would I go about getting the response code 500 from the controller or is this more of an integration testing thing.
How about doing it in a more MVCish way:
public ActionResult Http500()
{
return new HttpStatusCodeResult(500, "An error occurred whilst processing your request.");
}
and then:
// arrange
var sut = new HomeController();
// act
var actual = sut.Http500();
// assert
Assert.IsInstanceOfType(actual, typeof(HttpStatusCodeResult));
var httpResult = actual as HttpStatusCodeResult;
Assert.AreEqual(500, httpResult.StatusCode);
Assert.AreEqual("An error occurred whilst processing your request.", httpResult.StatusDescription);
or if you insist on using the Response object you could create a fake one:
// arrange
var sut = new HomeController();
var request = new HttpRequest("", "http://example.com/", "");
var response = new HttpResponse(TextWriter.Null);
var httpContext = new HttpContextWrapper(new HttpContext(request, response));
sut.ControllerContext = new ControllerContext(httpContext, new RouteData(), sut);
// act
var actual = sut.Http500();
// assert
Assert.AreEqual(500, response.StatusCode);
Assert.AreEqual("An error occurred whilst processing your request.", response.StatusDescription);
What is FakeHttpObject()? Is it a mock created using Moq? In that case you need to setup setters and getters to store the actual values somewhere. Mock<T>doesn't provide any implementation for properties and methods. When setting a value of property literally nothing happens and the value is 'lost'.
Another option is to provide a fake context that is a concrete class with real properties.

Resources