Catching a WebFaultException's details in WCF Rest - c#

I have a server which is throwing WebFaultException
try
{
...
}
catch (InvalidPasswordException)
{
throw new WebFaultException<String>
("This is a bad password",
HttpStatusCode.Unauthorized);
}
So far so good. However when this WebFault is caught in another C# project (the client) I don't know how to get the details.
try
{
HttpWebRequest httpWebRequest = WebRequest.Create(uri) as HttpWebRequest;
httpWebRequest.Method = verb;
httpWebRequest.ContentType = "text/xml";
httpWebRequest.ContentLength = serializedPayload.Length;
using (StreamWriter streamOut = new StreamWriter(httpWebRequest.GetRequestStream(), Encoding.ASCII))
{
streamOut.Write(serializedPayload);
streamOut.Close();
}
// error here on GetResponseStream
using (StreamReader streamIn = new StreamReader(httpWebRequest.GetResponse().GetResponseStream()))
{
string strResponse = streamIn.ReadToEnd();
streamIn.Close();
return strResponse;
}
}
catch (Exception e)
{
// The exception is of type WebException with unauthorized but don't know where the details are
}
This code catches a WebException which I can't find the details for, just the unauthorized.
Thanks
UPDATE 1: When I do the request in fiddler the response body is the detail but as that exception is thrown before the response body is ever read then it isn't showing up. So the question is how do I read the response body DESPITE a non 200 being thrown.
Fiddler Raw Response:
HTTP/1.1 401 Unauthorized
Server: ASP.NET Development Server/10.0.0.0
Date: Tue, 12 Oct 2010 22:42:31 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 100
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Email/Password Mismatch</string>

I'm just guessing, but I think you just need to check the .StatusCode member of the result of GetResponse() and don't try to call GetResponseStream() unless it's 200. If it's an error code (401 in your case), then the error details should be in the .Content member of GetResponse().
Something like:
var r = httpWebRequest.GetResponse();
if(r.StatusCode != 200)
{
MessageBox.Show(r.Content); // Display the error
}
else
{
var streamIn = new StreamReader(r.GetResponseStream());
string strResponse = streamIn.ReadToEnd();
streamIn.Close();
return strResponse;
}

WebException has a Response property that you can cast to a HttpWebResponse and then call GetResponseStream on.
On a related note, go get yourself the Microsoft.Http library from the WCF REST Starter kit, it is a way better client library that HttpWebRequest/Response. For more details, I have some blog posts here: http://www.bizcoder.com/index.php/2009/12/08/why-the-microsoft-http-library-is-awesome/

I've used this
try
{
//wcf service call
}
catch (FaultException ex)
{
throw new Exception( (ex as WebFaultException<MyContractApplicationFault>).Detail.MyContractErrorMessage );
}

Related

HTTP 500 response is received when using C# HttpClient

I have developed a C# application that calls a REST service existing in some PC in the network.
This is the code to make a request:
public async Task<bool> OpenDoorAsync(string name, int delay)
{
var data = await CallApiAsync("api/door/remoteOpenByName", new Dictionary<string, string> { { "doorName", name }, { "interval", delay.ToString() } });
return data.IsSuccess;
}
private async Task<ResponseData> CallApiAsync(string endPoint, Dictionary<string, string> parameters)
{
try
{
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Connection.ParseAdd("keep-alive");
var content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
string fullUri = "http://192.168.0.122:8088/api/door/remoteOpenByName?doorName=10.185.85.237-1&interval=5&access_token=1234";
HttpResponseMessage response = await client.PostAsync(fullUri, content);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ResponseData>(responseBody);
}
}
catch (Exception ex)
{
OnError("Existió un error al realizar la llamada.", ex);
return new ResponseData()
{
message = "failed"
};
}
}
Entry point is OpenDoorAsync, called this way, from a Winform form:
await _device.OpenDoorAsync(TxtNombrePuerta.Text.Trim(), IntInterval.Value);
Well, after the execution of PostAsync method, a HTTP 500 error is returned:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: close
Date: Thu, 28 Jan 2021 21:06:35 GMT
Set-Cookie: JSESSIONID=4062B932CDB44B4CA3FCCC275937AC15; Path=/; HttpOnly
Server: Apache-Coyote/1.1
Content-Length: 2580
Content-Language: en
Content-Type: text/html; charset=utf-8
}}
However, if I make the same request using Google Chrome RESTED extension, it works perfectly:
Just in case, I analyzed Google Chrome developer tools after the RESTED call and I have not found anything weird. I thought maybe I missed to send something else in the headers.
Does anybody know what is happening with the call from the C# application? Clearly, I am not doing something that RESTED is.
I don't really know why it does not work when using HttpClient class, however, I solved the problem installling an using RestSharp NuGet package.
Finally, the code was reduced to this:
private ResponseData CallApi(string endPoint, Dictionary<string, string> parameters)
{
try
{
string fullUri = $"http://{GetServerIp()}:{((MainWindow)MainWindow).ServerPort}/{endPoint}?{GetQueryParameters(parameters)}";
var client = new RestClient(fullUri);
var request = new RestRequest(Method.POST);
var response = client.Execute(request);
return JsonConvert.DeserializeObject<ResponseData>(response.Content);
}
catch (Exception ex)
{
OnError("Existió un error al realizar la llamada.", ex);
return new ResponseData()
{
message = "failed"
};
}
}
Your working example is passing cookies, which may be required for the API you're calling.

C# WebRequest returns intermittent 500 errors from valid URLs

I have an application that was written to read and parse a large quantity of XML from a web service. It's been working fine for months. Quite suddenly one particular section of the code is throwing 500 errors when trying to get responses from the server.
On investigation, hitting one of these problematic URLs in a browser gets a valid response and the expected XML. To further add to the mystery, this section iterates through a number of requests varying the parameters in the URL, and the failures are intermittent - there's not one particular call that's causing the issue.
I have tried hitting the failure, requesting via the browser, then proceeding with the url from the code in debug mode: this still results in a 500 error.
Here's the code - there's some notes on attempted fixes in the comments. Essentially I've seen a number of questions on this already, and the suggested fix is to add a user-agent. It didn't work in this case, and previous questions haven't had intermittent problems.
private static HttpWebResponse RetryGetResponse(WebRequest request)
{
int failCounter = 1;
while (failCounter < 20)
{
try
{
// I tried this as a fix for the issue
// the code functions but it didn't fix the bug
// HttpWebRequest req = (HttpWebRequest)request;
// req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36";
// return (HttpWebResponse)req.GetResponse();
// this is original code
return (HttpWebResponse)request.GetResponse();
}
catch (WebException ex)
{
if (ex.Status != WebExceptionStatus.ReceiveFailure &&
ex.Status != WebExceptionStatus.ConnectFailure &&
ex.Status != WebExceptionStatus.KeepAliveFailure)
{
LogError(string.Format("Failed to contact server, retrying {0} times", failCounter), ex);
Thread.Sleep(failCounter * 10000);
failCounter++;
}
}
}
throw new Exception("Fatal error - unable to connect");
}
public static void PopulateActions()
{
// this fetches data to iterate through
DataTable customers = GetTableData("Customers");
foreach (DataRow row in customers.Rows)
{
string url = string.Format(baseUrlTemplate, "subscribers", "actions.xml?email=" + HttpUtility.UrlEncode(row["EmailAddress"].ToString()));
WebRequest request = WebRequest.Create(url);
request.Timeout = 999999;
request.Credentials = new NetworkCredential(networkKey, "");
using (WebResponse response = RetryGetResponse(request))
{
XDocument doc = XDocument.Load(response.GetResponseStream());
IEnumerable<XElement> emails = doc.Root.Elements("Email");
foreach (XElement email in emails)
{
// parse the xml and record to Db
}
}
}
}
What else can I do to investigate and attempt to fix this infuriating intermittent problem?
EDIT: Response details
StatusCode: InternalServerError
StatusDescription: "Internal Server Error"
Headers:
{
Vary: Accept-Encoding
X-Cache: MISS
X-Code: 500
X-Error-Type: WS-Failure
X-Request-Duration: 30457ms
X-Request-Id: 979b6ee7-59c0-41e8-8acc-a38ec9b4087b
transfer-encoding: chunked
Connection: keep-alive
Cache-Control: private, s-maxage=0
Content-Type: application/xml
Date: Thu, 05 Jan 2017 11:02:32 GMT
P3P: CP="OTI DSP COR CUR IVD CONi OTPi OUR IND UNI STA PRE"
Server: cm-api-server
}

RestSharp - How to handle non-200 responses? RestClient throws exception on Execute

I'm using RestSharp in Windows Phone 8.1 application. RestClient throws exception when server returns response with code different than 200. Wiki says that I should get response with correct status code. I want to get content of response because server returns error messages.
private async Task<T> ExecuteAsync<T>(IRestRequest request)
{
if (!_networkAvailableService.IsNetworkAvailable)
{
throw new NoInternetException();
}
request.AddHeader("Accept", "application/json");
IRestResponse<T> response;
try
{
response = await _client.Execute<T>(request); //here I get exception
}
catch (Exception ex)
{
throw new ApiException();
}
HandleApiException(response);
return response.Data;
}
private void HandleApiException(IRestResponse response)
{
if (response.StatusCode == HttpStatusCode.OK)
{
return;
}
//never reach here :(
ApiException apiException;
try
{
var apiError = _deserializer.Deserialize<ApiErrorResponse>(response);
apiException = new ApiException(apiError);
}
catch (Exception)
{
throw new ApiException();
}
throw apiException;
}
sample of server response:
HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 86
Content-Type: application/json;charset=UTF-8
Expires: -1
Server: Microsoft-IIS/8.0
Access-Control-Allow-Origin: *
X-Powered-By: ASP.NET
Date: Mon, 28 Sep 2015 12:30:10 GMT
Connection: close
{"error":"invalid_token","error_description":"The user name or password is incorrect"}
If you are working under Windows Phone 8.1, you're using RestSharp Portable (https://github.com/FubarDevelopment/restsharp.portable) (probably).
Use this:
var client = new RestClient();
client.IgnoreResponseStatusCode = true;
With this, you don't get exception with 404 for example.
I hope it will be helpful :)

http response code is OK when it should not be

I have a restful web service. If someone calls a particular method with the wrong key, I return this:
OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
response.StatusCode = System.Net.HttpStatusCode.Unauthorized;
response.StatusDescription = "Invalid key provided.";
If I call it using Firefox with Firebug running, send an invalid key and handle the error in my calling code like:
string serviceResponse = "";
try
{
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
string statusCode = response.StatusCode.ToString();
string statusDescription = response.StatusDescription.ToString();
}
}
catch(Exception ex)
{
serviceResponse = ex.Message;
}
the Response headers show:
HTTP:1.1 200 OK
Having handled the error, I can show a message on the screen. However, as soon as the 'using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)' line is hit, an error is thrown and the response object seems to be discarded.
If I don't put the HttpWebResponse code in a try catch block, a 401 unauthorized message shows in the browser but Firebug now shows:
HTTP:1.2 500 Internal Server Error
... which is weird, as I am sending a 401 back.
What is the point, in a Restful web service, of setting the response StatusCode and StatusDescription as, in a browser calling the web service, as soon as a 401 or 500 StatusCode is encountered, an error is thrown and the Response object discarded.
If a 500 or 401 is returned, how are you supposed to read the StatusDescription?

HttpWebRequest - GetResponse() - WebException ReceiveFailure

I have a WCF service that is running frequent (1000+) outbound connections per minute to external APIs.
My code throws the following exceptions frequently, but not always showing that is is a WebException with the WebException status property being ReceiveFailure
The code that is making the outbound request is the following:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(paramBuilder.ToString());
request.ServicePoint.ConnectionLeaseTimeout = 0;
request.Method = "GET";
request.Timeout = 33000; //33 Second Timeout Is By Design
Stream stream = default(Stream);
HttpWebResponse response = default(HttpWebResponse);
try
{
response = (HttpWebResponse) request.GetResponse();
stream = response.GetResponseStream();
reader = new StreamReader(stream,Encoding.UTF8);
string str = reader.ReadToEnd();
return str;
}
catch (WebException exception)
{
//Handle WebException
}
catch (Exception exception)
{
//Handle Exception
}
finally
{
if (reader != null)
reader.Dispose();
if (response != null)
response.Close();
if (stream != null)
stream.Dispose();
}
The exception stack trace shows that the exception is caused from GetResponse().
What could be causing this to happen that I receive an occasional WebException -ReceiveFailure.
I have already reference the MSDN documentation for this status, but that doesn't help me.
Shooting in the dark here...
There is a special condition, while waiting for response: if the system clock is being set automatically by the Windows Time service, or manually, you may experience some unpredictable results.
If you're sending your requests over HTTPS, maybe you're facing a regular timeout that was wrongly thrown as a ReceiveFailure.
Check this article for more information: http://support.microsoft.com/kb/2007873
I have a related problem and I realise a few things while I was searching for a solution.
WebExceptionStatus enum is not equivalent to http status code that the API you call returned. Instead it is a enum of possible error that may occour during a http call.
The WebExceptionStatus error code that will be returned when you receive an error (400 to 599) from your API is WebExceptionStatus.ProtocolError aka number 7 as int.
When you need to get the response body or the real http status code returned from the api, first you need to check if WebException.Status is WebExceptionStatus.ProtocolError. Then you can get the real response from WebExceptionStatus.Response and read its content.
Sometimes the timeout is handled by the caller (aka your code) so you do not have a response in that case. So you can look if WebException.Status is WebExceptionStatus.Timeout
This is an example:
try
{
...
}
catch (WebException webException)
{
if (webException.Status == WebExceptionStatus.ProtocolError)
{
var httpResponse = (HttpWebResponse)webException.Response;
var responseText = "";
using (var content = new StreamReader(httpResponse.GetResponseStream()))
{
responseText = content.ReadToEnd(); // Get response body as text
}
int statusCode = (int)httpResponse.StatusCode; // Get the status code
}
else if (webException.Status == WebExceptionStatus.ProtocolError)
{
// Timeout handled by your code. You do not have a response here.
}
// Handle other webException.Status errors. You do not have a response here.
}

Categories