I'm consuming an ASMX service with SOAP messages. Since Xamarin doesn't support SOAP 1.2, I'm writing the Web requests manually. I'm sending a user/password to retrieve the user ID.
A successful response may look like this:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="link-to-namespace">
<soap:Body>
<ns:User>
<ns:UserId>123</ns:UserId>
</ns:User>
</soap:Body>
</soap:Envelope>
An error response may look like this:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<soap:Fault>
<soap:Code>
<soap:Value>InvalidUsernameAndPassword</soap:Value>
</soap:Code>
</soap:Fault>
</soap:Body>
</soap:Envelope>
What's the best way to parse the response? And how to check if there is an error?
Currently I just look for the presense of the <Fault>
element.
WebService class handles the request and returns the SOAP envelope as an XDocument:
class WebService
{
// Returns the SOAP envelope as an XDocument.
public XDocument Invoke(string soapAction, string method, Dictionary<string, string> parameters)
{
UserApi class acts as a proxy towards the service.
class UserApi
{
// Sends user/pwd to server to retrieve user id.
public string GetUserId(string username, string password)
{
var soapAction = "link-to-soap-action";
var method = "GetUserId";
var parameters = new Dictionary<string, string>
{
{ "Username", username },
{ "Password", password}
};
var service = new WebService();
var result = service.Invoke(soapAction, method, parameters);
XNamespace nsSoap = "http://www.w3.org/2003/05/soap-envelope";
XNamespace ns = "link-to-namespace";
// Look for the "Fault" element in the response. If present, there was an error.
var fault = result.Root.Element("{" + nsSoap + "}Body").Element("{" + ns_soap + "}Fault");
if (error != null)
{
var codeWithNs = error.Element("{" + nsSoap + "}Code").Element("{" + ns_soap + "}Value").Value;
var codeSplit = codeWithNs.Split(':');
var code = codeSplit.Length == 2 ? codeSplit[1] : codeSplit[0];
if (code == "InvalidUsernameOrPassword")
{
throw new InvalidUsernameOrPasswordException();
}
else
{
throw new Exception("Something went wrong.");
}
}
return userId = result.Descendants(ns + "UserId").First().Value;
}
public async Task<string> GetUserIdAsync(string username, string password)
{
return await Task.Run(() =>
{
return GetUserId(username, password);
});
}
}
Currently it works, at least under optimal conditions. But my solution looks dense and fragile. If I should go with deserialization, here's some pseudo code of what I'm thinking:
try
{
var serializer = new XmlSerializer(typeof(UserEnvelope));
var user = serializer.Deserialize(response);
}
catch (InvalidOperationException)
{
var serializer = new XmlSerializer(typeof(ErrorEnvelope));
var error = serializer.Deserialize(response);
if (error.Code == "InvalidUsernameOrPassword")
...etc
}