I'm writing a Web API in using the MVC 4 Web API framework. I'm using BASIC authentication with a custom authentication message handler inheriting from DelegatingHandler.
Everything is working peachy, except my error handling.
I'm using the Web API as a wrapper over an existing authentication mechanism and an exception is thrown when the user's password is expired. Within the API action methods, I am able to handle this easily by throwing an HTTPResponseException and I get a tidy little json object such as below:
{
"Message": "Password is expired."
}
If I try this same approach in my Auth Message Handler, I get all the nasty .NET response output.
<html>
<head>
<title>Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.</title>
<style>
body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}
p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
... you get the idea ...
I think I'm close... in SendAsync
catch (Exception ex)
{
var response = request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex.Message);
response.Headers.Add("WWW-Authenticate", "Basic realm=\"myrealm\"");
}
Here, perhaps if I can somehow get a Task<HttpResponseMessage> from my response object to return, I think I might be ok.
Basically my question is "How should exception handling really be done so that I am able to return a nice json/xml object to the user?"
One more thing to note, this is .NET 4, not 4.5. I've seen a few examples using the await keyword, but that won't work in this scenario.
You can wrap your response in a task:
catch (Exception)
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var response = request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex.Message);
response.Headers.Add("WWW-Authenticate", "Basic realm=\"myrealm\"");
return response;
});
}
return base.SendAsync(request, cancellationToken);
Related
I have a service which is consuming an SMS REST API using HttpClient:
HttpClient http = this._httpClientFactory.CreateClient();
// Skipped: setup HttpRequestMessage
using (HttpResponseMessage response = await http.SendAsync(request))
{
try
{
_ = response.EnsureSuccessStatusCode();
}
catch (HttpRequestException)
{
string responseString = await response.Content.ReadAsStringAsync(); // Fails with ObjectDisposedException
this._logger.LogInformation(
"Received invalid HTTP response status '{0}' from SMS API. Response content was {1}.",
(int)response.StatusCode,
responseString
);
throw;
}
}
The API returns an error, but I would like to be able to log it. So I need to log both the failing status code (which I can read from response.StatusCode) and the associated content (which may contain additional error useful details).
This code fails on the instruction await response.Content.ReadAsStringAsync() with this exception:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.HttpConnection+HttpConnectionResponseContent'.
Module "System.Net.Http.HttpContent", in CheckDisposed
Module "System.Net.Http.HttpContent", in ReadAsStringAsync
Some sources suggest that you shouldn't read the response content when the status code is not in the success range (200-299), but what if the response really contains useful error details?
.NET version used: .NET Core 2.1.12 on AWS lambda linux runtime.
OK, apparently this is a known issue in the .NET API, which has been addressed in .NET Core 3.0. response.EnsureSuccessStatusCode() is actually disposing the response content. It was implemented this way to supposedly help users:
// Disposing the content should help users: If users call EnsureSuccessStatusCode(), an exception is
// thrown if the response status code is != 2xx. I.e. the behavior is similar to a failed request (e.g.
// connection failure). Users don't expect to dispose the content in this case: If an exception is
// thrown, the object is responsible fore cleaning up its state.
This is an undesirable behavior which was removed from 3.0. In the meantime, I just switched to use IsSuccessStatusCode before the log:
HttpClient http = this._httpClientFactory.CreateClient();
// Skipped: setup HttpRequestMessage
using (HttpResponseMessage response = await http.SendAsync(request))
{
if (!response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync(); // Fails with ObjectDisposedException
this._logger.LogInformation(
"Received invalid HTTP response status '{0}' from SMS API. Response content was {1}.",
(int)response.StatusCode,
responseString
);
_ = response.EnsureSuccessStatusCode();
}
}
A little bit more redundant, but it should work.
We are having an Web API exposed by 3rd party which we do not have any control. It looks like they are wrapping the custom error message model on throwing back 400 (Bad Request).
We get custom error message model when using Postman
{
"Message": "Site Name Exists",
"ErrorId": "<some-guid>",
"ErrorCode": 400,
"GeneratedTime": "<some-datatime>",
"RequestedUri": "origin-api-uri"
}
We tried using HttpWebResponse or WebResponse to get the response from API but it throws exception with 400 code on httpWebRequest.GetResponse(). It does not give us the expected response as we get in Postman but it says 400 (Bad Request) and with default error message, 'The remote server returned an error'.
We want to get original error message as Postman. Any thoughts?
By default, most exceptions are translated into an HTTP response with status code 500, Internal Server Error, or 400 bad Request and this is very painful situation. You need to catch WebException in catch block.
refer to: Exception Handling in ASP.NET Web API
Give it a try.
catch (WebException ex)
{
StreamReader sr = new StreamReader(ex.Response.GetResponseStream());
Console.WriteLine(sr.ReadToEnd());
}
I'm creating a REST Web API in C# using ASP.NET Web API for MVC 5.
I've created an abstract class BaseApiController, which all API controllers extend. In this base controller I handle all the Exceptions and everything the controllers need to properly work.
I've already implemented exception handling for the following Exceptions:
public override async Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
{
string controllerName = null;
string actionName = null;
try
{
SetContext(controllerContext);
SetServices();
await AuthenticateUser();
controllerName = controllerContext?.Controller?.GetType().FullName;
var services = controllerContext?.ControllerDescriptor?.Configuration?.Services;
actionName = services?.GetActionSelector()?.SelectAction(controllerContext)?.ActionName;
return await base.ExecuteAsync(controllerContext, cancellationToken);
}
catch (HttpResponseException e)
{
ClientDataService.Logger(e, $"{controllerName}.{actionName}",
$"Response -> Status Code: {(int)e.Response.StatusCode}, Reason: {e.Response.ReasonPhrase}",
SystemLogOriginTypesEnum.WEBSITE_API);
throw;
}
catch (Exception e)
{
ClientDataService.Logger(e, $"{controllerName}.{actionName}",
$"Response -> Status Code: {(int)HttpStatusCode.InternalServerError}, Reason: Internal server error",
SystemLogOriginTypesEnum.WEBSITE_API);
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("Internal server error"),
ReasonPhrase = "Internal server error"
});
}
The Controllers throw HttpResponseException mainly when mandatory parameters aren't passed or properly setup (HTTP status code
400), the second is when something went terribly wrong (HTTP status code 500).
My question is what kind of HTTP status code should the API respond when a service throws exceptions like a NotSupportedException or a custom InvalidSortAttributeException?
I don't mean HTTP status categories, i.e., 4XX vs 5XX, since the InvalidSortAttributeException should be a 4XX category because the error gets thrown from an invalid request, maybe a 400 (Bad Request)!?
Although the second, I believe could fall on both categories since the server doesn't support the request but it doesn't exactly mean it will not support it in the future, maybe a 406 (NotAcceptable) or a 501 (NotImplemented)?
The 4XX is built specifically for user errors
The 5XX is built for internal server errors
You have to respond with an intelligent code for the scenario. If there is a notsupported exception but your code caller constructed a not supported scenario, then it's 500. If the user managed to put together a bad request, it should be 4XX.
Note also that there's a difference between missing an attribute in the JSON request (400) and missing a value in the url (404) or the resource is no longer available (404).
Information of what I have and what I am trying to achieve
I am using ServiceStack and have it up and running for what I need, however I am unable to find out how to disable the Body/Content for uncaught exceptions.
I have ServiceStack handling ALL routes.
If I navigate to a route which is not mapped to ServiceStack, I get a StatusCode of 404 (perfect) and content in the Body of the response of "Handler for Request not found: ...."
If my code throws an uncaught exception, ServiceStack will kindly return a relevant StatusCode, however it also returns a ResponseStatus with ErrorCode and Message populated.
I have DebugMode turned off, this disables the StackTrace, however I want to completely mute the entire Body of the response for exceptions.
What I have tried
I have tried a Response Filter of the following:
ResponseFilters.Add((req, res, dto)) =>
{
if (dto is Exception) res.Close();
});
it unfortunately did not work.
What I want to avoid
try{
return service.GetResponse();
} catch (Exception) {
return new HttpResult(.....);
}
My Question
How do I disable the response body for all uncaught exceptions, but still return the StatusCode? I would like null returned in the body, and StatusCodes to remain in tact.
I've tried to make my question clear, but if I have been a bit vague in any way, please ask me questions.
You should be able to override the default exception handling behavior by overriding HandleException of ServiceBase.
You can inspect the default implementation here: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.ServiceInterface/ServiceBase.cs#L228.
How can I get the Compiler Error Message if I have the pageUrl?
I tried using the HttpWebRequest class, but I haven't gotten the result yet.
I have collection of pages, that must execute automatically, and if the page fails, I need it to create a log.
Thank you
You can catch all application errors in application global class (global.asax) in Application_Error handler.
Other way. You can catch exceptions in custom error module as well, just register you module in <httpModules> section and implement following function there:
void context_Error(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
Exception ex = context.Server.GetLastError();
//... here goes some code
}
Thus you have to ways to catch any error. Other task is to request all pages. As I can see from your post, you've already have such solution.
string pagetext = (new System.Net.WebClient()).DownLoadString(<url>);
//Add a better control here
if(pagetext.Contains("Server Error"))
{
`enter code here`
}
You can write a program to visit the pages, if the response of the request is a HTTP error than you can investigate further.
If you do not want to write your own program to detect errors originating from HTTP requests, you can use a testing framework like selenium.
Disclaimer: I do very little with ASP.NET type stuff.
ELMAH might help you a bit. It's an error logger for ASP.NET projects.
Where are you trying to catch these exceptions? In your website, or in an external application that is crawling a site?
In an external application, using an HttpWebRequest you'd do something like this:
string urlToTry = "http://www.example.com/ServerErrorPage.htm";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToTry);
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Process your success response.
}
catch (WebException we)
{
HttpWebResponse error = (HttpWebResponse)we.Response;
// If you want to log multiple codes, prefer a switch statement.
if (error.StatusCode == HttpStatusCode.InternalServerError)
{
// This is your 500 internal server error, perform logging.
}
}
The WebException class will give you messages like:
The remote server returned an error: (500) Internal Server Error.
Once you cast them to an HttpWebResponse, you can get at the StatusCode and perform whatever logging you require.