I want to deal with exceptions in a WebAPI action method, by catching them setting the status code, and writing a message to the response. Normally in a normal MVC Controller I would do this like so, using Controller's Response property:
Response.StatusCode = 404;
Response.Write("Whatever");
However it seems ApiController doesn't have any Response property. Is there a reason for this? Is it OK to just use HttpContext.Current.Response like this:?
HttpContext.Current.Response.StatusCode = 404;
HttpContext.Current.Response.Write("Whatever");
Or is there a specific way of writing to the response from a WebAPI controller??
The action method is supposed to create the response object. Either just do new HttpResponseMessage or call this.CreateResponse.
If instead of returning the HttpResponseMessage you want to return a custom CLR object then you will need to throw a HTTPResponseException to return a 404.
If you want to create a message that describes your exception, your best bet is to call Request.CreateErrorResponse, and use any of the many overloads available. There are caveats to how the response is formatted depending on whether you have CustomErrors set to ON in your web.config, or whether you're in DEBUG mode. You can actually configure this behavior programatically as well, using the HttpConfiguration.IncludeErrorDetailPolicy property. See here as well: http://weblogs.asp.net/cibrax/archive/2013/03/01/asp-net-web-api-logging-and-troubleshooting.aspx
You can read this article for an in depth write up, and some options you have to solve the exact problem you describe: Web API, HttpError and the behavior of Exceptions – ‘An error has occurred’
Related
I am new to Asp.net core web API.
is return Problem(errorMessage) is the right way to return any kind of error in asp.net core if we are not sure about a particular status code.
below is my code,
[HttpPost]
public async Task<ActionResult> Post([FromForm] MyModel request)
{
var (success,statuscode, errorMessage) = await _service.MyMethod(Id, request);
return success ? Ok() : Problem(errorMessage, statusCode: statuscode);
}
MyMethod method will either return BadRequest or UnprocessableEntity status code if any error will come.
So,is it a right way to return the error in Problem()?
Thanks in advance!
For any validation issues, you should return UnprocessableEntity which is 422 if you want to go by the book and add the details on ProblemDetails(check below).
Now, this is not mandatory. At the end of the days your api is a contract between you and your client, and you should define this hand check for your integration. What are you expecting on the server side and what is the client going to receive.
Now for global exceptions you can inject a middleware and capture them and provide a specified details of what happened. In this case you should return a ProblemDetails instance.
ProblemDetails class
The ietf tried to create a standard by specifying the details of this class.
Now by default when you return this type you should include as well a MediaType, if the request was a get, you should return as part of the headers Accept: application/problem + json
Using a post you will be adding Content-Type:application/problem + json instead.
My recommendation:
Now for Validation issues you can check for ModelState.IsValid and if there is any error of your model it should be on the Errors property.
For exceptions go with the middleware.
Now this is not a blue print. You can extend the class or the customer may use another type of format to process your responses in case of any errors, at the end of the day as I said, this is a contract between you and your client.
Normally I use Ok() to return the 200OK message form a API controller. Also NotFound() and NoContent(). But I could not find something like Acceptable Response(406). So, my question is is it possible to return Acceptable Response(406) from API controller without using HttpResponseMessage.
I know I can use something like HttpResponseMessage(System.Net.HttpStatusCode.NotAcceptable) but I want to know if there is a better approach which covers all the status codes and a custom error message can be returned. Reason why I am trying to find another way is because in postman It shows wrong status code(Below Screenshot is given).
Here is a question that I think kind of what I am looking for Click Here. But I could not use CreateResponse in .NET 5.0 which I am currently working with.
Thank You.
You can return status codes using the following:
return StatusCode(StatusCodes.Status406NotAcceptable);
return new ObjectResult("Some message or object") { StatusCode = StatusCodes.Status406NotAcceptable };
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 an asp.net web api project. In my controllers I have set up the
ExceptionFilterAttribute
To catch any errors at a global level. There are two get requests being fired off from the controller method. They are failing and so I am seeing the exception being raised in the exception filter. However the exception is not showing me details of the failed request. Is it possible to get them? For example 4 GET requests might have been invoked and one of them is failing and the exception is being thrown. But all im seeing is a message saying...
The remote name could not be resolved: 'xx.xx.com'
But I need more details, like the query string etc...
The response object on the web exception is null too :-(
Within the OnException method of your ExceptionFilterAttribute you have a parameter argument of type HttpActionExecutedContext. Within the instance of this class you can access the Request and Response properties to get all the information you need, either of the request or the response. Within the ActionContext property you can even get all routing, controller and action informations.
var requestHttpMethod = actionExecutedContext.Request.Method;
var requestUri = actionExecutedContext.Request.RequestUri;
var controllerDescriptor = actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor;
var actionDescriptor = actionExecutedContext.ActionContext.ActionDescriptor;
You can also modify the response object to return a more appropriated error message. Just look around a bit what information these classes can provide you.
Thanks for looking!
Background
I am writing an API layer for a company that will be used by disparate apps and external consumers.
On the consumption end, most consumers will call a service via ajax using a jQuery $.post(); however, Internet Explorer makes our lives more challenging (of course!). For IE, I must use the XDomainRequest object because IE will not run a jQuery $.post() and because if I use IE's XMLHttpRequest(), I get a security message which is unacceptable (again--of course!):
Otherwise, XMLHttpRequest() works fine.
I am using C#, .NET MVC 4 (WebApi)
Problem
The problem is that XDomainRequest does not allow you to set the Content-Type header and always defaults to text-plain which MVC 4 WebApi controllers will not accept (and yet again--of course!!).
Question
How can I intercept requests for my controllers, detect the presence of text-plain content types and change them to text-json content-type on the fly?
Thanks in advance!
Well after two days and pouring over documentation and reading in this thread I've been able to make this work. So please forgive me if my description of the solution is poor; this is the first time I answer one of these types of threads. Since it took me so long to find the problem I figured it is worth saving some soul from falling into this same problem.
The source for my help came from the above link by byterot.
First thing I did was to create a DelegatingHandler. So in my helper folder or where every you want to create a class and call it what you want.
Here is mine:
public class ContentTypeHandler : DelegatingHandler
{
/** Check that this is an IE browser. */
if ((request.Headers.UserAgent.ToString().IndexOf("MSIE", System.StringComparison.Ordinal) > -1))
{
MediaTypeHeaderValue contentTypeValue;
if (MediaTypeHeaderValue.TryParse("application/json", out contentTypeValue))
{
request.Content.Headers.ContentType = contentTypeValue;
request.Content.Headers.ContentType.CharSet = "utf-8";
}
}
/** Return request to flow. */
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
// work on the response
var response = task.Result;
return response;
});
}
Last think that you have to do is call the Handler in your Global.asax.cs file inside your Application_Start():
GlobalConfiguration.Configuration.MessageHandlers.Add(new ContentTypeHandler());
That's all I did and it worked. So good luck I hope this helps someone.
There is no problem modifying request in HTTP stack. It can be done by writing and registering your custom DelegatingHandler before it gets to the controller. Delegating handler can take care of this early-on in the game, so your request will arrive to the controller in the form you want it to. It could be route-specific handler as well.
http://msdn.microsoft.com/en-us/library/system.net.http.delegatinghandler.aspx
Did you try $.ajax instead of $.post ?