WCF own EndpointBehavior read client Session - c#

I have an IClientMessageInspector interface with BeforeSendRequest() method implemented.
In this method I want to retrieve the Session object set in the client. Something like.
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
//Instantiate new HeaderObject with values from ClientContext;
var dataToSend = new MyCustomHeader
{
MyValue = HowDoIGetClientSession["abcValue"];
};
var typedHeader = new MessageHeader<CustomHeader>(dataToSend);
var untypedHeader = typedHeader.GetUntypedHeader("custom-header", "s");
request.Headers.Add(untypedHeader);
return null;
}
I think I need something very similar as in this question.

If both your client application and WCF service are hosted in same application (actually virtual application in IIS), you can share your session state.
But you need to enable ASP.Net compability mode within your WCF (see this: https://msdn.microsoft.com/en-us/library/ms752234.aspx)
If your applications are hosted in different applications, you can still do it, but you must use an external storage for you session, like a Session State server (see more here: https://msdn.microsoft.com/en-us/library/ms178586%28v=vs.140%29.aspx).
You proceed the same way, but you must set in both web.config files the same session storage.
Hope it helps.

I ended up using an approach like in this tutorial.
I add the relevant information in a cookie as key-value pairs and read it in the service implementation. Instead of service reference I am using ChannelFactory, but basically the main idea is the same as in the tutorial.
My BeforeSendRequest method is:
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel)
{
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name
, out httpRequestMessageObject))
{
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (string.IsNullOrEmpty(httpRequestMessage.Headers["Cookie"]))
{
httpRequestMessage.Headers["Cookie"] = cookie;
}
}
else
{
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add("Cookie", cookie);
request.Properties.Add(HttpRequestMessageProperty.Name
, httpRequestMessage);
}
return null;
}
The binding settings is:
<basicHttpBinding>
<binding name="basicHttp" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="20000000" maxBufferSize="20000000" maxReceivedMessageSize="20000000"
textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true"
messageEncoding="Text">
<readerQuotas maxDepth="32" maxStringContentLength="200000000" maxArrayLength="200000000"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
In the client app I needed (in web.config system.serviceModel):
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
And in the service implementation class annotation:
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Required)]
public class MyService : IMyService {
// ...
}
For service calls I am using a wrapper object which implements IDisposable, takes case of proper disposal and adding to EndpointBehaior to the ChannelFactory before creating the channel.
_factory = new ChannelFactory<T>( );
_factory.Endpoint.Behaviors.Add(new CookieEndpointBehavior(cookieStr));
_channel = _factory.CreateChannel();
I am using the service wrapper class in using block, which will call Dispose, once it reaches out of scope.

Related

WCF The request channel timed out attempting to send after 00:30:00

this is my config
<bindings>
<netTcpBinding>
<binding name="netTcpBindConfig" closeTimeout="00:30:00" openTimeout="00:30:00" receiveTimeout="00:30:00" sendTimeout="00:30:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" portSharingEnabled="true" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
This issue occurs in production environment,I cannot reproduce this error.
If I set a sleep time such as 40 * 60 * 1000 (40min) in wcf server method.
public List<CompositeType> GetData(List<CompositeType> value)
{
List<CompositeType> list = new List<CompositeType>();
try
{
Thread.Sleep(40 * 60 * 1000);
}
catch (Exception ex)
{
}
return list;
}
then i can get this error:The request channel timed out while waiting for a reply after 00:30:00.
they are different.
my error likes a particular call is not finished in a given time interval.
how can i reproduce this error: The request channel timed out attempting to send after xx:xx:xx
thanks a lot!

Error while sending file to WCF: The request failed with HTTP status 400: Bad Request

I am trying to upload file using wcf service. I am getting The request failed with HTTP status 400: Bad Request at:
ds = WcfSpend.RegisterSupplier("blah", new SpendWcfRef.FileData
{
FileName = myFile.FileName.ToString(),
BufferData = file,
FilePosition = 1
});
WCF:
public DataSet RegisterSupplier(string SupplierId, FileData file)
{
DataSet ds = new DataSet();
return ds;
}
[ServiceContract]
public interface ISPEND
{
[OperationContract]
DataSet executeProcedure(string procedurename, string[] paramsName, string[] paramsValue, int num);
[OperationContract]
DataSet RegisterSupplier(string SupplierId, FileData file);
//[OperationContract]
//bool UploadFileData(FileData fileData);
}
[DataContract]
public class FileData
{
[DataMember]
public string FileName { get; set; }
[DataMember]
public byte[] BufferData { get; set; }
[DataMember]
public int FilePosition { get; set; }
}
APPLICATION:
ds = WcfSpend.RegisterSupplier("blah", new SpendWcfRef.FileData
{
FileName = myFile.FileName.ToString(),
BufferData = file, FilePosition = 1
});
Apllication config file:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ISPEND"
closeTimeout="00:01:00"
openTimeout="00:01:00"
receiveTimeout="00:10:00"
sendTimeout="00:01:00"
allowCookies="false"
bypassProxyOnLocal="false"
hostNameComparisonMode="StrongWildcard"
maxBufferSize="2147483647"
maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647"
messageEncoding="Text"
textEncoding="utf-8"
transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="2147483647"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647" />
<security mode="None">
<transport clientCredentialType="None"
proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName"
algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client/>
</system.serviceModel>
WCF WEB CONFIG:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="myBindingForBigArrays"
openTimeout="00:10:00"
closeTimeout="00:10:00"
receiveTimeout="00:10:00"
sendTimeout="00:10:00"
maxReceivedMessageSize="2147483647"
maxBufferPoolSize="2147483647"
maxBufferSize="2147483647">
<readerQuotas maxDepth="64"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="4096"
maxNameTableCharCount="16384"/>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
There could be a few things that are resulting in the error you are seeing. The first thing I would recommend is that you assign the binding configurations you created to an explicitly defined endpoint. Something like this:
<!-- This goes in the <system.serviceModel> section -->
<services>
<service name="MyService">
<endpoint address=""
binding="wsHttpBinding"
bindingConfiguration="myBindingForBigArrays"
contract="<namespace>.ISpend" />
</service>
</services>
The above is the configuration for the service. Make sure you fully qualify the contract name with the namespace, and the service name needs to be the same as the name in the markup of the .svc file.
You would do something similar for the client, except it would be the <client> tag, rather than <service>.
If you don't specify the binding configuration to use in the endpoint, then WCF will use the default (lower) values for the binding type you selected. An alternative is to make the binding configuration the default for that kind of binding, by omitting the name attribute.
If this doesn't solve the issue, you can also try adjusting the maxRequestLength value for the <httpRuntime> element in <system.web>. <system.web> is a child of <configuration>:
<system.web>
<httpRuntime maxRequestLength="2147483647" />
</system.web>

How to consume a WCF with changing endpoint or server address [duplicate]

I have my first WCF example working. I have the host on a website which have many bindings. Because of this, I have added this to my web.config.
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
This is my default binding http://id.web, which works with the following code.
EchoServiceClient client = new EchoServiceClient();
litResponse.Text = client.SendEcho("Hello World");
client.Close();
I am now trying to set the endpoint address at runtime. Even though it is the same address of the above code.
EchoServiceClient client = new EchoServiceClient();
client.Endpoint.Address = new EndpointAddress("http://id.web/Services/EchoService.svc");
litResponse.Text = client.SendEcho("Hello World");
client.Close();
The error I get is:
The request for security token could not be satisfied because authentication failed.
Please suggest how I may change the endpoint address at runtime?
Additional here is my client config, requested by Ladislav Mrnka
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IEchoService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://id.web/Services/EchoService.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IEchoService" contract="IEchoService"
name="WSHttpBinding_IEchoService">
<identity>
<servicePrincipalName value="host/mikev-ws" />
</identity>
</endpoint>
</client>
</system.serviceModel>
So your endpoint address defined in your first example is incomplete. You must also define endpoint identity as shown in client configuration. In code you can try this:
EndpointIdentity spn = EndpointIdentity.CreateSpnIdentity("host/mikev-ws");
var address = new EndpointAddress("http://id.web/Services/EchoService.svc", spn);
var client = new EchoServiceClient(address);
litResponse.Text = client.SendEcho("Hello World");
client.Close();
Actual working final version by valamas
EndpointIdentity spn = EndpointIdentity.CreateSpnIdentity("host/mikev-ws");
Uri uri = new Uri("http://id.web/Services/EchoService.svc");
var address = new EndpointAddress(uri, spn);
var client = new EchoServiceClient("WSHttpBinding_IEchoService", address);
client.SendEcho("Hello World");
client.Close();
This is a simple example of what I used for a recent test.
You need to make sure that your security settings are the same on the server and client.
var myBinding = new BasicHttpBinding();
myBinding.Security.Mode = BasicHttpSecurityMode.None;
var myEndpointAddress = new EndpointAddress("http://servername:8732/TestService/");
client = new ClientTest(myBinding, myEndpointAddress);
client.someCall();
app.config
<client>
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="LisansSoap"
contract="Lisans.LisansSoap"
name="LisansSoap" />
</client>
program
Lisans.LisansSoapClient test = new LisansSoapClient("LisansSoap",
"http://webservis.uzmanevi.com/Lisans/Lisans.asmx");
MessageBox.Show(test.LisansKontrol("","",""));
We store our URLs in a database and load them at runtime.
public class ServiceClientFactory<TChannel> : ClientBase<TChannel> where TChannel : class
{
public TChannel Create(string url)
{
this.Endpoint.Address = new EndpointAddress(new Uri(url));
return this.Channel;
}
}
Implementation
var client = new ServiceClientFactory<yourServiceChannelInterface>().Create(newUrl);

Migrating WSE Client to WCF - how to replace SecurityPolicyAssertion and UsernameToken?

I have a web service implemented in Java that is currently being invoked by a WSE 3.0 client, and I would like to migrate from WSE to WCF. Using the standard tools, I have created a client that can invoke the web service, but it returns a SoapException with a message of "Required parameter value is missing". The web service uses HTTPS and requires a username & password to be provided. In the existing WSE client code, the credentials area supported by subclassing SecurityPolicyAssertion and SendSecurityFilter, as follows:
public class UTClientAssertion : SecurityPolicyAssertion
{
public UTClientAssertion()
{
}
public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
{
return new ClientOutputFilter(this, context);
}
public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
{
// we don't provide ClientInputFilter
return null;
}
public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
{
// we don't provide any processing for web service side
return null;
}
public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
{
// we don't provide any processing for web service side
return null;
}
#region ClientOutputFilter
class ClientOutputFilter : SendSecurityFilter
{
public ClientOutputFilter(UTClientAssertion parentAssertion, FilterCreationContext context)
: base(parentAssertion.ServiceActor, false, parentAssertion.ClientActor)
{
}
public override void SecureMessage(SoapEnvelope envelope, Security security)
{
UsernameToken token = new UsernameToken("UserName", "Password", PasswordOption.SendPlainText);
security.Tokens.Add(token);
security.MustUnderstand = false;
}
}
#endregion
These classes are applied to the generated proxy class in the client as follows:
// Create the web service client
ListService objListSvc = new ListService();
//code to set up the security policy and user assertion
UTClientAssertion objAssertion = new UTClientAssertion();
// create policy, add the assertion, and set it on the web service
Policy objPolicy = new Policy();
objPolicy.Assertions.Add(objAssertion);
objListSvc.SetPolicy(objPolicy);
What I've found is that if I edit the WSE client code to remove the line objListSvc.SetPolicy(objPolicy), I get the same error message of "Required parameter value is missing".
What is the equivalent WCF configuration/code to match the WSE code above that configures the Username and Password for this web service? The WCF configuration being used is the default that was generated:
<basicHttpBinding>
<binding name="ListBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
Thanks in advance
It is hard to tell without a sample of how your SOAP message from WSE3 looks on the wire. Try to get one via Fiddler and publish it here.
Generally if you use SSL try something like this:
<bindings>
<basicHttpBinding>
<binding name="NewBinding0">
<security mode="TransportWithMessageCredential " />
</binding>
</basicHttpBinding>
</bindings>
If you do not use ssl try CUB.

How to set proxy credentials to specific wcf client?

I need to connect to some public wcf service, but there is some proxy between me and service.
If i use default proxy settings such as
<system.net>
<defaultProxy useDefaultCredentials="true" />
</system.net>
or
HttpWebRequest.DefaultWebProxy
it works perfectly fine
but i don't need to set proxy settings for entire application, i need to set it for specific connection. So how I can do that?
I saw ProxyAddress property
(client.Endpoint.Binding as BasicHttpBinding).ProxyAddress
but there is no any properties for credentials...
I was thinking to somehow modify HttpWebRequest, but I do not know how to get it...
Solved
Thank you all for your answers.
Answer of AntonK suitable for solving my problem.
At the time when this question was actual, I solved it in the same way, but without the use of web.config and wrote this method
void SetProxySettings<TChannel>(ClientBase<TChannel> client,
bool useProxy, string address, int port, string login, string password)
where TChannel : class
{
if (!useProxy) return;
var b = client.Endpoint.Binding as BasicHttpBinding;
if (b == null)
{
System.Diagnostics.Debug.WriteLine("Binding of this endpoint is not BasicHttpBinding");
return;
}
b.ProxyAddress = new Uri(string.Format("http://{0}:{1}", address, port));
b.UseDefaultWebProxy = false; // !!!
b.Security.Mode = BasicHttpSecurityMode.Transport;
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; // !!!
b.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic; // !!!
if (client.ClientCredentials == null) return;
client.ClientCredentials.UserName.UserName = login;
client.ClientCredentials.UserName.Password = password;
}
Here's an article dealing with this issue.
http://blogs.msdn.com/b/stcheng/archive/2008/12/03/wcf-how-to-supply-dedicated-credentials-for-webproxy-authentication.aspx
In summary, this is how to set a proxy for a specific service in the web.config. In the binding config, set proxyAddress="http://myproxy:8080" and set useDefaultWebProxy="false"
<bindings>
<basicHttpBinding>
<binding name="SubscriberFulfilmentServiceSOAP12Binding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:01:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"
textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="false"
proxyAddress="http://myproxy:8080"
messageEncoding="Text">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647"
maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
I found a solution. You have to update WCF to the latest.
Go to NuGet Package Manager -> Update all related Project URL of WCF There must be:
System.ServiceModel.Security
System.ServiceModel.NetTcp
System.ServiceModel.Http
This apply to .net core 2.1 version.
You can try this
HttpWebRequest request = HttpWebRequest.Create("URI") as HttpWebRequest;
var proxy = new WebProxy(HttpWebRequest.GetSystemWebProxy().GetProxy(request.RequestUri), true);
proxy.Credentials = new NetworkCredential(proxyUserName, proxyPassword, DomainName);
request.Proxy = proxy;
hope it helps

Categories

Resources