Using WS2007FederationHttpBinding with SOAP 1.1 - wcf

Is there a way to use WS2007FederationHttpBinding binding, but generate SOAP 1.1 request envelop? I need to use WS2007FederationHttpBinding to authenticate using a bearer token acquired from an STS service. Here is my bindings:
private static Binding GetWS2007FederationHttpBinding()
{
var binding = new WS2007FederationHttpBinding(
WSFederationHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.NegotiateServiceCredential = false;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
//binding.MessageVersion.Addressing = AddressingVersion.WSAddressingAugust2004;
//binding.MessageVersion.Envelope = EnvelopeVersion.Soap11;
// or
//binding.MessageVersion = MessageVersion.Soap11WSAddressingAugust2004;
return binding;
}
But I can't change binding.MessageVersion because it's a read-only property?

You would need a custom binding for that. One way would be to statically declare a custom binding equivalent to WS2007FederationHttpBinding - could take a while to fine tune it. Or you could create WS2007FederationHttpBinding in code (like you do), clone it into a custom binding:
CustomBinding outputBinding = new CustomBinding(federationBinding.CreateBindingElements());
and then find the text message encoding channel and change its soap version.

Related

Content issue in .NET Core app Consuming a WCF service

I am trying to call a WCF service method from an .NET Core Web API using the new Visual Studio WCF Connected service.
But when I am testing this, I get the following error:-
The content type multipart/related; type="application/xop+xml"; start="http://tempuri.org/0"; boundary="uuid:9e7f9b02-4d9c-4ec1-bad4-1007704a579a+id=1197"; start-info="text/xml" of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: '
--uuid:9e7f9b02-4d9c-4ec1-bad4-1007704a579a+id=1197
Content-ID: http://tempuri.org/0
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
The exposed WCF service uses MTOM MessageEncoding and in traditional .NET framework client application, we can set the client to use MTOM in the application's config file but in .NET core, we don't have the config file where we can set the MessageEncoding and all this configuration
is taken care of in the code present in Reference.cs(which is a generated file).
I thinking changing this generated file to set the MessageEncoding is not a good option.
Any idea on what is the best way to handle this issue?
I just came to know from WCF Core team that currently MTOM encoding is not supported in .NET Core based clients. This is a requested feature which will be available in future versions.
Here is github link which has more information: Adding MTOM support in WCF runtime
I was facing the same MTOM consumtion issue in my project, and had to find a way to be able to consume the service.
It ended up in some (ugly) code, but functional.
I just wanted to share the solution (as I wasn't able to find anything on the web) :
To start, generate the Client with Visual Studio (2017 in my case) by adding a connected service (as you would do for a regular SOAP client).
This will help you save lot a dummy code typing ;)
then, use RestSharp to call the endpoint, and serialize manually the response/request :
var client = new RestClient("http://myService/Service");
var request = new RestRequest(Method.POST);
request.AddHeader("accept", "text/plain");
request.AddHeader("content-type", "text/xml");
// create parameter
var serializer = new XmlSerializer(typeof(myParameter));
var requestParameter = new myParameter(1,2,3,4);
string requestParameterStr;
var namepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var settings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, NamespaceHandling = NamespaceHandling.OmitDuplicates }; // some parameters to make it clean, only OmitXmlDeclaration is mandatory
using (var stringWriter = new StringWriter())
{
using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
{
serializer.Serialize(xmlWriter, requestParameter, namepsaces);
requestParameterStr = stringWriter.ToString();
}
}
// patch parameter to add the namespace prefix required by consumer service
requestParameterStr = requestParameterStr.Replace("myParameter", "myNs:myParameter");
// wrap parameter in a soap envelop
requestParameterStr =
$"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:myNs=\"http://myService/Service/\"><soapenv:Header/><soapenv:Body>{requestParameterStr}</soapenv:Body></soapenv:Envelope>";
request.AddParameter(
"text/xml",
requestParameterStr,
ParameterType.RequestBody);
var response = client.Execute(request);
var mtomMsg = response.Content;
// remove MTOM elements from the received Content. here comes the ugly part ^^
var responseContentType = response.ContentType;
var contentTypeElements = responseContentType.Split(";");
var boundary = contentTypeElements.FirstOrDefault(x => x.TrimStart().StartsWith("boundary="))?.Trim().Substring("boundary=".Length);
var startElement = contentTypeElements.FirstOrDefault(x => x.TrimStart().StartsWith("start="))?.Trim().Substring("start=".Length);
boundary = boundary.Trim('"');
startElement = startElement.Trim('"');
var startIndex = mtomMsg.IndexOf(startElement) + startElement.Length;
var endIndex = mtomMsg.LastIndexOf("--" + boundary + "--", startIndex);
var cleanedMtomMsg = mtomMsg.Substring(startIndex, endIndex - startIndex);
// Get the result inside the Soap envelop
var soapDocument = XDocument.Parse(cleanedMtomMsg);
var envelopeElt = soapDocument.Root;
var bodyElt = (System.Xml.Linq.XElement)envelopeElt.FirstNode;
var responseStr = bodyElt.FirstNode.ToString();
// deserialize the result
var memstream = new MemoryStream(Encoding.UTF8.GetBytes(responseStr));
var reader = XmlDictionaryReader.CreateTextReader(memstream, XmlDictionaryReaderQuotas.Max);
var deserializer = new XmlSerializer(typeof(myResponse), "http://myService/Service/"); // don't forget the namespace
var result = deserializer.Deserialize(reader) as myResponse;
note : myParameter & myResponse are the classes generated at step 1
There could be easier ways, but at least, this works.
Hope some of you find this helpfull.
In my case, I solved this issue by using WcfCoreMtomEncoder package in my .NET Core 2.1 project. You can learn more about using it here
I fixed the problem by installing latest version of visual studio 2017. by installing latest version of visual studio it will automatically update your net core to the latest verion (1.1.2).
you can also use "binaryMessageEncodingBindingElement":
ChannelFactory<ITestService> factory = null;
ITestService serviceProxy = null;
BinaryMessageEncodingBindingElement binaryMessageEncodingBindingElement = new BinaryMessageEncodingBindingElement();
binaryMessageEncodingBindingElement.CompressionFormat = CompressionFormat.GZip;
HttpTransportBindingElement httpTransportBindingElement = new HttpTransportBindingElement();
httpTransportBindingElement.MaxReceivedMessageSize = int.MaxValue;
CustomBinding customBinding = new CustomBinding(new BindingElement[] { binaryMessageEncodingBindingElement, httpTransportBindingElement });
factory = new ChannelFactory<ITestService>(customBinding, new EndpointAddress("http://localhost/test.svc/mex"));
serviceProxy = factory.CreateChannel();
var result = serviceProxy.GetResultData(50);

Simple way to create a WCF Binding from an EndpointAddress

Is there a shortcut for creating the most basic WCF Binding based on the address of a given Endpoint?
Endpoint: net.tcp://localhost:7879/Service.svc
Instead of a big block of if statements...
Binding binding = null;
if (endpoint.StartsWith("net.tcp"))
{
binding = new NetTcpBinding();
}
else if (endpoint.StartWith("http"))
{
binding = new WsHttpBinding();
}
.
.
.
Is there a shortcut in the Framework library that will do this for me that I just can't find or can I not find it because it doesn't publicly exist?
WCF in .NET 4 does that automatically for you - the feature is called default endpoints.
Read about all of WCF 4's new features here: A Developer's Introduction to WCF 4
Default endpoints is about the second or so paragraph into the article.
While WCF 4 supports default service endpoints, it does not support default client endpoints. Unfortunately the methods used by the framework to create default bindings are internal, but the logic behind it is simple, so I have reimplemented it to use on the client side (skipping original caching and tracing logic):
private static Binding GetBinding(string scheme)
{
// replace with ConfigurationManager if not running in ASP.NET
var configuration = WebConfigurationManager.OpenWebConfiguration(null);
var sectionGroup = ServiceModelSectionGroup.GetSectionGroup(configuration);
Debug.Assert(sectionGroup != null, "system.serviceModel configuration section is missing.");
var mapping = sectionGroup.ProtocolMapping.ProtocolMappingCollection
.OfType<ProtocolMappingElement>()
.SingleOrDefault(e => e.Scheme == scheme);
if (mapping == null)
throw new NotSupportedException(string.Format("The URI scheme {0} is not supported.", scheme));
var bindingElement = sectionGroup.Bindings.BindingCollections.Single(e => e.BindingName == mapping.Binding);
var binding = (Binding) Activator.CreateInstance(bindingElement.BindingType);
var bindingConfiguration = bindingElement.ConfiguredBindings.SingleOrDefault(e => e.Name == mapping.BindingConfiguration);
if (bindingConfiguration != null)
bindingConfiguration.ApplyConfiguration(binding);
return binding;
}
Without any configuration this code is equivalent to the code in the question, but you can select and configure your bindings inside the system.serviceModel/protocolMapping section.
After looking at the issue deeper I don't really need to read the configuration in manually. Instead I need to send the binding information along with the address and contract.
http://www.codeproject.com/KB/WCF/WCFDiscovery.aspx?display=PrintAll
I have built a simple component that serializes the binding information.
http://nardax.codeplex.com/

Modify SOAP header Mustunderstand attribute in WCF client

I am writing a WCF client for a service (not WCF). Getting an error that Unprocessed 'mustUnderstand' header element: {http://www.w3.org/2005/08/addressing}Action, because request SOAP contains header with mustunderstand='true'. I have to either set it false or remove the whole header. can you show the way to do that?
Here is the binding code
var transportElement = new HttpsTransportBindingElement();
transportElement.AuthenticationScheme = AuthenticationSchemes.Basic;
var messegeElement = new TextMessageEncodingBindingElement();
messegeElement.MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap11);
var binding = new CustomBinding(messegeElement, transportElement);
return binding;
I resolved this one by setting AddressingVersion to None that did not put the SOAP headers.
here is the code
MessageVersion.CreateVersion(EnvelopeVersion.Soap11, AddressingVersion.None)
Specifying the messageVersion solved my problem.
<mtomMessageEncoding messageVersion="Soap12"/>

How can I set the maxItemsInObjectGraph property programmatically from a Silverlight Application?

I have a Silverlight 3.0 application that is using a WCF service to communicate with the database, and when I have large amounts of data being returned from the service methods I get Service Not Found errors. I am fairly confident that the solution to it is to simply update the maxItemsInObjectGraph property, but I am creating the service client progrogrammatically and cannot find where to set this property. Here is what I am doing right now:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
MaxReceivedMessageSize = int.MaxValue,
MaxBufferSize = int.MaxValue
};
MyService.MyServiceServiceClient client = new MyService.MyServiceProxyServiceClient(binding, new EndpointAddress(new Uri(Application.Current.Host.Source, "../MyService.svc")));
It's not defined in binding, but in Service Behavior.
In Silveright, maxItemsInObjectGraph defaults to int.MaxValue.
Here is an article on how to change it for .NET application, but not Silverlight: Programattically setting the MaxItemsInObjectGraph property in client
A snippet of the code:
protected ISecurityAdministrationService GetSecAdminClient()
{
ChannelFactory<ISecurityAdministrationService> factory = new ChannelFactory<ISecurityAdministrationService>(wsSecAdminBinding, SecAdminEndpointAddress);
foreach (OperationDescription op in factory.Endpoint.Contract.Operations)
{
DataContractSerializerOperationBehavior dataContractBehavior =op.Behaviors.Find<DataContractSerializerOperationBehavior>() as DataContractSerializerOperationBehavior;
if (dataContractBehavior != null)
{
dataContractBehavior.MaxItemsInObjectGraph = 2147483647;
}
}
ISecurityAdministrationService client = factory.CreateChannel();
return client;
}
The following is a function that I've used inside a client object that inherits from
System.ServiceModel.ClientBase(Of IServiceName)
The purpose of the method is to programatically set the MaxItemsInObjectGraph value for each operation. This allows me to have much more complex structures.
Private Sub IncreaseObjectCount()
For Each op As System.ServiceModel.Description.OperationDescription In Me.Endpoint.Contract.Operations
For Each dscob As System.ServiceModel.Description.DataContractSerializerOperationBehavior In op.Behaviors.FindAll(Of System.ServiceModel.Description.DataContractSerializerOperationBehavior)()
dcsob.MaxItemsInObjectGraph = Integer.MaxValue
Next dcsob
Next op
End Sub
I usually call it in the constructors of the object.
Change the maxItemsInObjectGraph in your WCF service for each endpoint, changing it in Silverlight means the client will be able to support the behavior, but the service must support it aswell.
After changing it in your service, regenerate the proxy/update web service, and you will get a new ServiceReference.config, that will include the new maxItemsInObjectGraph value

WCF: Why does passing in a remote endpoint fail?

The problem I am having connecting a wcf client application to a host running on a separate machine is documented in a question previously asked:
WCF: Why does passing in a remote endpoint fail?
However, the solution provided here says you need to use a SpnEndpointIdentity with an empty string. Since my code doesn't look anything like the case in the example I have referenced, I need to know what to do with the SpnEndpointIdentity object I have created.
I have a ChannelFactory upon which I call Create channel, passing in an EndpointAddress:
public override void InitialiseChannel()
{
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName);
Proxy = ChannelFactory.CreateChannel(address);
}
(NB: ChannelFactory is of type IChannelFactory, where T is the service contract interface)
So what do I do with spnEndpointIdentity? I can't pass it to CreateChannel.
Or perhaps I can use it somehow when I create the channel factory:
private ChannelFactory<T> CreateChannelFactory()
{
var binding = new NetTcpBinding
{
ReaderQuotas = { MaxArrayLength = 2147483647 },
MaxReceivedMessageSize = 2147483647
};
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var channelFactory = new ChannelFactory<T>(binding);
return channelFactory;
}
Again, I can't pass it into the constructor, so what do I do with it?
Thanks.
You almiost got it.
What you're missing is that you associate the EndpointIdentity with the EndpointAddress, and then provide that to CreateChannel():
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName, spnEndpointIdentity);