I have been inspecting messages sent in a WCF-based system, using the available IClientMessageInspector (and IDispatchMessageInspector).
Currently I'm trying to manually add XML to the message, I cannot manage to get it working.
Situation:
Incoming message has a body like
<s:Body>
<Type xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
...
</Type>
</s:Body>
I want to replace the entire body with custom content, manually structured in a string. I.e., I have a correct XML body in a string, which I want to place within the body of the message.
Is this even possible?
Edit:
To further clarify the question: Can I somehow access the "raw text" of the message, and edit it?
Edit2: I.e. I want to keep the original header and all from the incoming message, but want to replace everything between
<body> </body>
with my custom content which currently resides in a string.
You could use approach similar to one in this blog post https://blogs.msdn.microsoft.com/kaevans/2008/01/08/modify-message-content-with-wcf/
In short you add EndpointBehavior in which you add custom MessageInspector:
Service1Client proxy = new Service1Client();
proxy.Endpoint.Behaviors.Add(new MyBehavior());
public class MyBehavior : IEndpointBehavior
{
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
MyInspector inspector = new MyInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
}
public class MyInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var xml = "XML of Body goes here";
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
Message replacedMessage = Message.CreateMessage(reply.Version, null, xdr);
replacedMessage.Headers.CopyHeadersFrom(reply.Headers);
replacedMessage.Properties.CopyProperties(reply.Properties);
reply = replacedMessage;
}
}
EDIT: added MemoryStream initiated with data from a string value.
Related
I'm new to dialogflow and trying to use permission handler to ask for location permission using .NET core webapi. I've created intent, entities and event(google.assistent.permission) in dialogflow console. Now I want to send a request from my webapi to send the request to access location.
Can somebody please provide a code sample how to send request to access location from my webhook?
You need to include the helper intent DialogFlow JSON as part of the payload:
WebhookResponse response;
Struct payload;
response.Payload = payload;
Alternatively, it can be added as a fulfillment message with the payload type1.
The payload struct can be parsed from JSON:
response.Payload = Struct.Parser.ParseJson(#"{
""google"": {
""expectUserResponse"": true,
""systemIntent"": {
""intent"": ""actions.intent.PLACE"",
""data"": {
""#type"": ""type.googleapis.com/google.actions.v2.PlaceValueSpec"",
""dialogSpec"": {
""extension"": {
""#type"": ""type.googleapis.com/google.actions.v2.PlaceValueSpec.PlaceDialogSpec"",
""permissionContext"": ""To find a location"",
""requestPrompt"": ""Where would you like to go?""
}
}
}
}
}
}");
Or created using the Protobuf API (slightly faster due to skipping the parsing step and type safe, but incredibly ugly):
response.Payload = new Struct
{
Fields =
{
["google"] = Value.ForStruct(new Struct
{
Fields =
{
["expectUserResponse"] = Value.ForBool(true),
["systemIntent"] = Value.ForStruct(new Struct
{
// ... and so on
})
}
})
}
};
Keep in mind that including any message in the payload (which is necessary to call the helper) will override any other messages you added previously and ignore anything added afterwards (they are still part of the returned object, but stripped out by DialogFlow). That means: If you want any other rich response, it currently also needs to be manually added to the payload. At that point, you might as well create the entire JSON response from scratch.
I'm very new to WCF and have a question that I hope you can help me with.
Project: I've been asked to create a WCF service that allows a client to be able to upload a word file along with some metadata.
The client doesn't have a sample of the POST call they'll be making so I can't create a class off of that WSDL, but the post would contain data like this:
{
author: 'John Doe',
pages: '32',
size: '14432',
authToken: '322222222233',
encoding: 'binary'
name: 'Document1.doc'
}
I'm thinking of creating an [OperationContract] such as bool UploadFile(CustomDocument inputDocument) instead of bool UploadFile (string author, string encoding ....).
My question: If I use a custom object as an input parameter (CustomDocument) for an [OperationContract] would the client be able to pass all the information as string, int etc in its service call, or would they have to first create an instance of CustomDocument on their end, and then include that object in the post?
Sorry, I'm very new to WCF, my apologies in advance if this question doesn't make any sense; I'll update it based on your feedback.
You have to make sure that CustomDocument is a Serializable object and have a public parameterless constructor.
The easiest way is share the dll that contains the class CustomDocument between the WebService and the Application that will use it.
But personally when I try to send a complex object to a WebServce I prefer to serialize as a byte array and then Deserialize inside the WebService.
Good luck!
You don't need the custom object CustomDocument. Suppose you have this service
[ServiceContract]
public interface IMyTestServce
{
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "/Upload?author={author}&pages={pages}&size={size}&name={name}&authToken={authToken}")]
void Upload(string author, int pages, long size, string name,
string authToken,
Stream file);
}
public class MyTestService : IMyTestServce
{
public void Upload(string author, int pages, long size, string name,
string authToken,
Stream file)
{
Console.WriteLine(String.Format("author={0}&pages={1}&size={2}&name={3}&authToken={4}", author, pages, size, name, authToken));
Console.WriteLine(new StreamReader(file).ReadToEnd());
}
}
You can easily call it like
HttpClient client = new HttpClient();
var content = new StreamContent(File.OpenRead(filename);
await client.PostAsync("http://localhost:8088/Upload?author=aa&pages=3&name=bb&authToken=112233", content);
PS: You need to use webHttpBinding (or WebServiceHost if it is not hosted in IIS).
I have a problem that given 3rd party WSDL I am able from a Console App to easily create a service proxy that works, but from a WF4 WF service I am not. The generated proxy in the latter case is clearly buggy, involving specifically 2 problems:
a) Message contracts always generated when not requested or needed
b) Incorrect response messages and xml wrapper names used, resulting in null response objects and failed deserialization
The problem I am facing is in the actual generation of the Reference.cs class on the basis of 3rd party WSDL. In the WSDL there are many operations, and in order of appearance 2 of them are as so:
<operation name="pu013">
<documentation>
<description>Check-response service</description>
<help>The service handles (cut out)</help>
</documentation>
<input message="tns:pu013Request" />
<output message="tns:SimpleResponse" />
</operation>
...
<operation name="mi102">
<documentation>
<description>Instruction insert to Matching System</description>
<help>This service (cut out)</help>
</documentation>
<input message="tns:mi102Request" />
<output message="tns:SimpleResponse" />
</operation>
What this results in in the Reference.cs is the following C#:
WorkflowService1.PSE.pu013Response pu013(WorkflowService1.PSE.pu013Request request);
...
WorkflowService1.PSE.pu013Response mi102(WorkflowService1.PSE.mi102Request request);
Note that for some reason the mi102 operation is generated with the INCORRECT response message of pu013Response, which is declared as this:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="pu013Response", WrapperNamespace="http://pse/", IsWrapped=true)]
public partial class pu013Response {
Note the WrapperName prevents the XML serializer from recognising the response, which is mi102Response, so for all operations that are not pu013 I always get a NULL response.
Also, this does NOT occur if I add a reference from a console application. This does not generate Message contracts, and in this case, invocation and response work.
What is different? Is svcutil being invoke behind the scenes? If so, what is different about the parameters used? Can svcutil be used to generate the xamlx activities too, so that I might find a command line workaround?
This looks like a VS / Add Service Reference bug. The alternative is to manually correct many operations in the Reference.cs.
Ideally, I am looking for a way to easily, automatically, run svcutil or Add Service Reference so that the Reference class is correct and the xamlx activities generated. A nice to have is an explanation of why there is a difference, and behind the scenes what is happening.
UPDATE:
Message contracts generated in the console app result in the same problem - incorrect Response declarations. The problem goes away if parameters are used instead of messages, which are not available from a WF service app.
I am far from an authority on these issues, and while this response below might not be an exact fit to your problem, my recent experience of making a proxyless connection to a service might offer some insight to you or the next person with a similar issue.
I would start by seeing if you can hand roll the SOAP request using fiddler, and see if you are able to create the correct message and send that along. Since you describe the automation tools as being buggy (or perhaps there is a config issue you're not getting just so). Either way, having a clear understanding of the shape of the contract, and being able to perform a reliable test in fiddler may offer clarity.
You don't necessarily need to rely on using a proxy. You can create your own soap message and send it along one of two ways. The first is using the ChannelFactory.
Create your message body (if required, message class can work w/out one)
Create your message
Send your message via the ChannelFactory
For step 1 you can construct your message by making a simple POCO to mirror what is expected in your contract. You should be able to derive what that class is via the WSDL.
Let's say the service is something like this:
[ServiceContract(Namespace = "http://Foo.bar.car")]
public interface IPolicyService
{
[OperationContract]
PolicyResponse GetPolicyData(PolicyRequest request);
}
public class PolicyData : IPolicyService
{
public PolicyResponse GetPolicyData(PolicyRequest request)
{
var polNbr = request.REQ_POL_NBR;
return GetMyData(polNbr);
}
}
You would need a class something like this:
[DataContract(Namespace = "http://Foo.bar.car")]
public class GetPolicyData
{
[DataMember]
public request request { get; set; }
}
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy")]
public class request
{
///<summary>
/// Define request parameter for SOAP API to retrieve selective Policy level data
/// </summary>
[DataMember]
public string REQ_POL_NBR { get; set; }
}
and then you would call it like this:
private static Message SendMessage(string id)
{
var body = new GetPolicyData {request = new request{ REQ_POL_NBR = id }};
var message = Message.CreateMessage(MessageVersion.Soap11, "http://Foo.bar.car/IPolicyService/GetPolicyData", body);
// these headers would probably not be required, but added for completeness
message.Headers.Add(MessageHeader.CreateHeader("Accept-Header", string.Empty, "application/xml+"));
message.Headers.Add(MessageHeader.CreateHeader("Content-Type", string.Empty, "text/xml"));
message.Headers.Add(MessageHeader.CreateHeader("FromSender", string.Empty, "DispatchMessage"));
message.Headers.To = new System.Uri(#"http://localhost:5050/PolicyService.svc");
var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
MessageEncoding = WSMessageEncoding.Text,
MaxReceivedMessageSize = int.MaxValue,
SendTimeout = new TimeSpan(1, 0, 0),
ReaderQuotas = { MaxStringContentLength = int.MaxValue, MaxArrayLength = int.MaxValue, MaxDepth = int.MaxValue }
};
message.Properties.Add("Content-Type", "text/xml; charset=utf-8");
message.Properties.Remove("Accept-Encoding");
message.Properties.Add("Accept-Header", "application/xml+");
var cf = new ChannelFactory<IRequestChannel>(binding, new EndpointAddress(new Uri("http://localhost:5050/PolicyService.svc")));
cf.Open();
var channel = cf.CreateChannel();
channel.Open();
var result = channel.Request(message);
channel.Close();
cf.Close();
return result;
}
What you receive back will be a Message, which you will need to deserialize, and there are a few OOTB ways of doing this, (Message.GetReaderAtBodyContents, Message.GetBody) in keeping w/the hand-rolled theme:
/// <summary>
/// Class MessageTransform.
/// </summary>
public static class MessageTransform
{
/// <summary>
/// Gets the envelope.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>XDocument.</returns>
public static XDocument GetEnvelope(Message message)
{
using (var memoryStream = new MemoryStream())
{
var messageBuffer = message.CreateBufferedCopy(int.MaxValue);
var xPathNavigator = messageBuffer.CreateNavigator();
var xmlWriter = XmlWriter.Create(memoryStream);
xPathNavigator.WriteSubtree(xmlWriter);
xmlWriter.Flush();
xmlWriter.Close();
memoryStream.Position = 0;
var xdoc = XDocument.Load(XmlReader.Create(memoryStream));
return xdoc;
}
}
/// <summary>
/// Gets the header.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>XNode.</returns>
public static XNode GetHeader(Message message)
{
var xdoc = GetEnvelope(message);
var strElms = xdoc.DescendantNodes();
var header = strElms.ElementAt(1);
return header;
}
/// <summary>
/// Gets the body.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="localName">Name of the local.</param>
/// <param name="namespaceName">Name of the namespace.</param>
/// <returns>IEnumerable<XElement>.</returns>
public static IEnumerable<XElement> GetBody(Message message, string localName, string namespaceName)
{
var xdoc = GetEnvelope(message);
var elements = xdoc.Descendants(XName.Get(localName, namespaceName));
return elements;
}
}
OR you could build your soap envelope by hand and use WebClient:
using System.Net;
using System.Xml.Linq;
public static class ClientHelper
{
public static string Post(string targetUrl, string action, string method, string key, string value)
{
var request = BuildEnvelope(method, key, value);
using (var webClient = new WebClient())
{
webClient.Headers.Add("Accept-Header", "application/xml+");
webClient.Headers.Add("Content-Type", "text/xml; charset=utf-8");
webClient.Headers.Add("SOAPAction", action);
var result = webClient.UploadString(targetUrl, "POST", request);
return result;
}
}
public static string BuildEnvelope(string method, string key, string value)
{
XNamespace s = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace d = "d4p1";
XNamespace tempUri = "http://tempuri.org/";
XNamespace ns = "http://Foo.bar.car";
XNamespace requestUri = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy";
var xDoc = new XDocument(
new XElement(
s + "Envelope",
new XAttribute(XNamespace.Xmlns + "s", s),
new XElement(
s + "Body",
new XElement(
ns + method,
new XElement(requestUri + "request",
new XElement(tempUri + key, value))
)
)
)
);
// hack - finish XDoc construction later
return xDoc.ToString().Replace("request xmlns=", "request xmlns:d4p1=").Replace(key, "d4p1:" + key);
}
which is called with:
return ClientHelper.Post("http://localhost:5050/PolicyService.svc", "http://Foo.bar.car/IPolicyService/GetPolicyData", "GetPolicyData", "REQ_POL_NBR", id);
Testing it in Fiddler would look something like this:
Post action: http://localhost:5050/PolicyService.svc
Header:
User-Agent: Fiddler
SOAPAction: http://Foo.bar.car/IPolicyService/GetPolicyData
Content-type: text/xml
Host: localhost:5050
Content-Length: 381
Body:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetPolicyData xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://Foo.bar.car">
<request xmlns:d4p1="http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy">
<d4p1:REQ_POL_NBR>1</d4p1:REQ_POL_NBR>
</request>
</GetPolicyData>
</s:Body>
</s:Envelope>
Again, this answer isn't trying to resolve how to invoke svcUtil differently, but to avoid calling it altogether, so I hope the edit gods don't ding me for that ;-)
My code above has been inspired by better developers than I, but I hope it helps.
http://blogs.msdn.com/b/stcheng/archive/2009/02/21/wcf-how-to-inspect-and-modify-wcf-message-via-custom-messageinspector.aspx
I would suggest you to generated wsdl proxy using command line utility and add generated proxy file in your project. It will work from every project and you can find the required configurations from output.config that will generate from command line utility.
If you require the wsdl command and options then I can provide you.
I wrote c# code for a Soap XML request, I have verified this code generating an XML class.
My question is how to send request and receive response using c# code.
Please be kind with my simple or basic mistakes because I am a newbie to XML but your help would be really appreciated.
SOAP XML Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ce="http://www." xmlns:os="http://www.domainname.com/schema/soap/v1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<soapenv:Header />
<soapenv:Body>
<ce:message>
<ce:m_control>
<os:control_timestamp>2001-12-31T12:00:00</os:control_timestamp>
<os:message_id>000000000000000000000000000000000</os:message_id>
<os:message_type>Contract Enquiry Request</os:message_type>
<os:message_version>ce/v2.2/NameContractRequest</os:message_version>
<os:expected_response_type>synchronous</os:expected_response_type>
<os:initiator_id>initiator_id</os:initiator_id>
<os:initiator_orchestration_id>initiator_orchestration_id</os:initiator_orchestration_id>
<os:KeyInfo>
<ds:X509Data>
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=OSIS Customer CA, O=Origo Secure Internet Services Ltd., CN=OSIS Customer CA</ds:X509IssuerName>
<ds:X509SerialNumber>111111111111111111111111111111111111</ds:X509SerialNumber>
</ds:X509IssuerSerial>
<ds:X509SubjectName>C=GB, O=FirmID3400010000023NR11QQ, OU=CPS - www.unipass.co.uk/cps, OU=Warning/Terms of Use - www.unipass.co.uk/tou, OU=EmployeeID10101101010101, OU=TPSP2, OU=BPNR1 1QQ, CN=Testt Orgg/emailAddress=Fname.Lname#aviva.co.uk</ds:X509SubjectName>
</ds:X509Data>
</os:KeyInfo>
<os:responder_id>os:responder_id</os:responder_id>
</ce:m_control>
<ce:m_content>
<ce:b_control>
<ce:contract_enquiry_reference>TestRequest</ce:contract_enquiry_reference>
</ce:b_control>
<ce:intermediary>
<ce:FirmFSARef id="idvalue14">456123</ce:FirmFSARef>
</ce:intermediary>
<ce:request_scope>
<ce:contract_details_required_ind>No</ce:contract_details_required_ind>
<ce:valuation_currency>GBP</ce:valuation_currency>
<ce:fund_code_type_required>SEDOL</ce:fund_code_type_required>
<ce:valuation_request ce:type="Current" />
</ce:request_scope>
<ce:contract>
<ce:contract_reference_number>TL12345678</ce:contract_reference_number>
</ce:contract>
</ce:m_content>
</ce:message>
</soapenv:Body>
</soapenv:Envelope>
################################################ c# code ##################
Guid CEGuid = Guid.NewGuid();
string GuidString = CEGuid.ToString();
string CEVersion = "";
string URL = "";
string ResponderId = "";
string ContractDetailsRequired = "Yes";
using (XmlTextWriter xmlRequestWriter = new XmlTextWriter(#"C:/Unipass/PensionRequest.xml", Encoding.UTF8))
{
xmlRequestWriter.WriteStartDocument();
xmlRequestWriter.WriteComment("This file is generated by the program.");
xmlRequestWriter.WriteStartElement("soapenv:Envelope");
xmlRequestWriter.WriteAttributeString("xmlns:soapenv", null, "http://schemas.xmlsoap.org/soap/envelope/");
xmlRequestWriter.WriteAttributeString("xmlns:ce", null, "http://www.WhateveryDomain.com/schema/ce/v2.2/WhateverRequest");
xmlRequestWriter.WriteAttributeString("xmlns:os", null, "http://www.WhateveryDomain.com/schema/soap/v1");
xmlRequestWriter.WriteAttributeString("xmlns:ds", null, "http://www.w3.org/2000/09/xmldsig#");
xmlRequestWriter.WriteStartElement("soapenv:Header");
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteStartElement("ce:message");
xmlRequestWriter.WriteStartElement("ce:m_control");
xmlRequestWriter.WriteElementString("os:control_timestamp", DateTime.Now.ToString("s"));
xmlRequestWriter.WriteElementString("os:message_id", GuidString);
xmlRequestWriter.WriteElementString("os:message_type", "Contract Enquiry Request");
xmlRequestWriter.WriteElementString("os:message_version", "ce/v2.2/WhateverRequest");
xmlRequestWriter.WriteElementString("os:expected_response_type", "synchronous");
xmlRequestWriter.WriteElementString("os:initiator_id", "initiator_id");
xmlRequestWriter.WriteElementString("os:initiator_orchestration_id", "initiator_orchestration_id");
xmlRequestWriter.WriteStartElement("os:KeyInfo");
xmlRequestWriter.WriteStartElement("ds:X509Data");
xmlRequestWriter.WriteStartElement("ds:X509IssuerSerial");
xmlRequestWriter.WriteElementString("ds:X509IssuerName", "CN=OSIS Customer CA, O=Origo Secure Internet Services Ltd., CN=OSIS Customer CA");
xmlRequestWriter.WriteElementString("ds:X509SerialNumber", "111111111111111111111111111111111");
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteElementString("ds:X509SubjectName", "C=GB, O=FirmID3400010000023NR11QQ, OU=CPS - www.unipass.co.uk/cps, OU=Warning/Terms of Use - www.unipass.co.uk/tou, OU=EmployeeID01200012000003, OU=TPSP2, OU=BPNR1 1QQ, CN=Testt Orgg/emailAddress=fname.lastname#aviva.co.uk");
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteElementString("os:responder_id", "os:responder_id");
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteStartElement("ce:m_content");
xmlRequestWriter.WriteStartElement("ce:b_control");
xmlRequestWriter.WriteElementString("ce:contract_enquiry_reference", "TestRequest");
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteStartElement("ce:intermediary");
xmlRequestWriter.WriteStartElement("ce:FirmFSARef");
xmlRequestWriter.WriteElementString("id", "456123");//="idvalue14">
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteStartElement("ce:request_scope");
xmlRequestWriter.WriteElementString("ce:contract_details_required_ind", "Yes");
xmlRequestWriter.WriteElementString("ce:valuation_currency", "GBP");
xmlRequestWriter.WriteElementString("ce:fund_code_type_required", "SEDOL");
xmlRequestWriter.WriteStartElement("ce:valuation_request");
xmlRequestWriter.WriteElementString("ce:type", "Current");//"ce:type","Current"
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteStartElement("ce:contract");
xmlRequestWriter.WriteElementString("ce:contract_reference_number", "PP12345678");
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndDocument();
xmlRequestWriter.Flush();
Please could you tell me how to send Request and receive response from WCF service using above code.
Even though I have added wsdl reference but I don't know how to pass XmlElement[] in the line below.
serviceReference.getDetail(ref XmlElement[] Any);
Here is a method in Reference class.
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://www.domainname.com/whatever/webname/schema/ce/v2.2/NameContractServic" +
"e", ConfigurationName="TestingPension.NameContractServiceDetailType")]
public interface NameContractServiceDetailType {
// CODEGEN: Generating message contract since the wrapper namespace (http://www.domainname.com/schema/ce/v2.2/NameContractRequest) of message getDetailRequest does not match the default value (http://www.domainname.com/name/name/schema/ce/v2.2/NameContractService)
[System.ServiceModel.OperationContractAttribute(Action="http://www.origostandards.com/schema/ce/v2.2/CEPensionSingleContract#getDetail", ReplyAction="*")]
[System.ServiceModel.FaultContractAttribute(typeof(SoapReqResWebApplication.TestingPension.Error[]), Action="http://www.domainname.com/schema/ce/v2.2/NameContract#getDetail", Name="errors", Namespace="http://www.domainname.com/schema/tech/v1.0/SOAPFaultDetail")]
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
SoapReqResWebApplication.TestingPension.getDetailResponse getDetail(SoapReqResWebApplication.TestingPension.getDetailRequest request);
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(WrapperName="message", WrapperNamespace="http://www.domainname.com/schema/ce/v2.2/NameContractRequest", IsWrapped=true)]
public partial class getDetailRequest {
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=0)]
[System.Xml.Serialization.XmlAnyElementAttribute()]
public System.Xml.XmlElement[] Any;
public getDetailRequest() {
}
public getDetailRequest(System.Xml.XmlElement[] Any) {
this.Any = Any;
}
}
#CodeCaster
I tried your code as:
var documentToSend = new XmlDocument();
//// TODO: add all elements you like
using (XmlTextWriter xmlRequestWriter = new XmlTextWriter(#"C:/Unipass/Request.xml", Encoding.UTF8))
{
xmlRequestWriter.WriteStartDocument();
xmlRequestWriter.WriteStartElement("ce:message");
xmlRequestWriter.WriteEndElement();
xmlRequestWriter.WriteEndDocument();
xmlRequestWriter.Flush();
documentToSend.Save(xmlRequestWriter);
//// Create an array with the root message node as only element.
var xmlToSend = new XmlElement[] { documentToSend.DocumentElement }; //xmlToSend = null
}
Might be I am doing completely wrong, please can you tell me why xmlToSend is null.
It seems that you are writing code by hand to serialise messages into SOAP/XML. The SOAP request example is an actual instance document rather than WSDL, yes? This is one of the things that WCF does for you - you should not need to generate your own SOAP messages.
In WCF, you focus on the object types you want to transmit and the operations you want to support. Then WCF does all the heavy lifting of SOAP serialisation for you. That is the main point of WCF really.
It's quite hard to decipher from your sample SOAP message what exactly you are trying to do, so I would recommend looking at one of the simple WCF samples, say from here
http://msdn.microsoft.com/en-us/library/vstudio/ms751514(v=vs.90).aspx
Get a simple one up and running using wsHttpBinding or basicHttpBinding (these are SOAP bindings in WCF). Then capture the HTTP request using Fiddler or similar and see what WCF has done for you in terms of the SOAP envelope generation.
You should then be able to translate that to your own case.
It seems like WCF's proxy generator cannot create a class from the provided WSDL/XSD, given the generated method signature is getDetailRequest(System.Xml.XmlElement[] Any). What does a tool like SoapUI say about the WSDL?
A way to solve this is to indeed manually generate the message, which is just the <ce:message>..</ce:message> block. WCF will wrap it in a proper SOAP envelope when you call the service method.
You'll have to play around a bit with the XmlDocument class, but I think something like this will do it:
// Create an XmlDocument and fill it
var documentToSend = new XmlDocument();
// TODO: add all elements you like
// Create an array with the root message node as only element.
var xmlToSend = new XmlElement[] { documentToSend.DocumentElement };
// Call the service.
var response = serviceReference.getDetail(xmlToSend);
Another way would be to manually recreate the request class in C# and populate and serialize an instance of it to XML when sending a request. The ultimate way would be to fix the XML so Add Service Reference (SvcUtil) can generate classes from the service's metadata.
As for your edit, you're not writing the XML elements to the documentToSend, but you write them to your file.
This is the scenario: I have a WCF service running, who communicates with this method, in C#:
public bool ValidateUser(UserPass up)
{
initializeAttributes();
IMembershipService Member = new AccountMembershipService();
bool login = Member.ValidateUser(up.User, up.Pass);
return login;
}
The parameter are encapsulated in this class:
[DataContract]
public class UserPass
{
string user = "";
string pass = "";
string email = "";
[DataMember]
public string User
{
get { return user; }
set { user = value; }
}
[DataMember]
public string Pass
{
get { return pass; }
set { pass = value; }
}
[DataMember]
public string Email
{
get { return email; }
set { email = value; }
}
}
Now, I want to connect to the server via an Android application, now, my question is, how can I replicate the UserPass class in Java, so the ValidateUser method can receive its parameter in a way it can understands it.
for reference, this is the code where I'm obtaining the User and Password:
private void validateUser(String user, String pass)
{
String SOAP_ACTION = "http://tempuri.org/IUserService/ValidateUser/";
String METHOD_NAME = "ValidateUser";
String NAMESPACE = "http://tempuri.org/";
String URL = "http://10.0.2.2/UserService.svc";
AlertDialog popup;
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty(user, pass);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut = request;
envelope.dotNet = true;
HttpTransportSE httpTransport = new HttpTransportSE(URL);
try
{
httpTransport.call(SOAP_ACTION, envelope); //here's the exception!!
Object response = envelope.getResponse();
popup = createAlertDialog("Respuesta",response.toString(),"OK");
popup.show();
}
catch (Exception exception)
{
String exceptionStr=exception.toString();
popup = createAlertDialog("Exception!!",exceptionStr,"OK");
popup.show();
}
}
The exception it throws is xmlpullparserexception, which, according to my understanding, is because of a missmatch between the parameters of the request and the actual method.
Many thanks for reading my question, and many more for those who can answer it :)
EDIT:
I finnaly got how to compare the XMLs... now, this is what my SOAP is providing:
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d="http://www.w3.org/2001/XMLSchema"
xmlns:c="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header />
<v:Body>
<ValidateUser xmlns="http://tempuri.org/" id="o0" c:root="1">
<User i:type="d:string">someuser</User>
<Pass i:type="d:string">somepass</Pass>
<Email i:type="d:string"></Email>
</ValidateUser>
</v:Body>
and this is what it SHOULD have made (retrieved from WCF Test Client application from Visual Studio 2010):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IUserService/ValidateUser</Action>
</s:Header>
<s:Body>
<ValidateUser xmlns="http://tempuri.org/">
<up xmlns:d4p1="http://schemas.datacontract.org/2004/07/LiveAndesWCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Email i:nil="true" />
<d4p1:Pass>somepass</d4p1:Pass>
<d4p1:User>someuser</d4p1:User>
</up>
</ValidateUser>
</s:Body>
</s:Envelope>
Now, I'm lost on how to code my soap code to have it generate a xml file like the latter one.
Many thanks again.
have you tried to look at the xml created by the soap call? you can compare it to the xml created by a .net proxy. maybe this helps to find a solution.
here is how you can enable the logging of the soap calls:
http://msdn.microsoft.com/en-us/library/ms730064.aspx
This line looks suspect to me:
request.addProperty(user, pass);
As far as I can tell, SoapObject comes from the KSOAP2 library, and according to the docs, addProperty takes the name of the property and the value. To set user and pass, I would expect something more like this:
request.addProperty("user", user);
request.addProperty("pass", pass);
Currently, it looks like you're adding a single property named using the value of the user parameter. If the endpoint is expecting at least 2 arguments, then this could be the source of your mismatch.
Also, is the value "Email", from the UserPass wrapper class, optional? As I don't see it being set anywhere, and the wrapper class suggests it's required by the SOAP request