WCF consuming multiple possible REST responses - c#

I've recently started reading about the WebHttpBinding usage in WCF and being able to consume REST services, however, I've been stumped on this one for a bit now.
I'm writing a service that makes a single request to a given api, however, can return one of many responses.
For example, the generic response:
<ActualResponse>
<ResponseItem>
<Name />
<Area />
</ResponseItem>
</ActualResponse>
However, if something were invalid in the outgoing request or the responding service itself was experiencing any sort of issue a returning response would be:
<ErrorResponse>
<Message />
</ErrorResponse>
Pedram Rezaei had a great post on consuming REST services, which is where I borrow most of my information from. From what I can tell, we can create an object as long as the object has serializable attributes defined. The issue being, there's no condition to which class to be created(ErrorResponse/ActualResponse).
I'm unsure if I should be looking at some sort of TryParse functionality that sends the initial request and catches the error if no deserialization can occur or if there is a more elegant approach.
I'm fairly new to the WCF world, so the possibility exists I may be entirely overlooking something!

I think you can borrow some practice from SOAP, which has a hierarchy like so:
<soap:Envelope>
<soap:Body>
... message contents
</soap:Body>
</soap:Envelope>
I'm not suggesting that you use SOAP, I'm suggesting that you learn from the design employed by SOAP. What SOAP does is embed the successful (or in your words "actual") response within the Body, or return a soap:Fault within the Body.
a success in SOAP might look like this:
<soap:Envelope>
<soap:Body>
<ActualResponse>... </ActualResponse>
</soap:Body>
</soap:Envelope>
while a fault might look like this:
<soap:Envelope>
<soap:Body>
<soap:Fault>... </soap:Fault>
</soap:Body>
</soap:Envelope>
In your case, you might have this:
<ServiceResponse>
<ActualResponse> ... </ActualResponse>
</ServiceResponse>
or
<ServiceResponse>
<Fault> ... </Fault>
</ServiceResponse>
And XML Serialization is really good at that. . .
But it sounds like you have no control over the envelope. The fact is that you can get multiple different responses. To handle this, you could wrap the actual response received in a contrived XML envelope, and deserialize the result of that.
If you get <ActualResponse>...</ActualResponse> , wrap it in a deserializable envelope to get something like <ServiceResponse><ActualResponse>...</ActualResponse></ServiceResponse>, then deserialize.

Related

I Getting null Response From sabre service GetReservation

I am getting null response from sabre web service when call GetReservation Request
<GetReservationRQ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.18.0">
<Locator xmlns="http://webservices.sabre.com/pnrbuilder/v1_18">Its Unique </Locator>
<RequestType xmlns="http://webservices.sabre.com/pnrbuilder/v1_18">Stateful</RequestType>
<ReturnOptions xsi:type="ReturnOptions" xmlns="http://webservices.sabre.com/pnrbuilder/v1_18">
<ViewName>VaDefaultWithPq</ViewName>
</ReturnOptions>
</GetReservationRQ>
in this ReturnOptions tag one more element is missing is ResponseFormate that default value is STL so thats why it can not show on xml writing.
Is there a reason you are using "It's Unique" as a record locator? You should put a valid record locator there or delete the node if you are reading a PNR you have in your work area.
Can you post the Sabre endpoint you are pointing your application to?

Web service (asmx) to read in customer's soap message c# - null object error

I am pretty new with web services and WSDLs.
What I want to do is to build a web service to read in a customer's SOAP message and process it.
The soap message sent to me is as follows (note I have trimmed the SOAP message right down)
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">
<soap2:Header>
<wsa:Action>http://htng.org/PWSWG/2010/12/ReservationSynch_SubmitRequest</wsa:Action>
<wsa:ReplyTo>
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
</wsa:ReplyTo>
</soap2:Header>
<Body>
<OTA_HotelResNotifRQ>
<POS>
//More tags
</POS>
<HotelReservations>
<HotelReservation RoomStayReservation="true">
<UniqueID Type="14" ID="59071IC000041" />
</HotelReservation>
</HotelReservations>
</OTA_HotelResNotifRQ>
</Body>
</Envelope>
This is my web service asmx (again I have trimmed it right down)
[WebMethod]
[SoapDocumentMethodAttribute("http://htng.org/PWSWG/2010/12/ReservationSynch_SubmitRequest", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Bare)]
public OTA_HotelResNotifRS ReservationSynch_SubmitRequest(OTA_HotelResNotifRQ OTA_HotelResNotifRQ)
{
}
Problem: When the message comes in, the method ReservationSynch_SubmitRequest gets fired but OTA_HotelResNotifRQ is null. The customer gave me the WSDL, as far as am concerned the class is correct. Though I notice the <Body> tag and is not <soap2:Body>. Does this make a difference?
I cannot change customer's message, I can only change my web service to read what they have sent to me. Am I missing something in my asmx?
If you are interested, here is their WSDL https://interface.synxis.com/interface/ota2010av2/OTA2010A.svc.wsdl
Found the problem!
I changed the namespace for a couple of the classes, which affected the schema files in the WSDL.

C# returning null on certain SOAP requests

We have been battling with a SOAP related problem for some time. We have a SOAP based API that can be called from a number of languages (PHP, Ruby etc). C#, however, seems to choke in certain circumstances.
Unfortunately, it is not clear to us why it dies. We are not C# people, but we did get an external, C# person to look at the problem. And, they were also baffled!
The wdsl can be seen here: http://sandbox.knowledgetree.com/ktwebservice/webservice.php?wsdl (yes, its large).
From C#, session creation, and several other calls work happily. However, the get_folder_contents call fails. The call executes, and fiddler shows valid XML being returned. However, C#'s return value is null.
The request, per Fiddler, is as follows:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<s:get_folder_contents>
<session_id xsi:type="xsd:string">or8llmjj5rm7co9h5p2k762s77</session_id>
<folder_id xsi:type="xsd:int">99</folder_id>
<depth xsi:type="xsd:int">1</depth>
<what xsi:type="xsd:string">DFS</what>
</s:get_folder_contents>
</s:Body>
</s:Envelope>
(I've added spaces and line breaks to the fiddler logs).
The response, per Fiddler (but, again, formatted for clairty), is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns4="urn:KnowledgeTree" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<SOAP-ENV:get_folder_contentsResponse>
<item xsi:type="ns4:kt_folder_contents">
<status_code xsi:type="xsd:int">0</status_code>
<message xsi:type="xsd:string"></message>
<folder_id xsi:type="xsd:int">99</folder_id>
<folder_name xsi:type="xsd:string">Nic</folder_name>
<full_path xsi:type="xsd:string">Nic</full_path>
<items xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="ns4:kt_folder_item[0]" xsi:nil="true"/>
</item>
</SOAP-ENV:get_folder_contentsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The (crude) C# application we used to test is as follows:
public partial class Form1 : Form
{
private string _sessionId;
private KnowledgeTreePortClient _webService;
public Form1()
{
InitializeComponent();
_webService = new KnowledgeTreePortClient();
_webService.loginCompleted += new EventHandler<loginCompletedEventArgs>(WebLoginCompleted);
_webService.loginAsync("username", "secret_password", "nil", "c-sharp-test");
}
private void WebLoginCompleted(object sender, loginCompletedEventArgs e)
{
_sessionId = e.Result.message;
_webService.get_folder_contentsCompleted += GetFolderComplete;
_webService.get_folder_contentsAsync(_sessionId, 99,1, "DFS");
}
private void GetFolderComplete(object sender, get_folder_contentsCompletedEventArgs e)
{
}
}
I'd prefer to fix this from the client (C#) side. But, any guidance as to what we are doing wrong would be much appreciated!
renen.
I tried it and got the same response.
The way I resolved it and got the response you're looking for is by adding a web reference instead. To do this right click on the references folder of your project, select add service service reference, click on the web reference button on the bottom and then proceed from there.
The proxy classes generated by that method behave as you'd expect.
As to why the service reference is not working I'm still looking into that....
EDIT:
So I've check the response Xml against the WSDL and I cannot spot any namespace mismatches. I found a couple of articles detailing how to resolve sub elements (like arrays) being null which was mostly because of a namespace mismatch - but in your case the whole kt_folder_contents value is bull and as far as I can see the namespace (urn:KnowledgeTree) in the generated wrappers is correct.
I hope someone else can give a fix for this - else if the Web Reference generated proxy works for you...
Not sure whether this makes a difference in view of the comments regarding true root cause but I have found that C#/VS interprets the entire return value as null when there is an array within the SOAP response that contains a xsi:nil="true"/> value as in:
items xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="ns4:kt_folder_item[0]" xsi:nil="true"/>
I am trying to consume the webservice in a WINFORMS application and responses without this array value seem to work fine but the two that show null return values in their response object both have the nil="true" for an array that is part of the object. Don't know if it's merely coincidence but appears to be related to comment on profile (WS-I BP1) compliance?
Alan
I came across this problem and the Web Reference proxy workaround that #QuintonBernhardt suggested worked for me. Just want to mention how to add Web Reference in recent Visual Studio versions (2017+):
In regular add service reference dialog, choose "Advanced"
Then in the compatibility section select "Add Web Reference"

Webservice response with nullable doubles is not deserialized properly

I'm calling a webservice from one of our partners. I have no problem calling it and I get a result.
The response has nullable doubles. In fiddler I see in the XML of the response these doubles have values, but somehow these fields do not get deserialized to my proxy class because these fields have the value null there.
This is a summary of the response xml I get, somehow the value in id27 does not get serialized, but the boolean in id26 does. Why?
<extras soapenc:arrayType="ns2:Extra[16]" xsi:type="soapenc:Array">
<extras href="#id1"/>
<extras href="#id2"/>
...
</extras>
...
<multiRef id="id1" soapenc:root="0"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="ns6:Extra" xmlns:ns6="http://model.bookingservice.app.traserv.com"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<assignments xsi:type="ns6:Assignment" xsi:nil="true"/>
<category xsi:type="xsd:string">def_Surety</category>
...
<orderUnit xsi:type="xsd:string">Verblijf</orderUnit>
<payAtLocation href="#id26"/>
<price href="#id27"/>
<priceNotes xsi:type="xsd:string" xsi:nil="true"/>
...
</multiRef>
...
<multiRef id="id26" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:boolean" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">true</multiRef>
<multiRef id="id27" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:double" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">345.0</multiRef>
I found WCF clients and axis webservice do have these problems.
You can solve it in the proxy, which is obviously not nice because you have to edit generated classes. Or you can change the webservice, which is also a problem because you have not always control over that one.
Anyway I found these items about it:
http://www.tomergabel.com/GettingWCFAndApacheAxisToBeFriendly.aspx
Why won't .NET deserialize my primitive array from a web service?

Consuming in Java a .NET Web Service that requires a custom SOAP Header

So I need to consume a Web Service that uses a custom SoapHeader, as described below. What is the simplest way to pass the correct values through this header using Java. I'm using Netbeans.
<?xml version="1.0" encoding="utf-8"?\>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<CustomSoapHeader xmlns="http://snip">
<UserName>"string"</UserName>
<Password>"string"</Password>
</CustomSoapHeader>
</soap:Header>
<soap:Body>
<SomeWebMethod xmlns="http://snip" />
</soap:Body>
</soap:Envelope>
EDIT: What's the best way to display XML on Stack Overflow?
It might help to add that the Web Service is implemented in .NET and I cannot change the server side code.
Here are the basic steps, assuming you're doing this on the client side:
Install a HandlerResolver on your service interface (service.setHandlerResolver())
Override HandlerResolver.getHandlerChain() to insert your own implementation of SOAPHandler
Implement SOAPHandler.handleMessage() to modify the SOAP header before it's sent out
You can pass parameters to your handler through the request context:
Map<String, Object> context = ((BindingProvider) port).getRequestContext();
context.put("userName', "foo");
...
in handleMessage() you can get at the header like this:
public boolean handleMessage(SOAPMessageContext context) {
...
SOAPMessage msg = context.getMessage();
msg.getSoapHeader();
...
}
Hope that helps. I'm guessing there's also a way to do this stuff with annotations as well.

Categories