I have a problem with recieving any kind of array from remote WCF service. Even the most trival example with array of string is returning empty collection:
[ServiceContract]
interface IArrayService
{
[OperationContract]
string[] getArrayStr();
}
SOAP response captured by Wireshark:
<soap11env:Envelope xmlns:soap11env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://tempuri.org/">
<s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" />
<soap11env:Body>
<tns:getArrayStrResponse>
<tns:getArrayStrResult>
<tns:string>ASD</tns:string>
<tns:string>ASD</tns:string>
<tns:string>ASD</tns:string>
</tns:getArrayStrResult>
</tns:getArrayStrResponse>
</soap11env:Body>
</soap11env:Envelope>
Result is empty:
var result = client.getArrayStr();
What am i doing wrong?
Related
I'm having issues with strings containing ampersands in my client generated from dotnet-svcutil.
When I recieve a response through the client all ampersands are serialized as
<Users xsi:type=\"xsd:string\">John & Jane</Users>
When i extract the value from the request in my code it has been deserialized as an ampersand
UserResponse users = await GetUsersAsync()
users.Names = "John & Jane"
In a later stage i need to send the value of the users back like this
await SetUsersAsync(users.Names);
When i intercept the generated soap request I can see that the ampersand has not been escaped
public class MyMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var xml = reply.ToString(); // This shows <Users xsi:type=\"xsd:string\">John & Jane</Users>
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var xml = request.ToString(); // This shows <Users xsi:type=\"xsd:string\">John & Jane</Users>
return null;
}
}
This causes the whole request to fail in the connected service. From the information I've found online the ampersand should be converted to & by the serializer. I could convert the value myself, but since I'm only returning a value I got from the soap client, I would have to check and convert every single string that is returned back.
Do you have any suggestions on how I could configure the generated client to automatically escape ampersands?
For .net core 2.1+:
Use the HtmlEncode method of HttpUtility available in System.Web to encode the strings you send to the client.
using System.Web;
await SetUsersAsync(HttpUtility.HtmlEncode(users.Names));
For .net core 2.0 (or .net 1.* with a package install):
use HtmlEncoder.Default.Encode(users.Names)
which is available in System.Text.Encodings.Web
I have a WCF RIA Service with methods that return IQueryable<>. I want to access this methods using ClannelFactory from a
console application. I have an interface on the client that matches the the methods in my RIA Service. When I run the server and
the client applications I can see that the server method is called and an IQuery<> object is returned. The problem is that on the
client I can't get the data sent by the server. I can see that data actually comes by using Fiddler, but I think that the data can't be
deserialized to IQueryable.
For me the type of the data received doesn't matter. I'll be happy with just an array. But because the method in the
service returns IQueryable my OperationContract method on the client has the same type.
So the question is how to get the data from the server, without changing the return type (IQueryable) on the server side?
Server side:
public IQueryable<Customers> GetCustomers()
{
List<Customers> customersList = new List<Customers>();
customersList.Add(new Customer())
...
return customersList.AsQueryable();
}
Client side:
[ServiceContract]
public interface CustoemrsService
{
[OperationContract]
IQueryable<Customers> GetCustomers();
}
And the ChannelFactory code:
var endpointAddress = new EndpointAddress(_endpointAddress);
var channelFactory = new ChannelFactory<VfxSystemDomainServiceSoap>(new BasicHttpBinding());
var channel = channelFactory.CreateChannel(endpointAddress);
IQueryable<Customers> customersCollection = channel.GetVfxfopenQuery();
I have been trying to understand WCF internals. I tried to create custom formatters and message inspectors (both at the client and the server by following some online links) but have been facing issues.
My operationcontract method at the server simply reads a file (which has "testing" text in it) and returns a filestream. A custom dispatch formatter then tries to serialize the the stream received as a message. Following is the definition of custom serializer method at server -
public System.ServiceModel.Channels.Message SerializeReply(System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters, object result)
{
string content;
using(var reader = new StreamReader(((FileStream)result), Encoding.ASCII))
{ content = reader.ReadToEnd(); }
Message message = Message.CreateMessage(messageVersion, "http://tempuri.org/IService1/GetStreamVideoResponse", new CustomBodyWriter(content));
return message;
}
CustomBodyWriter is implemented as -
class CustomBodyWriter : BodyWriter
{
string content;
public RawBodyWriter(string content)
: base(true)
{
this.content = content;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("GetStreamVideoResponse");
writer.WriteAttributeString("xmlns", "http://tempuri.org/");
writer.WriteStartElement("GetStreamVideoResult");
writer.WriteString(content);
writer.WriteEndElement();
}
}
I think the xmldictionarywriter overrides the envelop when i write xml in OnWriteBodyContents method in CustomBodyWriter. But then how can i insert the response elements properly..
When I hook in to custom Inspector just to print the reply sent in BeforeSendReply method, I see following output at the console (which has an ending envelop element missing)-
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w
3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IService1/GetStreamVideoRe
sponse</a:Action>
<ActivityId CorrelationId="0e0a6aae-91fa-4f9d-94b2-8c390cb5493d" xmlns="http
://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">e452a645-e8c5-44bf-81
06-d821522b7883</ActivityId>
<a:RelatesTo>urn:uuid:72b789cb-cc0b-48ac-b73a-434040434ce1</a:RelatesTo>
</s:Header>
<s:Body>
<GetStreamVideoResponse xmlns="http://tempuri.org/">
<GetStreamVideoResult>**testing**</GetStreamVideoResult>
</GetStreamVideoResponse>
</s:Body>
At the client, i have created custom inspector which tries to deserialize message by simply reading the body part of message in a byte[] and creating a filestream over it which is then returned -
public object DeserializeReply(System.ServiceModel.Channels.Message message, object[] parameters)
{
var getBody = message.GetReaderAtBodyContents();
byte[] buffer = getBody.ReadContentAsBase64();
FileStream fs = new FileStream("c:\\test.txt", FileMode.OpenOrCreate);//not ideal but need for testing at present
fs.Write(buffer, 0, buffer.Length);
return fs;
}
how can i get the complete SOAP message that culminates with Envelop element.
Even the byte[] created at client has no value in it. What is wrong that I have been doing?
Thanks in anticipation..
I am extending WebHttpBehavior to expose a WCF REST service with customized serialization and deserialization (plus a certain number of other features that are not relevant to the problem).
The new behavior uses an implementation of IDispatchMessageFormatter to perform custom serialization and deserialization of POCOs served by the service and sent to it thanks to the SerializeReply and DeserializeRequest methods.
I can serve XML and JSON exactly how I need them in SerializeReply.
I can deserialize XML without a problem, however I can't seem to find the way to deserialize a JSON message because I can't obtain the plain text contained in the Message parameter of DeserializeRequest.
This is what the code in DeserializeRequest looks like right now:
if (format == System.ServiceModel.Web.WebMessageFormat.Json)
{
var data = ""; // TODO obtain plain text from Message object
var json = JsonConvert.DeserializeObject(data, paramType, new IsoDateTimeConverter(), new StringEnumConverter());
parameters[paramIndex] = json;
}
else
{
var serializer = new System.Xml.Serialization.XmlSerializer(paramType, string.Empty);
var reader = message.GetReaderAtBodyContents();
parameters[paramIndex] = serializer.Deserialize(reader);
}
I'm using Json.NET for JSON (de)serialization.
Any suggestions on how to obtain plain text from the Message object in order to deserialize it would be greatly appreciated.
If you think there's something wrong in my approach I'd also like to hear of it in the comments.
You need to make sure that the WebMessageFormat of the Message is set to Raw, otherwise WCF will use a the JsonMessageEncoder in order to create the Message which in turn won't allow you to get to the raw message content.
You do that by implementing a custom WebContentTypeMapper:
public class RawMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Raw;
}
}
...which needs to be applied to the WebHttpBinding:
webHttpBinding.ContentTypeMapper = new RawMapper();
..or via configuration:
<bindings>
<webHttpBinding>
<binding contentTypeMapper="Samples.RawMapper, MyContentTypeMapperAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</webHttpBinding>
</bindings>
With that in place, you can then retrieve the request body as a String like that:
public void DeserializeRequest(Message message, object[] parameters)
{
var bodyReader = message.GetReaderAtBodyContents();
bodyReader.ReadStartElement("Binary");
byte[] rawBody = bodyReader.ReadContentAsBase64();
string messageAsString;
using (var reader = new StreamReader(new MemoryStream(rawBody)))
messageAsString = reader.ReadToEnd();
object jsonObj = JsonConvert.DeserializeObject(messageAsString);
parameters[0] = jsonObj;
}
Even if you are using WebMessageFormat.Json you can convert your message to string and then deserialize it to object inside DeserializeRequest method.
MemoryStream mss = new MemoryStream();
XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(mss);
message.WriteMessage(writer);
writer.Flush();
string messageBody = Encoding.UTF8.GetString(mss.ToArray());
var obj = JsonConvert.DeserializeObject(messageBody, operation.Messages[0].Body.Parts[0].Type);
parameters[0] = obj;
I have an ASP application which is client of WCF SERVICE1 , which is client of WCF SERVICE2.
I have added IDispatchMessageInspector and IClientMessageInspector to WCF SERVICE1.
Now I need to pass a custom value from ASP to WCF1 , then to WCF2.
from ASP to WCF1 it is trivial , via Message Headers.
The question is , how to pass a custom value from IDispatchMessageInspector.AfterReceiveRequest(request from ASP received by WCF1) to IClientMessageInspector.BeforeSendRequest(prepare to send request to WCF2) operation of WCF SERVICE 1 ?
Is there is some context which could be used ?
What does your code look like? Assuming that first Dispatch Message Inspector is the one making the request to WCF2, then simply using message properties would suffice.
However, if your dispatch message inspector does something; then the request continues processing and it is the service implementation that actually calls WCF2, then you'll need to jump through a few more hoops. In general, I'd say you'd need the inspector to put some data in the service request message properties that the service implementation would need to pick up and copy to the message to send to WCF2 so that the client inspector can pick them up.
That's ugly, and would kinda make the whole process more brittle.
Can you elaborate a bit more what you're trying to do? What kind of data are you hoping to pass around this way?
In my case, I had to identify and log nested service calls requested by client.
To do that, I stamp each service call by ThreadStatic property and add this property to the header of client call(service1 count as client for service2) than in AfterReceiveRequest method I have checked its existance. If exists,current method was requested by parent service.
public class GenericMessageInspector : IDispatchMessageInspector, IClientMessageInspector
{
[ThreadStatic]
private static string _masterServiceGUID;
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
if (request.Headers.Action == null)
return null;
//Control request header for nested call
string masterRequestId = string.Empty;
var IsMasterExist = request.Headers.FindHeader("MasterServiceGUID", "namespace");
if (IsMasterExist > -1)
{
//requested by internal service
masterRequestId = request.Headers.GetReaderAtHeader(IsMasterExist).ReadInnerXml();
}
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if (!String.IsNullOrEmpty(_masterServiceGUID))
{
request.Headers.Add(MessageHeader.CreateHeader("MasterServiceGUID", "namespace", _masterServiceGUID));
}
return null;
}
}
}