good morning, I have a solution in .net where I call the webservices of saber bargain finder max. Now I want to download the compressed information but the response object returns null. I read that you have to call the interface IClientMessageInspector BeforeSendRequest and AfterReceiveReply but I do not know how to proceed. someone will have an example or solution about it? Thank you
Asked
Active
Viewed 488 times
2
-
You don't have to do anything. WCF supports HTTP compression. Sabre enables HTTP Compression on https://webservices3.sabre.com/websvc instead of https://webservices.sabre.com/websvc . Don't use their custom compression call - it's still GZip, it compresses part of the body but *you'll* have to write the code to decompress it. – Panagiotis Kanavos Mar 02 '18 at 14:55
-
PS Fiddler can decompress compressed HTTP responses. It's indispensable during development to inspect responses, check contents, error messages etc. If you use custom compression you'll end up with a blob that you'll have to decompress in code before you inspect it. Makes debugging and testing a bit hard – Panagiotis Kanavos Mar 02 '18 at 15:04
1 Answers
2
The response for the service cannot handle the compressed response, so yes, you have to put middleware in order to process the decompression before you let it continue.
First of all, you need to import the BFM WSDL as a service instead of as a web services, so:
- Right click on "Service References" and click on "Add Service Reference..."
- Paste the WSDL URL under "Address:" and click on "Go"
- Recommendation: Download the WSDL and the associated schemas so you can modify them, since .NET has some issues handling some things, which you can manually modify. Sabre has some covered here.
- Name the service whatever you like, in this example I'll call it BargainFinderMaxRQ_4_1_0_Srvc, under "Namespace:" and click on "OK"
Then, I created 2 classes, one that will be the one calling BFM ("BFM_v410Service"), and another one for the middleware ("BFMInspector").
Let's start with the BFMInspector:
// The inspector class has to implement both IClientMessageInspector and IEndpointBehavior interfaces
public class BFMInspector : IClientMessageInspector, IEndpointBehavior
{
// This is the method to action after receiving a response from Sabre
public void AfterReceiveReply(ref Message reply, object correlationState)
{
try
{
// Get compressed response from reply and load that into a byte array.
XmlDictionaryReader bodyReader = reply.GetReaderAtBodyContents();
bodyReader.ReadStartElement("CompressedResponse");
byte[] bodyByteArray = bodyReader.ReadContentAsBase64();
// Create some helper variables
StringBuilder uncompressed = new StringBuilder();
String xmlString = "";
XmlDocument xmlPayload = new XmlDocument();
// Load the byte array into memory
using (MemoryStream memoryStream = new MemoryStream(bodyByteArray))
{
// Unzip the Stream
using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
{
byte[] buffer = new byte[1024];
int readBytes;
// Unzips character by character
while ((readBytes = gZipStream.Read(buffer, 0, buffer.Length)) != 0)
{
for (int i = 0; i < readBytes; i++)
// Append all characters to build the response
uncompressed.Append((char)buffer[i]);
}
}
xmlString = uncompressed.ToString();
xmlString = xmlString.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>", "");
}
// Convert the string into an XML
xmlPayload.LoadXml(xmlString);
// Create a new Message, which is what will substitute what was returned by Sabre
Message tempMessage = Message.CreateMessage(reply.Version, null, xmlPayload.ChildNodes[0]);
tempMessage.Headers.CopyHeadersFrom(reply.Headers);
tempMessage.Properties.CopyProperties(reply.Properties);
MessageBuffer bufferOfFault = tempMessage.CreateBufferedCopy(Int32.MaxValue);
// Replace the reply with the new Message
reply = bufferOfFault.CreateMessage();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Nothing is done here, so we simply return null
return null;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// Nothing done
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
// Add "this" as an endpoint to be inspected
clientRuntime.MessageInspectors.Add(this);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Nothing done
}
public void Validate(ServiceEndpoint endpoint)
{
// Nothing done
}
}
Now, the BFM_v410Service:
// BFM calling class
public class BFM_v410Service
{
// The constructor and CreateRequest simeply create a complete request
private BargainFinderMaxRQRequest service;
private OTA_AirLowFareSearchRQ request;
public OTA_AirLowFareSearchRS response;
private string endpoint;
public BFM_v410Service(string token, string pcc, string convId, string endpoint)
{
CreateRequest(pcc, true);
this.endpoint = endpoint;
service = new BargainFinderMaxRQRequest()
{
MessageHeader = new BargainFinderMaxRQ_3_4_0_Srvc.MessageHeader()
{
From = new From()
{
PartyId = new PartyId[]
{
new PartyId()
{
Value = pcc
}
}
},
To = new To()
{
PartyId = new PartyId[]
{
new PartyId()
{
Value = endpoint
}
}
},
ConversationId = convId,
CPAId = pcc,
Service = new Service()
{
Value = "BargainFinderMaxRQ"
},
Action = "BargainFinderMaxRQ",
MessageData = new MessageData()
{
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssK")
}
},
OTA_AirLowFareSearchRQ = request,
Security = new Security()
{
BinarySecurityToken = token
}
};
}
private void CreateRequest(string pcc, bool compressed)
{
request = new OTA_AirLowFareSearchRQ()
{
Version = "3.4.0",
POS = new SourceType[]
{
new SourceType()
{
PseudoCityCode = pcc,
RequestorID = new UniqueID_Type()
{
ID = "1",
Type = "1",
CompanyName = new CompanyNameType()
{
Code = "TN",
Value = "TN"
}
}
}
},
OriginDestinationInformation = new OTA_AirLowFareSearchRQOriginDestinationInformation[]
{
new OTA_AirLowFareSearchRQOriginDestinationInformation()
{
RPH = "1",
Item = "2018-09-21T11:00:00",
ItemElementName = ItemChoiceType.DepartureDateTime,
OriginLocation = new OriginDestinationInformationTypeOriginLocation()
{
LocationCode = "MVD"
},
DestinationLocation = new OriginDestinationInformationTypeDestinationLocation()
{
LocationCode = "KRK"
}
},
new OTA_AirLowFareSearchRQOriginDestinationInformation()
{
RPH = "2",
Item = "2018-09-28T11:00:00",
ItemElementName = ItemChoiceType.DepartureDateTime,
OriginLocation = new OriginDestinationInformationTypeOriginLocation()
{
LocationCode = "KRK"
},
DestinationLocation = new OriginDestinationInformationTypeDestinationLocation()
{
LocationCode = "MVD"
}
}
},
TravelerInfoSummary = new TravelerInfoSummaryType()
{
AirTravelerAvail = new TravelerInformationType[]
{
new TravelerInformationType()
{
PassengerTypeQuantity = new PassengerTypeQuantityType[]
{
new PassengerTypeQuantityType()
{
Code = "ADT",
Quantity = "1"
}
}
}
}
},
TPA_Extensions = new OTA_AirLowFareSearchRQTPA_Extensions()
{
IntelliSellTransaction = new TransactionType()
{
RequestType = new TransactionTypeRequestType()
{
Name = "50ITINS"
},
CompressResponse = new TransactionTypeCompressResponse()
{
Value = compressed
}
}
}
};
}
public void Execute()
{
try
{
// Instanciate the Inspector
BFMInspector inspector = new BFMInspector();
// Select the URL you'll be sending the request. I've passed this as a parameter in the constructor
EndpointAddress url = new EndpointAddress(new Uri(endpoint));
// Create a binding, with a couple of characteristics, because of the size of the response
Binding binding = new BasicHttpsBinding()
{
MaxReceivedMessageSize = Int32.MaxValue,
MaxBufferSize = Int32.MaxValue
};
// Create the executable the BargainFinderMaxPortTypeClient variable, which will allow me to call the service
BargainFinderMaxPortTypeClient execute = new BargainFinderMaxPortTypeClient(binding, url);
// Add the middleware. Here's where ApplyClientBehavior is called behind the scene and adds itself
execute.Endpoint.EndpointBehaviors.Add(inspector);
// Call BFM and successfully get the response as an OTA_AirLowFareSearchRS object
response = execute.BargainFinderMaxRQ(ref service.MessageHeader, ref service.Security, request);
Console.WriteLine(response);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Then, simply call this from a console application:
static void Main(string[] args)
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
string token = @"Shared/IDL:IceSess\/SessMgr:1\.0.IDL/Common/!ICESMS\/RESA!ICESMSLB\/RES.LB!-3146624380791354996!413892!0!1!E2E-1";
string pcc = "XXXX";
string convId = "HERE GOES YOUR CONVERSATION ID";
string endpoint = "https://webservices.havail.sabre.com";
BFM_v410Service bfm340 = new BFM_v410Service(token, pcc, convId, endpoint);
bfm410.Execute();
}
Hope this helps!

Giancarlo
- 1,446
- 8
- 17