I try to handle 404 and 500 error in ASP.NET MVC 6.
I have Action in controller which render page with error status and description:
[HttpGet]
[Route("/Errors/{status}")]
public IActionResult Errors(int status)
{
var model = Mapper.Map<ErrorViewModel>(BaseData);
model.ErrorCode = status;
if (error_descriptions.ContainsKey(status))
model.Description = error_descriptions[status];
else
model.Description = "Неизвестная ошибка";
return View(model);
}
I enable status code pages and exception handler:
app.UseExceptionHandler("/Errors/500");
app.UseStatusCodePagesWithReExecute("/Errors/{0}");
I want to ger 404 error when url doesn't match any route and when queryes to Database return empty sequencies (access to nonexists elements).
I've got InvalidOperationException when LINQ return empty sequence (the sequence contains no elements). I want to handle global exception, show 404 in this case and 500 in other.
I make a simple filter and check Exception in OnException:
public void OnException(ExceptionContext context)
{
logger.LogError(context.Exception.Message);
if (context.Exception is System.InvalidOperationException) ;
//NEED TO CALL HomeController.Error(404)
}
It works, but I don't know how to call my action and show error page
You can create a model in the OnException method like
public void OnException(ExceptionContext context)
{
var model = new HandleErrorInfo(filterContext.Exception, "YourError Controller","Errors");
}
Related
Here is my controller code.
my controller return 401 status code successfully.
How to return a 401 error message with my custom message
[HttpGet]
[Authorize(Roles = "reader")]
public async Task<IActionResult> GetAllBlogsAsync()
{
// get data from repository
var blogs = await blogRepository.GetAllAsync();
// mapping Domain to DTO
var blogsDTO = mapper.Map<List<Models.DTO.Blog>>(blogs);
// return results
return Ok(blogsDTO);
}
My expected Output is "401 UnAuthorized"
Advance Thanks.
you can use the Unauthorized method like below to return.
return Unauthorized("401 UnAuthorized");
I think what you need is a custom implementation of the attribute as explained in this post:
How to return custom message if Authorize fails in WebAPI
Getting 404 error in ASP.NET Core Web API. With ASP.NET I am using Angular 7 for front end. When I click on save button of a form, the form data reaches to post method in angular but after click on save it shows 404 error. For this form I created first model where I define its property for database then I created controller for that model and use that property that I declared in model.
Angular service ts file:
export class MurderService {
BaseUrl :string ='';
constructor( private http:HttpClient, private config:ConfigService) {
this.BaseUrl=config.getApiURI();
}
murderQuestionnaire(data: any){
var murderBody={
Dead: data.Dead,
Wounded: data.Wounded,
CriminalsInvolved: data.CriminalsInvolved,
CriminalAppearance: data.CriminalAppearance,
VehiclesUsed: data.VehiclesUsed,
WeaponsDescription :data.WeaponsDescription
};
return this.http.post(this.BaseUrl +'/Complians' , murderBody);//upto here data reaches
successfully
}
}
API controller for this service:
[Route("api/[controller]")]
[ApiController]
public class ComplainMurderController : ControllerBase
{
[HttpPost]
[Route("Complians")]
//api:/Complians
public void PostComplainMurder(Complians complian)
{
var complianMurder = new Complians()
{
Dead = complian.Dead,
Wounded = complian.Wounded,
CriminalsInvolved = complian.CriminalsInvolved,
CriminalAppearence = complian.CriminalAppearence,
VehiclesUsed = complian.VehiclesUsed,
WeaponsDescription = complian.WeaponsDescription
};
try
{
// var result = await complian.Add(complianMurder);
AuthenticationContext authenticationContext = new AuthenticationContext();
authenticationContext.Add(complianMurder);
authenticationContext.SaveChanges();
}
catch(Exception ex)
{
throw ex;
}
}
}
}
Error
POST http://localhost:49601/api/Complians 404 (Not Found)
Your controller route is defined as api/[controller] (in your case this leads to api/ComplainMurder). Your action is defined as Complians. These combined is your actual route: api/ComplainMurder/Complians, which does not match api/Complians.
Changing your Angular 7 side with this will fix it:
return this.http.post(this.BaseUrl +'/ComplainMurder/Complians' , murderBody);
Side note: be careful with incorrect/inconsistent spelling (complian instead of complain) as it's bound to lead to confusion and/or mistakes in the future.
I'm new to c# and asp mvc and I can't find a way to handle the exceptions globally, but returning to the same page from were they were generated. Right now in my controllers I have something like this in all the post methods:
try
{
service.Action(object);
return RedirectToAction("Index");
}
catch (MyBusinessException e)
{
ViewData["error"] = (e.InnerException.Message ?? "");
return View(object);
}
The message of the inner exception is a friendly message, like "You cannot create two articles with the same Code" that I want to show to the user in the form of create article rather than send him to an error page
I was trying to reproduce this behavior by creating a base controller that overrides the OnException method, but I don't know how to return to the same page from where I came and show the error message.
Edit based on response from #JotaBe
I've created a global OnException filter and registered in the FilterConfig.cs, now I'm able to return to the page from were I came, but the model that I get from the ExceptionContext it's null, therefore I get a NullPointerException when the view it's loaded.
This is my current method:
public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
var currentController = (string)filterContext.RouteData.Values["controller"];
var currentActionName = (string)filterContext.RouteData.Values["action"];
LOGGER.ErrorFormat("Action: {0} - Controller: {1} - ExceptionMessage: {2} - InnerException: {3}",
currentActionName,
currentController,
filterContext.Exception.Message,
filterContext.Exception.InnerException.Message);
Exception e = filterContext.Exception;
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = currentActionName,
ViewData = filterContext.Controller.ViewData,
MasterName = Master,
TempData = filterContext.Controller.TempData
};
}
How can I obtain the model that was posted?
What you need to do is to use filters. A filter is an attribute that can be applied to controllers to modify their behavior. Here you have the official MSDN docs for MVC filters.
In the previous doc you can see that there is a filter named HandleErrorAttribute, which is the one that you need for your particular case.
A filter can be applied globally (registering it on application start) or decorating each individual controller. For a good explanation and sample, you can read this blog entry.
Remember that there can be execptions different to those thrown by you. So, you should take into account that possibility to show different information depending on the type of the exception. This filter allows to show different views depending on the exception type.
I have a hybrid (use both MVC and classic ASP pages) ASP (C#) Net Application
and need to implement common error handling for both MVC and legacy codes;
Namely, I have to detect invalid URLs and re-route the invalid request to
either home page or login page (depending whether the user is logged in or not).
I have added the error handling code inside the 'Application_Error' (See code below).
The issue is the following: loggedin user id is kept in Session object
and for some invalid URLs session object becomes null with: "session state is not available in this context"
For example:
for the following URLs, the Session object is present:
1. http://myserver:49589/test/home/index
2. http://myserver:49589/test/home/in
3. http://myserver:49589/test/ho
But for the following URL, the session object is null:
4. http://myserver:49589/te
So, the question is why session object becomes null when I misspell the folder name in the Request, and how I can solve this issue.
Routing Map is the following:
context.MapRoute(
"default",
"test/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
if (httpException != null) // Http Exception
{
switch (httpException.GetHttpCode())
{
case 400: // Bad Request
case 404: // Page Not Found
case 500: // Internal Server Error
{
// Clear the error on server.
Server.ClearError();
ServerConfiguration scfg = ServerConfiguration.Instance;
if (ConnxtGen.App.AppUtility.GetCurrentUserID() != -1)
{
Response.RedirectToRoute("Unity_default", new { controller = "Home", action = "Index" });
}
else
{
Response.Redirect(scfg.PagePath + "/login/login.aspx", false);
}
break;
}
default:
{
break;
}
}
}
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
}
One thing you should understand is that Session variables are only available after the HttpApplication.AcquireRequestState event has happened.
In your question, the moment where you want to get some information in the session is too early in the process of the Asp.Net page lifecycle.
I found this post which will surely better explain when the session object will be available:
Asp.net What to do if current session is null?
And here is a great article that explain in deeper details the entire internal process of Asp.Net page lifecycle:
ASP.NET Application and Page Life Cycle
I am creating an ASP.Net 5 application with MVC 6, using .Net 4.5.1. I have a POST method that uses a FromBody parameter to get the object automatically.
[HttpPost]
public IActionResult Insert([FromBody]Agent agent)
{
try
{
var id = service.Insert(agent);
return Ok(id);
}
catch (Exception ex)
{
return HttpBadRequest(ex);
}
}
This is just a proof a concept, I won't return only the id on success or the full exception on error.
When a valid JSON is sent everything works fine. However when an invalid JSON is sent, I get an exception during debug:
Exception thrown: 'Newtonsoft.Json.JsonReaderException' in
Newtonsoft.Json.dll
Additional information: After parsing a value an unexpected character
was encountered: l. Path 'Name', line 2, position 20.
The problem is that after this error, the method is called normally, but with a null parameter, and the exception doesn't propagate.
I could check for null and return a generic message, but that is not as useful as the original message, which has important information, such as the tag name and position of the invalid character.
So I want to capture this exception and return it to the HTTP caller. How do I do that? Something like this:
{"error": "After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20"}
I know I could capture and deserialize the JSON manually inside a try/catch block, but that is not acceptable for me. I want to do it and continue using FromBody, which I find very productive.
The default JsonInputFormatter will in fact return a null model upon encountering an error - but it will populate ModelState with all exceptions.
So you have access to all encountered errors by digging into ModelState:
[HttpPost]
public IActionResult Insert([FromBody]Agent agent)
{
if (!ModelState.IsValid)
{
var errors = ModelState
.SelectMany(x => x.Value.Errors, (y, z) => z.Exception.Message);
return BadRequest(errors);
}
// Model is valid, do stuff.
}
Output of above is an array of all exception messages, e.g.:
[
"After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20",
"Another exception message..."
]
JsonInputFormatter - Source
I found myself with exactly the same problem, but was able to find a different solution.
I will share my solution here as an alternative to #ypsilo0n's answer.
Instead of checking in every controller the if (!ModelState.IsValid) we can have this middleware filter:
public class FooFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var modelState = context.ModelState;
if (modelState != null && modelState.IsValid == false)
{
// this class takes the model state and parses
// it into a dictionary with all the errors
var errorModel = new SerializableError(modelState);
context.Result = new BadRequestObjectResult(errorModel);
}
}
}
Now, the controller never gets called because this middleware runs before and ends the request. (read docs for more information).
When we set a non-null context.Result it means "end the HTTP request here" (the docs) -- not very user friendly/intuitive if you ask me but OK (would expect a return value instead).
using .net core 1.1