Designing the XML payload schema for a RESTful POST WCF service.....and arrays - wcf-rest

I wrote a simple test client a while back to test out my XML payload, but my service keeps giving me back 400 Bad Requests. The following is my DataContract, followed by an example payload:
[DataContract(Name = "TMGTLabel", Namespace = "http://Novartis.TMGT.TMGTLabelPrint")]
public class TMGTLabel
{
[DataMember(Name = "ProposedfileName")]
public string proposedfilename;
[DataMember(Name = "LabelDataGroup")]
public LabelData ldatagrp;
}
[DataContract(Namespace = "http://Novartis.TMGT.TMGTLabelPrint")]
public class LabelData
{
[DataMember(Name = "ldata")]
public string[] ldata;
}
<TMGTLabel>
<ProposedfileName>TestLabelFileName</ProposedfileName>
<LabelDataGroup>
<LabelData><ldata>sample 1</ldata></LabelData>
<LabelData><ldata>sample 2</ldata></LabelData>
<LabelData><ldata>sample 3</ldata></LabelData>
<LabelData><ldata>sample 4</ldata></LabelData>
<LabelData><ldata>sample 5</ldata></LabelData>
<LabelData><ldata>sample 6</ldata></LabelData>
<LabelData><ldata>sample 7</ldata></LabelData>
</LabelDataGroup>
</TMGTLabel>
Is there anything wrong with my payload according to how I have my DataContract designed?

Related

How do I overload method parameter using NET5 SoapCore

I'm trying to port an existing Soap WebService to .NET5 but am having issue with overloading a Soap method parameter.
In NET4 the code looks like this
namespace SoapWebServiceeTest.Soap
{
/// <summary>
/// Summary description for WsgSPServiceOrderService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class TextWebService : System.Web.Services.WebService
{
[WebMethod]
public string Test(
[XmlElement("object1", typeof(Object1))]
[XmlElement("object2", typeof(Object2))]
[XmlElement("object3", typeof(Object3))]
object request)
{
return $"{request.GetType().Name}";
}
}
public class Object1 { public string Param1 { get; set; } }
public class Object2 { public string Param2 { get; set; } }
public class Object3 { public string Param3 { get; set; } }
}
How do I achieve this in .NET5?
I have tried following but got reflection exception: System.Reflection.AmbiguousMatchException: 'Multiple custom attributes of the same type found.'
[ServiceContract]
public interface ITestWebService
{
[OperationContract]
string Test(
[XmlElement("object1", typeof(Object1))]
[XmlElement("object2", typeof(Object2))]
[XmlElement("object3", typeof(Object3))]
object request);
}
And also tried this but VS Add Connected Services errored with "More than one message named 'ISampleService_Test_InputMessage' was specified. Each message must have a unique name."
[OperationContract]
string Test(Object1 request);
[OperationContract]
string Test(Object2 request);
Any help would be awesome
You may try this
You can make it post or get based on your need
[OperationContract]
[WebInvoke(UriTemplate = "Test1", Method = "POST"]
string Test(Object1 request);
and
[OperationContract]
[WebInvoke(UriTemplate = "Test2", Method = "POST"]
string Test(Object2 request);
This way you can achieve objective

WCF Serialization Error Using NetTCP

Using VS2012 and NetTcpBinding. I am getting the following error when I call the serviceContract from the client - The service is hosted in IIS:
There was an error while trying to serialize parameter CS.ServiceContracts.Zzzzzz.Common:GetZipCodesResult. The InnerException message was 'Type 'System.DelegateSerializationHolder+DelegateEntry' with data contract name 'DelegateSerializationHolder.DelegateEntry:http://schemas.datacontract.org/2004/07/System' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
Here is my ServiceContract:
[ServiceContract(SessionMode = SessionMode.Allowed, Namespace = "CS.ServiceContracts.Zzzzzz.Common",
Name = "IZzzzzzCommonService")]
public interface IZzzzzzCommonService
{
[OperationContract]
GetZipCodesResponse GetZipCodes(GetZipCodesRequest request);
}
Here is my DataContract:
[DataContract]
[Serializable]
public class GetZipCodesResponse : ResponseBase
{
[DataMember(IsRequired = true)]
public List<ZipCodes> ZipCodes { get; set; }
}
And here is the ResponseBase:
[DataContract]
[Serializable]
[KnownType(typeof(GetZipCodesResponse)), XmlInclude(typeof(GetZipCodesResponse))]
public class ResponseBase
{
[DataMember(IsRequired = true)]
public int ResponseCode { get; set; }
[DataMember(IsRequired = false)]
public int ReasonCode { get; set; }
[DataMember(IsRequired = false)]
public string ReasonText { get; set; }
}
Here is the Implementation of the serviceContract Interface:
public class ZzzzzzCommonService : IZzzzzzCommonService
{
public GetZipCodesResponse GetZipCodes(GetZipCodesRequest request)
{
var response = new GetZipCodesResponse();
try
{
response.ZipCodes = ZipCodes.GetCustom(request.ZipCode, request.City, request.State);
}
catch (Exception ex)
{
this.BuildExceptionResponse(response, ex);
}
return response; // I get to this line ok, but here is where the error occurs
}
}
And here is the client code where I am calling the Service:
public void ZipCodes()
{
var endPoint = new EndpointAddress(
"net.tcp://localhost/CS.WebService.Zzzzzz.Common/ZzzzzzCommonService.svc");
var binding = new NetTcpBinding { TransferMode = TransferMode.Buffered, SendTimeout = TimeSpan.MaxValue, ReceiveTimeout = TimeSpan.MaxValue, MaxReceivedMessageSize = 100000000, MaxBufferSize = 100000000 };
using (var channel = new ChannelFactory<IZzzzzzCommonService>(binding, endPoint))
{
try
{
channel.Endpoint.Contract.SessionMode = SessionMode.Allowed;
var proxy = channel.CreateChannel();
var request = new GetZipCodesRequest();
request = new GetZipCodesRequest { ZipCode = "32701" };
response = proxy.GetZipCodes(request);
}
}
}
I have debugged the code and I am getting into the entity and the dataSet is populated with the appropriate rows but when it sends the dataset back is when I get the errors.
Not sure what I am missing. I believe I have the serviceContract and dataContract members decorated correctly so not sure why it is having problems serializing the List.
The problem was the ZipCode class. It was an Entity class and it was not able to be serialized. So I deconstructed the class and it was able to send it back to the client.

WCF - CODEGEN: Generating message contract since message FileRequest has headers

I am aware that there is a similar question here with no solution.
I'm working on a WCF streaming service over HTTP.
Here are my MessageContract
[MessageContract]
public class FileRequest
{
#region Message Header
[MessageHeader(MustUnderstand = true)]
public Credential Credentials { get; set; }
#endregion
#region Message body
[MessageBodyMember(Order = 1)]
public FileInfo FileInfo { get; set; }
#endregion
#region Ctor
// ...
#endregion
}
[MessageContract]
public class FileRequestResponse
{
#region Message Header
[MessageHeader(MustUnderstand = true)]
public FileInfo FileHeader { get; set; }
[MessageHeader(MustUnderstand = true)]
public OperationResult<bool> OperationResult { get; set; }
#endregion
#region Message Body
[MessageBodyMember]
public Stream FileStream { get; set; }
#endregion
#region Constructor
// ...
#endregion
}
Here is my ServiceContract
[ServiceContract(Namespace = "https://service.contract.example.com")]
public interface IUpdateService
{
[OperationContract(Action = "GetUpdates")]
OperationResult<List<FileInfo>> GetUpates(ApplicationInfo applicationInfo, Credential credential);
[OperationContract(Action = "GetFile")]
FileRequestResponse FileRequest(FileRequest fileRequest);
}
Now the question is why I am getting this error:
// CODEGEN: Generating message
contract since message FileRequest has
headers
When I add my service reference. The end result is that the service contract wraps the FileRequest operation into a wrapper which I do not want.
public FileInfo FileRequest(Credential Credentials, FileInfo, out OperationResult<bool> OperationResult, out System.IO.Stream FileStream)
NOTE:
I have not checked the "Always generate message contracts" in the service reference.
Set [MessageContract(IsWrapped=true)] for all the message contracts in the service and then try generating the proxy .
You might want to try to use the IsWrapped attribute on the message contract:
[MessageContract(IsWrapped=false)]
Not 100% sure which one you'll need (true or false) but that's one of the options you could try.
Also, another observation: I think it's a bit risky to have a method called FileRequest and a message contract which also is called FileRequest.
The generally accepted best practive would be to have a method GetFile, a request message for that called GetFileRequest and a response message type GetFileResponse. Do not use the same names for different things.

Client WCF DataContract has empty/null values from service

I have a simple WCF service that returns the time from the server. I've confirmed that data is being sent by checking with Fiddler. Here's the result object xml that my service sends.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetTimeResponse xmlns="http://tempuri.org/">
<GetTimeResult xmlns:a="http://schemas.datacontract.org/2004/07/TestService.DataObjects" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:theTime>2010-03-26T09:14:38.066372-06:00</a:theTime>
</GetTimeResult>
</GetTimeResponse>
</s:Body>
</s:Envelope>
So, as far as I can tell, there's nothing wrong on the server end. It's receiving requests and returning results.
But on my silverlight client, all the members of the returned object are either null, blank or a default vaule. As you can see the server returns the current date and time. But in silverlight, theTime property on my object is set to 1/1/0001 12:00 AM (default value).
Sooo methinks that the DataContracts do not match up between the server and the silverlight client. Here's the DataContract for the server
[DataContract]
public class Time
{
[DataMember]
public DateTime theTime { get; set; }
}
Incredibly simple. And here's the datacontract on my silverlight client.
[DataContract]
public class Time
{
[DataMember]
public DateTime theTime { get; set; }
}
Literally the only difference is the namespaces within the application. But still the values being returned are null, empty or a .NET default.
Thanks for you help!
UPDATE
Here is the ClientBase that all my services run through. I read an excellent article here to construct it.
public class ClientBase<T> where T :class
{
private T Channel { get; set; }
private Type ContractType { get; set; }
private ClientBase()
{
ContractType = typeof( T );
}
public ClientBase(string endPointConfiguration) :this()
{
Channel = new ChannelFactory<T>( endPointConfiguration ).CreateChannel();
}
public ClientBase( EndpointAddress address, Binding binding ):this()
{
Channel = new ChannelFactory<T>( binding, address ).CreateChannel();
}
public void Begin(string methodName, object state, params object[] parameterArray)
{
Begin( methodName, null, state, parameterArray );
}
public void Begin(string methodName, EventHandler<ClientEventArgs> callBack, object state, params object[] parameterArray)
{
if(parameterArray != null)
{
Array.Resize(ref parameterArray, parameterArray.Length + 2);
}
else
{
parameterArray = new object[2];
}
parameterArray[ parameterArray.Length - 1 ] = new ObjectClientState {CallBack = callBack, MethodName = methodName, UserState = state};
parameterArray[ parameterArray.Length - 2 ] = new AsyncCallback( OnCallBack );
ContractType.InvokeMember( "Begin" + methodName,
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod |
System.Reflection.BindingFlags.Public, null, Channel, parameterArray );
}
private void OnCallBack(IAsyncResult result)
{
ObjectClientState state = result.AsyncState as ObjectClientState;
if(state == null)
return;
Object obj = ContractType.InvokeMember( "End" + state.MethodName,
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod |
System.Reflection.BindingFlags.Public, null, Channel, new object[] {result} );
if(state.CallBack != null)
{
state.CallBack( this, new ClientEventArgs {Object = obj, UserState = state.UserState} );
}
}
public class ClientEventArgs : EventArgs
{
public object Object { get; set; }
public object UserState { get; set; }
public T LoadResult<T>()
{
if( Object is T )
return ( T ) Object;
return default( T );
}
}
private class ObjectClientState
{
public EventHandler<ClientEventArgs> CallBack { get; set; }
public string MethodName { get; set; }
public object UserState { get; set; }
}
}
Here is my interface
[ServiceContract]
public interface ITestService
{
[OperationContract( AsyncPattern = true )]
IAsyncResult BeginGetTime( AsyncCallback callback, object state );
Time EndGetTime( IAsyncResult result );
}
Now I have my service class that makes calls through my BaseService class using this interface.
public class TestSiteService : ClientBase<ITestService>
{
public TestSiteService (string endPointConfiguration):base(endPointConfiguration) { }
public TestSiteService ( EndpointAddress address, Binding binding ) : base( address, binding ) { }
public void GetTime( EventHandler<ClientEventArgs> callBack )
{
Begin( "GetTime", callBack, null, null );
}
}
Finally here is the code that actually calls everything and does the work.
TestSiteService client = new TestSiteService ( new EndpointAddress( "http://localhost:3483/wcf/Service.svc" ), new BasicHttpBinding() );
client.GetTime( delegate( object res, ClientBase<ITestService>.ClientEventArgs e )
{
Dispatcher.BeginInvoke( () => lblDisplay.Text = "Welcome " + e.LoadResult<Time>().theTime );
} );
Whew....I hope no one is lost from all this code I posted :P
Because you don't set the Namespace property on your DataContractAttribute, the namespace will be sythesized from the .NET class/namespace. You can see this in the SOAP message example you posted:
http://schemas.datacontract.org/2004/07/TestService.DataObjects
In order to have the contracts be considered equal, you must set the Namespace property on the DataContract to the same value on both sides. That might look a little something like this:
[DataContract(Namespace="urn:my-test-namespace")]
Extending on Drew Marsh's correct answer (+1 - thx) I had a generated Service Reference which was working, but when I tried to use the Wcf Client Factory one implementing the correct interface (but the namespace was different) then I was experiencing the problem described.
I had no easy way to work out what the "correct" namespace should have been but simply copying the following attributes from the service reference's DataContract entity to the one in the Wcf Client Factory implementation solved the issue;
[System.Runtime.Serialization.DataContractAttribute(Name = "BOSPrice", Namespace = "http://schemas.datacontract.org/2004/07/BOSDataService")]
[System.SerializableAttribute()]

WCF - Sending data to server outside of contract

I have a WCF service with a client application. I have complete control over both the client and server implementation. I have hundreds of methods in the WCF contract which need a piece of information supplied by the client. Instead of modifying hundreds of methods, is there a way I can send specific data from the client with every call to the service, possibly somewhere in the channel?
Maybe when the client is setting up the proxy before making the call, it can store this data somewhere in an internal property of the proxy... the data would then get sent to the server and from within the service method I could inspect the OperationContext or some other piece of memory to get this data back and use it?
Any ideas?
It sounds like you are wanting something like headers like with SOAP webservices. I'm not a WCF expert, but this looks like the WCF equivalent.
It shouldn't actually be that hard. The best way I can think of is to write an IClientMessageInspector that adds a SOAP header into the Message.Headers in its BeforeSendRequest method.
See e.g. http://weblogs.asp.net/paolopia/archive/2007/08/23/writing-a-wcf-message-inspector.aspx
You can't do this trivially. It will take some work.
It's true that SOAP Headers are the perfect way to pass out-of-band data to and/or from a service. But you already have your contract defined, and adding headers will change the contract.
I believe you'll have to start using message contracts.
Original:
[DataContract]
public class ComplexObject
{
[DataMember(Name = "Id")]
public int Id;
[DataMember]
public string Name;
}
[ServiceContract()]
public interface IMyContract
{
void MyOperation(ComplexObject co);
}
public class MyService : IMyContract
{
#region Implementation of IMyContract
public void MyOperation(ComplexObject co)
{
// use co.*
}
#endregion
}
Using Message Contracts:
[DataContract]
public class ComplexObject
{
[DataMember(Name = "Id")]
public int Id;
[DataMember]
public string Name;
}
[DataContract]
public class MyHeader
{
[DataMember]
public string UserName;
[DataMember]
public string Password;
}
[DataContract]
public class OutputHeader
{
[DataMember]
public string Token;
}
[MessageContract]
public class MyOperationRequest
{
[MessageHeader]
public MyHeader Authentication;
[MessageBodyMember]
public ComplexObject TheObject;
}
[MessageContract]
public class MyOperationResponse
{
[MessageHeader]
public OutputHeader OutputHeader;
}
[ServiceContract()]
public interface IMyContract
{
MyOperationResponse MyOperation(MyOperationRequest request);
}
public class MyService : IMyContract
{
public MyOperationResponse MyOperation(MyOperationRequest request)
{
// use request.TheObject.*
// Can also read request.Authentication.*
return new MyOperationResponse
{ OutputHeader = new OutputHeader { Token = "someToken" } };
}
}