I have a WebAPI app which has registered a custom exception handler with:
app.UseExceptionHandler("/error");
I then have an ErrorController with the following code:
[Route("/error")]
public IActionResult Error()
{
if (...)
return Problem(detail: "Text", statusCode: 403);
else
return NotFound();
}
This is obviously simplified.
The problem is that with 403 or any other code I've tried (apart from 404) this works fine.
With 404 (whether passed to Problem or using NotFound directly) I get a new exception raised caused by the system attempting to copy the original exception to a stream:
System.Net.Http.HttpRequestException : Error while copying content to a stream.
---- System.IO.IOException ...
Any thoughts?
Related
I have a really confusing issue, mainly because the exception I'm getting is very unhelpful and non-descriptive.
I have an ASP.Net Core-based API, and have just added a new controller. I am using ASP.Net Core 3.0, and am mapping my controllers within Startup.Configure by using the following:
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
However, when running the API in debug I was getting the following exception on startup:
RoutePatternException: There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.
I removed the controller and the issue disappears, so I figure there's something wrong with the controller that's causing the endpoint mapping to throw this exception but I can't see what it is?
It was only when I reconfigured how endpoints were routed that I found the answer to the problem. I've written this out in case anybody else gets any non-descriptive exceptions from ASP.Net Core. Within Startup.Configure, I replaced:
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
with:
app.UseMvc(routes =>
{
routes.MapRoute("repo1", "{controller=Repo1Controller}");
routes.MapRoute("repo2", "{controller=Repo2Controller}");
}
Then in Startup.ConfigureServices, I added:
services.AddMvc(option => option.EnableEndpointRouting = false);
Upon next startup this then showed the following exception:
RouteCreationException: The following errors occurred with attribute routing information: For action: 'MyAPI.Controllers.Repo2Controller.UpdateEntityProperty (MyAPI)'
Error: There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character. (Parameter 'routeTemplate')
In the Repo2Controller I had the following:
[HttpPut("{entityId}/Properties/{propertyId")]
public IActionResult UpdateEntityProperty(string entityId, string propertyId)
{
// Do some stuff
}
This highlighted the fact that I was missing the closing } in the HttpPut attribute. Once I replaced this and returned to the original endpoint routing method, everything worked fine.
Wrap Configure() method code with try catch like below.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
try
{
...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
...
}
catch (System.Exception exp)
{
throw;
}
}
Then investigate the thrown exception. Under InnerException->Pattern you will find the specific problem. Sample:
I tried to create custom error pages and view them while my environment is still "Development", so in my Startup.cs file I change my code into :
if (env.IsDevelopment())
{
//app.UseDeveloperExceptionPage();
app.UseExceptionHandler("/Home/Error");
app.UseStatusCodePagesWithRedirects("/Home/Error/{0}");
}
Then I create an Error Action in my HomeController.cs and a PageNotFound action.
So my HomeController.cs now looks like :
[RouteAttribute("/Home/Error/{code?}")]
public IActionResult Error(int? code = null)
{
if(code.Value == 404)
{
return View("PageNotFound");
}
return View();
}
public IActionResult PageNotFound()
{
return View();
}
Actually this works great except this redirects too much. For example, If I would like to go to a page which will return status code of 404, then what happens in my debug is, Error action calls 5~ times and then suddenly it returns my View("PageNotFound). And this is not the only issue, main issue is, after every action call(No matter if it returns an Error or not) my debugger goes to Error Action, repeats this 5 times then suddenly returns the main page content... What is happening here, what am I missing? Someone please help me.. Thank you!
I use the code below to handle all unhandled errors on my controllers:
public abstract class BaseController: Controller
{
protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
if (IsJsonCall())
filterContext.Result = HandleJsonError(filterContext.Exception);
else
filterContext.Result = HandleError(filterContext.Exception);
}
}
In HandleJsonError function, I return a JSON object with information about the error. In my JavaScript code I detect this info and handle it properly.
Its working fine on localhost, returning a 200 HTTP Code to the client, but in my production environment, it returns the JSON object with the error info, so I can say its running all the code, but its returning a 500 HTTP code instead of 200, which is causing problems when JavaScript tries to recognize the returned content.
Why the codes returned are different event if the content is the same?
I am trying to write my own authorization attribute where I run through some custom checks on any web api method with the CustomAuthorization attribute.
My code is as follows:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class CustomAuthorization : AuthorizationFilterAttribute
{
public override void OnAuthorization(AuthorizationContext context)
{
//// Attempt 1 - 404 error.
//// Doesnt block method with this attribute from executing (not desired behaviour).
//context.HttpContext.Response.StatusCode = 401;
//return;
//// Attempt 2 - 404 result.
//// Code with attribute doesnt execute (desired).
//// Error thrown says: An exception of type 'System.Web.Http.HttpResponseException' occurred in <namespace> but was not handled in user code
//// Additional information: Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
//throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
// Attempt 3 - 404 result.
// Code with attribute doesnt execute (desired).
context.Result = new HttpUnauthorizedResult();
}
}
The problem I'm having is that I'm getting a 404 response from the web api instead of an expected 401. What am I doing wrong?
This is asp.net core 1.
Thanks in advance!
It may be because you have authentication setup to redirect to a login page for 401 responses and that login page is not being found (happened to me).
I want to pass an identifier to a custom error page in ASP.NET MVC.
I have the web.config set up to redirect to an error page when there is an status 500 error. I don't know how to pass an error id from the application code that throws the exception (also sets HTTP Status of 500) back to the error page that is configured.
It's easy if I just used Status 200 and returned a view from the Error method of the controller and did all the logic in there, however the Status code must be 500, so that's not an acceptable solution.
I also do not and will not use session for this. I'd like to use query string or view data.
Not a duplicate. This is about error handling (status code 500) and exceptions. 404 is about Page Not Found
I don't know a better way to customized information to error page configured in web.config. This is what I normally do when i need an error redirection:
I'll create an error handler action filter:
public class MyErrorHandler: HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.ExceptionHandled = true;
filterContext.Result = new RedirectResult("/error?error_id=123456", true);
return;
}
base.OnException(filterContext);
}
}
Within the Exception filter, you may try to handle differnet error with different error id, or whatever you prefer to alter the exception.
Then apply it onto controller or controler class:
[MyErrorHandler]
public class HomeController : Controller
{
....
}
And you will get a Http status 500 and will be redirected to:
http://<host>/error?error_id=123456