In the config of my application I deleted the customErrors to make a test of an error that is difficult to replicate.
And where the error should be happening I put a try:
if (ModelState.IsValid)
{
db.TarefaHoras.Add(tarefaHora);
try
{
db.SaveChanges();
}
catch (DbUpdateException ex)
{
ErroDetalhe erro = new ErroDetalhe();
erro.Data = tarefaHora.Data;
erro.UsuarioId = tarefaHora.ApplicationUserId;
erro.JSON = JsonConvert.SerializeObject(tarefaHora);
erro.Tipo = "TarefaHora";
erro.Controller = "TarefaHoras";
erro.Action = "Create Post";
erro.Exception = ex.GetType().FullName;
db.ErroDetalhes.Add(erro);
db.SaveChanges();
return RedirectToAction("ErroNaAtualizacaoDaBase", "Erros", new { id = erro.ID });
}
return RedirectToAction("Index", "Home");
}
return View(tarefaHora);
}
This error only happens at runtime and only on Azure server, so my attempt was to get a better description of the error and for this I created a table where the information is deposited.
But....
The problem is that the View error that I'm getting is not the View "ErrorInputUpdate" that I created and that is redirected in Catch.
I am getting the default AspNET error view.
So I went into Shared Views and deleted the default page and removed customErrors from web.config.
And even then Azure still brings the default error view.
Anyone have any idea what might be happening?
So calling db.SaveChanges(); generates an exception. That means that the context db is invalid and it should be resolved or discarded.
However, what you are doing is creating an error entry and add using the same db context which is still in a faulty state. So the call to db.SaveChanges(); in your catch block will still generate an exception, but this time it won't be catched.
The solution is either to write the error to something else than the database, like a file or whatever or to use another EF context instance. Other options are to removed or correct the invalid entries before calling db.SaveChanges(); again or do not store the error in the database but include all the details in the redirect view.
Related
I have an ASP.NET Core application and I'm attempting to handle HTTP responses with status codes between 400 and 599 by using UseStatusCodePagesWithRedirects.
In Startup.cs I've added the following:
app.UseStatusCodePagesWithRedirects("/Error");
My Error controller is empty except for the following action (which was taken from the default scaffolded Home controller):
[Route("Error")]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
This works perfectly when I call return new BadRequestResult(); or return NotFound(); from one of my controllers, but when I try to return an error with more detail (such as including an error object) the controller action is never called and the body of the response is displayed on screen as plaintext instead. As an example, the following statement does not trigger the Error controller:
context.Result = new BadRequestObjectResult({ Errors = errors });
If I use the following statement instead, the middleware is correctly called:
context.Result = new BadRequestResult();
This appears to be working as designed, as the documentation states that UseStatusCodePagesWithRedirects "checks for responses with status codes between 400 and 599 that do not have a body" (emphasis mine) and the source code backs this up.
I want to include more information on my error page (such as user friendly error messages where appropriate) but I can't figure out how I can pass the data across effectively using the middleware since I'm trying to avoid my controllers knowing too much about how errors are handled so that the logic can be contained in one place and potentially changed later.
Is there a way to return a HTTP error that has additional information but will still get picked up by UseStatusCodePagesWithRedirects?
This is not how the exception handling middleware works. I'm not sure what you're doing exactly, but it looks like you're attempting to return BadRequest from middleware or an action filter. If you want to intercept some error there, you should simply allow the exception to bubble up (or throw one), not return a response, as that way, you'll keep the context of what happened.
Inside your error action, you can use HTTP feature interfaces to get the data you're looking for then. For example, there's:
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
If there was an exception, you can access it then via exceptionHandlerPathFeature.Error. There's also IStatusCodeReExecuteFeature, which you can use to get the original URL of the request for things like 404s:
var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
OriginalURL =
statusCodeReExecuteFeature.OriginalPathBase
+ statusCodeReExecuteFeature.OriginalPath
+ statusCodeReExecuteFeature.OriginalQueryString;
}
Source
Depending on exactly what you're doing, there might be other ways as well.
The below is not exactly what you need (passing an error details/an error object) but it seems like you can pass an error code, at least in ASP.NET Core.
If you look at the documentation for UseStatusCodePagesWithRedirects, it says that you can pass a status code, since the url template may contain such parameter:
app.UseStatusCodePagesWithRedirects("/MyStatusCode?code={0}");
Then in your MyStatusCode.cshtml you can intercept it like:
#{
var codeStr = Context.Request.Query.ContainsKey("code") ? Context.Request.Query["code"].ToString() : "-1";
}
I have the following global error handling setup for a webapp.
Global.asax.cs
protected void Application_Error()
{
//log error
_logger.Error("something bad happened", Server.GetLastError());
//clear error
Server.ClearError();
//redirect to error page
Response.RedirectToRoute(new { controller = "services", action = "error" });
}
Services controller with Error Action. This view uses the ErrorModel class as the model.
public ActionResult Error()
{
return View(new ErrorModel{ Message = "nothing really happened go to bed" });
}
General view:
public ActionResult General()
{
throw new Exception("Exception XYZ");
}
When I debug the application using VS F5, and go to /general view, the correct exception gets logged (Exception XYZ).
But when testing the published IIS webapp, and going to /general view, a different, unexpected exception is getting logged:
System.InvalidOperationException: The model item passed into the
dictionary is of type 'System.Web.Mvc.HandleErrorInfo', but this
dictionary requires a model item of type
'MyWebsite.Models.Services.ErrorModel'.
The Exception XYX does not get logged at all. The correct Error view with the correct error message (nothing really happened go to bed) does get shown.
What am I doing wrong? How do I make sure Exception XYZ gets logged?
I don't intend to use HandleErrorAttribute or override void OnException in the controller at this time.
EDIT
Seems like this issue is already discussed in 2010 on SO here
I have a MVC controller method that looks like this:
[HttpPost]
public async Task<JsonResult> Edit(Foo input)
{
if (ModelState.IsValid)
{
Foo current = await db.foos.FindAsync(input.Id);
current.SomeProp = input.SomeProp; // etc.
db.Entry(current).State = EntityState.Modified;
await db.SaveChangesAsync();
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json(false, JsonRequestBehavior.AllowGet);
}
This works when I'm debugging in Visual Studio. When I'm not debugging, I get a 500. What difference would debugging make here?
Note: I'm not using db.Entry(current).CurrentValues.SetValues(input); because there are some properties of Foo I don't want to change here.
Edit: I can't figure out how to get the exception details when not in debug mode. I tried writing it to a file. This works in debug mode (with throw new Exception("test"); taking the place of the statement that only works in debug mode) but I get a 401 when not debugging. The IIS user does have write permission on the directory.
The exception was a DbEntityValidationException indicating that a required field was missing. The missing field is a relation of Foo.
Based on Entity Framework Loading Related Entities, I believe my code loads Foo's relations lazily. I didn't even consider this possibility because I misunderstood comments like "EF doesn't support asynchronous lazy loading." Inspecting the object in debug mode seemed to confirm that its relations were being loaded eagerly. But now I realize that inspecting the object may have triggered lazy loading!
I already asked a similar question to this but i can't even look the code i write, it looks horrible. There is something wrong.
I'm trying to create a simple web application with Visual Studio, ASP.Net Web Pages and Entity Framework. People are not familiar with Web Pages, it's basically a development environment like classic ASP and PHP.
I have two tables, one is workers and other one is overhours. I created models for both, they are related so every overhour record has one worker.
Basically i'm using this code:
if (IsPost)
{
try
{
Worker curWorker = new Worker();
try
{
curWorker = m.Workers.Find(decimal.Parse(Request.Form["WorkerId"]));
}
catch (Exception)
{
errors += "Please select a worker.";
}
try
{
overhour.OverhourAmount = decimal.Parse(Request.Form["OverhourAmount"]);
if (overhour.OverhourAmount == 0)
{
throw new Exception();
}
}
catch (Exception)
{
errors += "Hour field should be numerical and non-zero.";
}
overhour.Worker = curWorker;
overhour.OverhourDate = DateTime.Today;
curWorker.Overhours.Add(overhour);
if (errors != "")
{
throw new WrongValueException(errors);
}
m.SaveChanges();
Response.Redirect(Page.ParentPage);
}
catch (DbEntityValidationException ex)
{
errors = kStatic.getValidationErrors(ex.EntityValidationErrors, "<br />");
}
catch (WrongValueException ex)
{
errors = ex.Message.ToString();
}
catch (Exception ex)
{
errors = "Critical error, technical details: " + ex.Message;
}
}
Form has a combobox with all workers named WorkerId. It works but i have a few problems.
I hate validation method. I need to validate if user has selected a valid worker from the combobox because it has an option named "Select a worker" and it's value is empty string, so i need to check if it's numerical. I can include a [Regex..] code to my model class but it doesn't matter because there will be an error when i try to assign string to decimal field (decimal WorkerId). I can catch the exception but it will be likely a mismatch exception. I need more details.
Same thing with OverhourAmount, it should be numerical and non-zero too.
I don't like putting this code into the page code itself. I can create a repository class with methods like r.addOverhour but people say it's unnecesary. Is it unnecesary for MVC or if you're using Entity Framework, you shouldn't use an extra repository class.
I want to check the database for some validation before saving changes. For example, an user (user who has username and password, not worker) shouldn't be able to create a record about a worker if they are not in same building. For example, user A works in building X, and worker H works in building Y, user A shouldn't be able to create any data related to worker H. So i need to check if they work in same building before adding the record. I have BranchId field in both user and worker tables, i can check that easily but where?
Basically i don't know how to structure my code. I think i'm missing something big here because everybody validates their data and filter their inputs.
Thanks
Decorate your dtos with validation. Modelbinding will verify your data automatically and if failed will return the property that fails validation and the reason which you can customize. You business rules need to happen in a logic layer which will basically operate on your data models and return dtos. Your logic layer will accept dtos and this will provide separation from the data layer from the web API/mvc
How do I gracefully handle errors when trying to connect to a database that may be down, or given an incorrect connection string in MVC 4?
I have my string in Web.config and the way it accesses the database is just instantiating my class derived from DBContext in each controller, like so:
private DBEntities db = new DBEntities("database");
This works fine if the database is up and everything is correct, but if it's not, the pages on my web site display this error:
Exception Details: System.ComponentModel.Win32Exception: The network path was not found
Obviously I don't want this to appear, what I'd like to do is have a way to try catch the error and display a custom error page (similar to 404 Page Not Found).
Thank you.
Do the initialisation in the function
private DBEntities db;
// then in your function attempt to initialise
try{
db = new DBEntities("database");
db.Connection.Open();
}
catch(Exception ex){
if(db.Connection != ConnectionState.Closed){
db.Connection.Close();
db.Dispose();
//go to error page
}
}
It depends where private DBEntities db = new DBEntities("database"); is and when it is called but why not just:
try {
private DBEntities db = new DBEntities("database");
} catch (Exception ex) {
//do something here
}
start by adding an 'Error.cshtml' view in the Shared views folder. Give it a model type of
System.Web.Mvc.HandleErrorInfo
Then add the following to your web.config file
<system.web>
<customErrors mode="On" defaultRedirect="/Error" />
</system.web>
This will redirect to the Error view on any exception.
Now it's just a matter of styling your error page.
I'd advise you try { } catch { } as suggested in the other posts, in your catch block, just rethrow with a user-friendlier message - like "Database connection could not be made."
Quite a full discussion/explanation here: http://www.prideparrot.com/blog/archive/2012/5/exception_handling_in_asp_net_mvc