How to communicate WCF exceptions to WebClient - wcf

I have a WCF web service which throws exceptions when invalid data is submitted. The data is submitted via an HTTP Post using the WebClient object.
Here is the code for the web service:
[WebInvoke(UriTemplate = "update", Method = "POST")]
public JsonValue Update(HttpRequestMessage message)
{
var context = new Entities();
dynamic response = new JsonObject();
// in order to retrieve the submitted data easily, reference the data as a dynamic object
dynamic data = message.Content.ReadAs(typeof(JsonObject), new[] { new FormUrlEncodedMediaTypeFormatter() });
// retrieve the submitted data
int requestId = data.requestId;
int statusId = data.statusId;
string user = data.user;
string encryptedToken = data.token;
string notes = data.notes;
// retrieve the request with a matching Id
var request = context.Requests.Find(requestId);
// make sure the request exists
if (request == null)
throw new FaultException("The supplied requestId does not exist.");
// make sure the submitted encrypted token is valid
var token = DecryptToken(encryptedToken);
if (token == null)
throw new FaultException("Invalid security token.");
// TODO: Validate other token properties (e.g. email)?
if (!request.User.UserName.Equals(token.UserName))
throw new FaultException("Invalid security token.");
// additional logic removed ...
}
And here is the code that submits data to the web service:
// use the WebClient object to submit data to the WCF web service
using (var client = new WebClient())
{
client.Encoding = Encoding.UTF8;
// the data will be submitted in the format of a form submission
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
var data = new NameValueCollection();
// prepare the data to be submitted
data.Add("requestId", requestId.ToString());
data.Add("statusId", this.StatusId);
data.Add("token", token.ToString());
data.Add("user", this.User);
data.Add("notes", this.Notes);
// submit the data to the web service
var response = client.UploadValues(this.Address, data);
}
I keep getting an exception with message: "The remote server returned an error: (500) Internal Server Error" at client.UploadValues(this.Address, data);.
Is there a way I can make sure that more detailed information is returned to the WebClient?
Also, how can I make sure that these exceptions (in the WCF service) are logged to the EventLog? (Basically I just need to know what happened).

Take a look at HttpResponseException (namespace Microsoft.ApplicationServer.Http.Dispatcher) - they're the way where you can control the response for error cases. You can specify the status code, and you have control over the HttpResponseMessage, in which you can control the message body.
On the client side, when you call WebClient.UploadValues, wrap that call and catch a WebException. If the service returns a response with a non-successful status code (e.g., 500, 400), the Response property of the WebException will have the body, in which you can read in your client.
Another option is to use HttpClient instead of the WebClient, in which case you can simply look at the HttpResponseMessage directly.

Related

Handle Azure Function input binding error

I have an Azure Function (version 4, with C# on .NET 6) that uses Cosmos DB input binding.
When the Cosmos DB input is querying for a document that does not exist it returns HTTP 404 response, however the Azure Function itself does not propagate this response and actually returns a HTTP 500 response.
How do I catch the Cosmos DB response to be able to act accordingly in the Azure function?
Or in other words - how do I perform error/http-response handling on the function's input bindings?
I hoped validating that the input argument is not null would be it,
and also tried to validate that the IEnumerable response is not empty,
but it turns out it doesn't even enter the code in the function when it gets the Cosmos DB 404 response. The function just breaks and the returns HTTP 500:
[FunctionName("GetItems")]
public IActionResult GetItems(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
[CosmosDB(
databaseName: "my-db",
containerName: "my-container",
Connection = "my-conn-string",
PartitionKey = "my-pk")] IEnumerable<MyItem> items)
{
_logger.LogInformation("Get list of items");
if (items == null)
{
_logger.LogInformation("Items object is null");
return new NotFoundResult();
}
var itemsList = items as List<MyItem>;
if (itemsList.Count == 0)
{
_logger.LogInformation("Items object is missing");
return new NotFoundResult();
}
return new OkObjectResult(itemsList.First());
}
}
Below is the example of input binding error.
[FunctionName("EventHubTrigger")]
[FixedDelayRetry(5, "00:00:10")]
public static async Task Run([EventHubTrigger("myHub", Connection = "EventHubConnection")] EventData[] events, ILogger log)
{
// ...
}
and Here is the complete documentation on Handling binding errors.

Azure service bus Message deserialize broken in core conversion

So, I've created a new Azure Functions project v3 and am porting over a subset of functions from v1 that was running on 4.6.2, while retiring the rest as obsolete. Unfortunately in the change from BrokeredMessage to Message due to changing from Microsoft.ServiceBus.Messaging to Microsoft.Azure.ServiceBus the following deserialization method is now failing with:
There was an error deserializing the object of type stream. The input source is not correctly formatted.
The problem is right there in the error, but Im not sure what the correct new approach is, its a bit unclear.
Serialize
public static Message CreateBrokeredMessage(object messageObject)
{
var message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(messageObject)))
{
ContentType = "application/json",
Label = messageObject.GetType().Name
};
return message;
}
Deserialize
public static T ParseBrokeredMessage<T>(Message msg)
{
var body = msg.GetBody<Stream>();
var jsonContent = new StreamReader(body, true).ReadToEnd();
T updateMessage = JsonConvert.DeserializeObject<T>(jsonContent);
return updateMessage;
}
Object
var fileuploadmessage = new PlanFileUploadMessage()
{
PlanId = file.Plan_Id.Value,
UploadedAt = uploadTimeStamp,
UploadedBy = uploadUser,
FileHash = uploadedFileName,
FileName = file.Name,
BusinessUnitName = businessUnitName,
UploadedFileId = uploadedFile.Id
};
```
Message.GetBody<T>() is an extension method for messages sent using the legacy Service Bus SDK (WindowsAzure.ServiceBus package) where BrokeredMessage was populated with anything other than Stream. If your sender sends an array of bytes as you've showed, you should access it using Message.Body property.
In case your message is sent as a BrokeredMessage, the receiving code will need to select either of the methods based on some information to indicate how the message was originally sent.

WCF responds with HTTP 400 to serialized JSON string from Razor Pages app

I'm trying to POST a request containing JSON from a Razor Pages app to a WCF service endpoint expecting a Json WebMessageFormat and Bare BodyStyle.The JSON passes just fine via Postman, but not when I send it through http-client. Wireshark also shows some extra bytes around JSON in the http-client produced packet that are not present in the Postman packet. Wireshark also reports this as line-based text data: application/json for the Postman packet. The .Net packet is JavaScript Object Notation: application/json.
Here's my C# code to send the JSON to the WCF endpoint:
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:8000");
dynamic foo = new ExpandoObject();
foo.position = 1;
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(foo), System.Text.Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:8000/WCFService/ControllerV1/PostJSON");
request.Headers.Add("cache-control", "no-cache");
request.Headers.Add("Accept", "*/*");
request.Headers.Add("Connection", "keep-alive");
request.Content = content;
try
{
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
}
catch (HttpRequestException e)
{
Console.WriteLine(e.Message);
}
And here's my WCF endpoint declaration:
[OperationContract, WebInvoke(Method="POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
void PostJSON(string jsonString);
I would expect the packets to produce the same response from the server, but, what appears to be the same string produces a response 200 when the packet is built by postman and a response 400 when built by .Net. I'm clearly missing something subtle here, but I can't seem to tease it out.
There are 2 possible BodyStyle for request and response, wrapped or bare. When you specify wrapped body style the WCF service expects a valid json to be passed which in your case would be
//note that property name is case sensitive and must match service parameter name
{
"jsonString": "some value"
}
And when you specify bare format the service expects only plain string value (in case of primitive type as yours) as the request like this
"some value"
When you serialize your object like this
dynamic foo = new ExpandoObject();
foo.position = 1;
string result = JsonConvert.SerializeObject(foo);
the result contains the following json
{
"position":1
}
which corresponds to wrapped format and the service returns 400: Bad Request. All you need to do is to turn this json into valid json string value like this
"{\"position\":1}"
It can be done by repeated JsonConvert.SerializeObject call
dynamic foo = new ExpandoObject();
foo.position = 1;
string wrapped = JsonConvert.SerializeObject(foo);
string bare = JsonConvert.SerializeObject(wrapped);
var content = new StringContent(bare, System.Text.Encoding.UTF8, "application/json");

Timeout using ServiceStack.Client

I have been using service stack via AJAX calls for some time without issue, but have recently created a quick winforms app which utilizes the service stack client (specifically JsonServiceClient).
However - I have hit a problem whereby I consistently get a timeout on a call which works successfully on the the first TWO attempts. It looks like either the service stack client is holding on to some resource, or I am using the client in the wrong way. It only occurs when running against a remote service (works every time on a local machine). Here is my code, and the exception:
var url = "http://www.TestServer.com/api";
var taskId = Guid.Parse("30fed418-214b-e411-80c1-22000a5b9fe5");
var email = "admin#example.com";
using (var client = new JsonServiceClient(url))
{
var result = client.Send(new Authenticate {UserName = "username", Password = "Password01", RememberMe = true});
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #1 - works fine
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #2 - works fine
try
{
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #3 - works fine
}
catch (WebException ex)
{
//Times out every time
//at System.Net.HttpWebRequest.GetRequestStream(TransportContext& context)
//at System.Net.HttpWebRequest.GetRequestStream()
//at ServiceStack.Net40PclExport.GetRequestStream(WebRequest webRequest)
//at ServiceStack.ServiceClientBase.<>c__DisplayClassa.<SendRequest>b__9(HttpWebRequest client)
//at ServiceStack.ServiceClientBase.PrepareWebRequest(String httpMethod, String requestUri, Object request, Action`1 sendRequestAction)
//at ServiceStack.ServiceClientBase.SendRequest(String httpMethod, String requestUri, Object request)
//at ServiceStack.ServiceClientBase.Send[TResponse](String httpMethod, String relativeOrAbsoluteUrl, Object request)
//at ServiceStack.ServiceClientBase.Put[TResponse](String relativeOrAbsoluteUrl, Object requestDto)
//at ServiceStack.ServiceClientBase.Put(Object requestDto)
//at SSClientIssue.Program.Main(String[] args) in c:\Users\David\Documents\Visual Studio 2013\Projects\SSClientIssue\SSClientIssue\Program.cs:line 27
throw;
}
}
After the timeout, I can close and reload the app (server stays up), and then get same behavior again (two successful calls). IIS logs show that the 3rd call does not make it to the server, so looks like a Client issue.
I have been looking at this for 8 hours and I think my eyes are starting to bleed...If anyone can help I will buy you a beer!
The issue is due to your ServiceClient requests not specifying a known response type.
Response types can either be marked on the Request DTO using the IReturn<T> marker (recommended):
public class GetAllAdminUsernamesRequest : IReturn<List<string>> { ... }
By adding this on the Request DTO, the ServiceClient is able to automatically infer and convert the response, e.g:
List<string> response = client.Get(new GetCurrentAdminUserAdminTasks());
Otherwise an alternative to specifying the Response on the Request DTO, is to specify it on the call-site, e.g:
List<string> response = client.Get<List<string>>(new GetCurrentAdminUserAdminTasks());
If you don't do this the Response is unknown so the ServiceClient will just return the underlying HttpWebResponse so you can inspect the response yourself.
HttpWebResponse tasks = client.Get(new GetCurrentAdminUserAdminTasks());
In order to be able to inspect and read from the HttpWebResponse the response cannot be disposed by the ServiceClient, so it's up to the call-site making the request to properly dispose of it, i.e:
using (HttpWebResponse tasks = client.Get(new GetCurrentAdminUserAdminTasks())) {}
using (HttpWebResponse adminUsers = client.Get(new GetAllAdminUsernames())) {}
try
{
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
}
...
Disposing of your WebResponses responses will resolve your issue.
If you don't do this the underlying WebRequest will throttle open connections and only let a limited number of simultaneous connections through at any one time, possibly as a safe-guard to prevent DDOS attacks. This is what keeps the underlying connections open and WebRequest to block, waiting for them to be released.

WCF API and API Key authorisation

Written or started to write a WEB API rest service in WCF. It's all going relatively well. However, I've come across a small problem. I've implemented this;
http://blogs.msdn.com/b/rjacobs/archive/2010/06/14/how-to-do-api-key-verification-for-rest-services-in-net-4.aspx
For key validation. (I'm not sure if this is the correct approach for WCF WEB API, since it looks more like the rest service implementation).
Anyway, it seems to work. However, when the api key is not provided the exception is not been displayed in the browser. I.e. if I provide the key, it returns correctly, if I don't it just shows a blank page.
private static void CreateErrorReply(OperationContext operationContext, string key)
{
// The error message is padded so that IE shows the response by default
using (var sr = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + APIErrorHTML))
{
XElement response = XElement.Load(sr);
using (Message reply = Message.CreateMessage(MessageVersion.None, null, response))
{
HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.Unauthorized, StatusDescription = String.Format("'{0}' is an invalid API key", key) };
responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";
reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
operationContext.RequestContext.Reply(reply);
// set the request context to null to terminate processing of this request
operationContext.RequestContext = null;
}
}
}
Instead of this showing an error, the result is a blank response. Can anyone help?