I have a series of self-hosted WCF services (using netTcpBinding) that I want to enhance by using the fantastic protobuf-net serializer.
I configured both the client and the service endpoints with the custom behavior extensions as instructed in the documentation of the ProtoEndpointBehavior class (see here). My tests ran fine from the very first try, however, I'm very skeptical of WCF-stuff running fine at the very first try ;)
Is there a simple way in which I can assert that the default DataContractSerializer was replaced by the XmlProtoSerializer?
I would really favor a test that can be also coded as part of a unit test. I wouldn't like the protobuf-net library to be inadvertently disabled by careless tampering of the .config file.
If you call your service in wcf test client, you will see < proto/> tag in the body of response
If you configure your client to use proto behaviour and call service with "inadvertently disabled protobuf", your answer will be null because of different serializers. You can check it in your test
var address = new EndpointAddress( url );
var binding = GetBinding( address.Uri );
var factory = new ChannelFactory<TService>( binding, address );
factory.Endpoint.EndpointBehaviors.Add( CreateProtobufEndpointBehavior() );
var client = factory.CreateChannel();
var answer = client.GetSomeInt();
AssertAnswerIsNotNull(answer);
Related
I have been contemplating on a dilemma for hours. I have a Visual Studio Solution that contains a WCF, WebForms, UWP, Xamarin and a SharedLibrary Projects.
I intend to use the WCF project as the backend which talks to the database and process Email and SMS integration and feed the other apps.
OPTION A
Currently, The WCF is hosted on an Azure App Service which makes it accessible via POST, GET, etc from the url which is: https://mywcfprojectlink.azurewebsites.net/service1.svc/GetUsers
With such arrangements, I can perform a POST request to get data from the apps:
string response = string.Empty;
string url = "https://mywcfprojectlink.azurewebsites.net/service1.svc/GetUsers";
try
{
var values = new Dictionary<string, string>
{
{ "data", Encryption.EncryptString(dat.ToString()) } //dat is incoming method param
};
string jsonString = JsonConvert.SerializeObject(values);
var cli = new WebClient();
cli.Headers[HttpRequestHeader.ContentType] = "application/json";
response = cli.UploadString($"{url}", jsonString);
var result = JsonConvert.DeserializeObject<string>(response);
topic.InnerText = Encryption.DecryptString(result.ToString());
}
catch (Exception)
{
return string.Empty;
}
The method above is a simple one as I have other ones where I Deserialize with Models/Classes.
OPTION B
I equally have access to the methods defined in service1 by adding the project reference to my WebForms which surprisingly is also compatible with xamarin but not with UWP. Nevertheless, I am interested in the WebForms scenario. Below is an example method:
using BackEnd;
//Service1 service1 = new Service1();
//var send = service1.GetUsers(dat.ToString()); //dat is incoming method param
//topic.InnerText = send;
Obviously, using the Option B would eliminate the need to encrypt, decrypt, serialize or deserialize the data being sent. However, I have serious performance concerns.
I need to know the better option and if there is yet another alternative (probably an Azure Resource), you can share with me.
If you decide to use https endpoint of the Azure website, option A is secure because of SSL encryption. So you don't have to encrypt/decrypt it by yourself. The only tip is to create a proper authorization mechanism. For example use TransportWithMessageCredential. An example is provided in below article https://www.codeproject.com/Articles/1092557/WCF-Security-and-Authentication-in-Azure-WsHttpBin
We recently had to upgrade our Win 8.1 store app to Win 10. Part of that change was modifying our NetTcpBindings to instead be BasicHttpBindings for file uploads since UWP does not currently support NetTcpBindings. Our issue is that when the client calls the UploadFileMethod on the proxy class, we intercept the message before it is sent to the server so we can apply headers that are used later as follows:
public async Task UploadFileAsync(RemoteFileInfo request)
{
using (new OperationContextScope(this.InnerChannel))
{
string nameSpace = #"http://tempuri.org";
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("FileName", nameSpace, request.FileName));
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Length", nameSpace,
request.Length));
await Channel.UploadFileAsync(request);
}
}
This used to work fine when we were using NetTcpBinding but since we switched to BasicHttpBinding that code is now throwing an exception on the line:
await Channel.UploadFileAsync(request);
With the exception reading:
This message cannot support the operation because it has been written.
After reading up on this exception it appears that we cannot mess with the request object at all before it is sent to the server when using BasicHttpBinding. If that is the case, how can we add OutgoingMessageHeaders to the message using properties of the request itself?
Edit: The proxy class is created as follows:
var imageProxy = new RTMImageServiceProxy(globalContext.Win10UploadBinding,
globalContext.ImageEndpointAddress);
Where Win10UploadBinding is configured as so:
BasicHttpBinding win10BasicBinding = new BasicHttpBinding();
win10BasicBinding.Security.Mode = BasicHttpSecurityMode.None;
win10BasicBinding.TransferMode = TransferMode.Streamed;
win10BasicBinding.SendTimeout = new TimeSpan(0, 0, 2, 0);
win10BasicBinding.MaxReceivedMessageSize = 2147483647;
this.win10UploadBinding = win10BasicBinding;
and globalContext is just a static class I used to store commonly-used variables.
Apparently it turns out that once written cannot be altered so create a copy with adjusted headers. Equivalent issue was brought up here.
Anyway, I encourage you to create custom message inspector: class deriving IClientMessageInspector, as far as client is concerned. It provides separation between method being invoced and headers being adjusted.
I found it is quite easy to create a .Net client to invoke a service with soap.tcp protocol.
Uri destinationUri = new Uri("soap.tcp://SomeHostName/SomePath");
Uri destinationUri = new Uri("http://SomeHostName/SomePath");
EndpointReference destination = new EndpointReference(destinationUri);
SoapSender sender = new SoapSender(destination);
But I need to create a Java client instead of a .Net client to the same URI (soap.tcp://SomeHostName/SomePath). Is it possible with Java with this protocol (soap.tcp) to invoke a web service.
Also I found the same question is asked
http://bytes.com/topic/java/answers/879818-calling-c-web-service-soap-tcp-java
Basically you can do that by implementing the "soap.tcp"-protocol as described by MS:
http://msdn.microsoft.com/en-us/library/cc219293.aspx
http://msdn.microsoft.com/en-us/library/cc219210.aspx
http://msdn.microsoft.com/en-us/library/cc219175.aspx
http://msdn.microsoft.com/en-us/library/cc219190.aspx
I don't know of anyone having done that... so it will be a major undertaking... "soap.tcp" is NOT made for interoperability... SOAP over HTTP is interoperable and should be used in cases like yours...
I am writing a "universal" client for Web services, and have hit an unexpected problem. I generate the code for the client dynamically by retrieving the Web services WSDL, and using the following (simplified a little) code to generate the client code:
ServiceDescription serviceDescription = ServiceDescription.Read(xmlTextReader(WSDL));
ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
descriptionImporter.ProtocolName = "Soap";
descriptionImporter.Style = ServiceDescriptionImportStyle.Client;
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("CSharp");
codeDomProvider.GenerateCodeFromCompileUnit(codeCompileUnit, Console.Out, new CodeGeneratorOptions());
I am testing this using a simple WCF Web service that exposes two methods:
[OperationContract]
int GetInteger();
[OperationContract]
string GetString();
If I examine the client-side generated code, then I can see that the GetString() returns a string, but the GetInteger() method returns void! I assume that this is something to do with value and reference types. Is there some way to force the code generator to make the GetInteger() method return an int?
Returning an int over WCF should not be a problem.
Check the code that produces the client side generated code.
The hack to get around the problem would be to return an object that had a single property that was an int.
From various experiments, I believe that my problem is an inherent limitation of using the ServiceDescriptionImporter to generate the client code. I am going to try to "upgrade" to using the System.ServiceModel.Description.WsdlImporter and System.ServiceModel.Description.ServiceContractGenerator framework, in the hopes that improves things.
I've having a very simple WCF service (a console application for file upload). I keep getting error (400) bad request. It works when I upload small files (4kb) but failing for 700kb.
From the readings I've done from stack overflow and other, I'll have to increase the MaxReceivedMessageSize. This was implemented using a custom class and overriding the OnOpening method but it still didn't work.
I'm testing with a console application using the webclient
outputBytes = webClient.UploadData(baseUrl + "/uploads/2." + filenameOnly, File.ReadAllBytes(filename));
Also, I'm using the WebServiceHost as in
var uri = new Uri("http://localhost:8000");
var svc = new WebServiceHost(typeof (UploadService), uri);
How do I solve this issue?
PS: Application does not have a config file so I'll looking at how to set this in code. If not and a config file is needed, then what should be the content.
Notes:
I found this link Bad Request Error 400 - WCF Client where it was explained that those properties are only valid for soap based services. He suggested updating the web.config. Since this is a console application, I'm wondering how this can be done'
regards.
You can set the maximumreceivedmessage programatically like this:
var binding = new wsHttpBinding(); // or whatever binding you are using
binding.MaxReceivedMessageSize = Int32.MaxValue;
var wcfClient = new WCFServiceTestClient(binding, strServiceURL);
Hopefully this will be enough to solve your problem, but you might also want to consider chopping up the file into bits (batching) before sending it to the server. Sending very large chunks of data can incurr a huge memory penalty as the client has to serialize the entire thing up front before sending it to the server (and then the same to deserialize it on the server side).
Edit: Based on your additional comment (below), the code on the application hosting the service might look something like this:
WebServiceHost webServiceHost = new WebServiceHost(typeof(UploadService), uri);
WebHttpBinding binding = new WebHttpBinding();
binding.MaxReceivedMessageSize = Int32.MaxValue;
webServiceHost.AddServiceEndpoint(typeof(IUploadService), binding, "WebServiceHost");
webServiceHost.Open();
Did you have a look at the documentation?
It covers both code and configuration.