Receiving eBay SOAP notification messages - c#

I am trying to receive notifications using ebay and their SDK. I have managed to find a working example using an ASP.net web service. However I want to be able to host this on Azure and moving this to a WCF seems like a suitable solution. I have tried doing so and the closest I have got is getting a xml serializer error like below:
The server encountered an error processing the request. The exception message is 'Unable to deserialize XML body with root name 'Envelope' and root namespace 'http://schemas.xmlsoap.org/soap/envelope/' (for operation 'GetOutBid' and contract ('IReceiver', 'urn:ebay:apis:eBLBaseComponents')) using XmlSerializer. Ensure that the type corresponding to the XML is added to the known types collection of the service.'
From what I have seen online, looks like it could be to do with how the datacontract should be setup - I'm new to WCF, so not sure!
Below is what is working in the web service which I'm trying to put into WCF:
[WebMethod()]
[System.Web.Services.Protocols.SoapHeaderAttribute("RequesterCredentials", Direction = System.Web.Services.Protocols.SoapHeaderDirection.In)]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(Action = "http://developer.ebay.com/notification/OutBid", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Bare)]
public void OutBid(GetItemResponseType GetItemResponse)
{
if (CheckSignature(GetItemResponse.Timestamp))
{
//Implement your own business logic here
}
else
{
//Implement your own business logic here
}
LogRequest(Server.MapPath("files/OutBid_" + GetItemResponse.Item.ItemID + "_" + GetItemResponse.Item.SellingStatus.BidCount.ToString() + "_" + GetItemResponse.CorrelationID + ".xml"));
}
public class student { public int rollno { get; set; } }
public class school { public student obj { get; set; } }
class Program
{
static void Main(string[] args)
{
school obje = new school(); obje.obj.rollno = 2; Console.WriteLine(obje.obj.rollno);
}
}

Based on your code, I found that the protocol between the service and client is SOAP. To support SOAP, we need to use basicHttpBinding in WCF. The protocolMapping configuration section in web.config should be like this.
<protocolMapping>
<add binding="basicHttpBinding" scheme="http"/>
</protocolMapping>
After that, we could define the service contact and service to handle requests.
The service contact could be like this.
[ServiceContract]
public interface IService1
{
[OperationContract(Action= "http://developer.ebay.com/notification/OutBid")]
string OutBid(school value);
}
A sample of Service.
public class Service1 : IService1
{
public string OutBid(school value)
{
return string.Format("The rollno is {0}", value.obj.rollno);
}
}
Following are my test results.
Sample request.
<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://developer.ebay.com/notification/OutBid</Action>
</s:Header>
<s:Body>
<OutBid xmlns="http://tempuri.org/">
<value xmlns:d4p1="http://schemas.datacontract.org/2004/07/TestSOAP" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:obj>
<d4p1:rollno>2</d4p1:rollno>
</d4p1:obj>
</value>
</OutBid>
</s:Body>
</s:Envelope>
Sample result.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body>
<OutBidResponse xmlns="http://tempuri.org/">
<OutBidResult>The rollno is 2</OutBidResult>
</OutBidResponse>
</s:Body>
</s:Envelope>
If you can't finish the work on your side, please post your sample XML request content for further discussion.

Related

SOAP Request format

I am trying to create a SOAP web service to use with a client.
According to the documentation, we have to create a web service with two methods that return certain information. I have created them without a problem, but I can not get them to fit the example format.
I have to say that for years I did not create any SOAP services (since Net 2.0) because I have been doing REST services for a long, so if you see any nonsense, forgive me.
The example request that there is in the documentation (the good one) is this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pag="http://pagorecibos.pagosrecibos.com/">
<soapenv:Header/>
<soapenv:Body>
<pag:obtenerImporte>
<referencia>040</referencia>
</pag:obtenerImporte>
</soapenv:Body>
</soapenv:Envelope>
That's the format that my service want to recieve:
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<obtenerImporte xmlns="http://tempuri.org/">
<referencia>string</referencia>
</obtenerImporte>
</soap:Body>
</soap:Envelope>
The code of my service is this:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service: System.Web.Services.WebService
{
[WebMethod]
[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]
public string ObtenerImporte(ObtenerImporte obtenerImporte)
{
return "Hola a todos";
}
[WebMethod]
[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]
public string RealizarPago(RealizarPago realizarPago)
{
return "Hola a todos";
}
}
Class (the other class is similar):
public class ObtenerImporte
{
[XmlElement("referencia")]
public string Referencia { get; set; }
}
The difference is in the namespace and the prefix in node "obtenerImporte" but I can't resolve it.
I'm using VS2017 Community Edition y created the project using "ASP.NET Web Service" template.
Problem solved!!!. Adding and removing code.... suddenly it works :D. The right way to define the service is this (thanks #zaitsman for your clue):
[WebService(Namespace = "http://pagorecibos.pagosrecibos.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
[WebMethod]
[SoapDocumentMethod(Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Bare)]
public string ObtenerImporte(ObtenerImporte obtenerImporte)
{
return "Obtener Importe";
}
[WebMethod]
[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]
public string RealizarPago(RealizarPago realizarPago)
{
return "Realizar Pago";
}
}
I hope that this solution could help other one.

WCF accessing FaultException details from IErrorHandler.ProvideFault in the client

I am trying to attach additional information to a FaultException for a WCF service, and then access it in the client. I am using the IErrorHandler ProvideFault method as my service has many methods that it exposes and I want to define the error handling logic in one place.
I have defined a data class CoreFault:
[DataContract]
public class CoreFault
{
[DataMember]
public string Foo { get; set; }
}
And implemented the IErrorHandler interface to return a strongly typed FaultException:
public class ErrorHandler : IErrorHandler
{
public void ProvideFault(Exception error, MessageVersion version, ref Message message)
{
var faultException = new FaultException<CoreFault>(
new CoreFault
{
Foo = "Bar"
},
"An error occurred",
null);
var fault = faultException.CreateMessageFault();
message = Message.CreateMessage(
version,
fault,
faultException.Action);
}
}
My service is configured in code:
public static void Configure(ServiceConfiguration config)
{
(config.Description.Behaviors[0] as ServiceBehaviorAttribute).IncludeExceptionDetailInFaults = true;
config.Description.Behaviors.Add(new ErrorHandlingBehaviour());
}
I have breakpointed the code after creating the message, and it shows the following XML:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header/>
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-GB">An error occurred</faultstring>
<detail>
<CoreFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/EmployerPortal.Models.Faults">
<Foo>Bar</Foo>
</CoreFault>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
But in my client, the error that is thrown is FaultException and not FaultException<CoreFault>, and the extra information is lost.
try
{
// Call service here
...
}
catch (FaultException<CoreFault> e)
{
throw;
}
catch (FaultException e)
{
// This line is hit
throw;
}
How can I pass Foo to the client, so that it can access it when it handles the exception?

Asp.net webservice always recieving null values from soap ui

I'm posting below message from soap UI and i always receiving null value in webservice. Whatever message I'm posting from soap UI it is taking as null only.
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
Test
</env:Body>
</env:Envelope>
Below is my simple WebService
[WebService(Namespace = "http://test.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class TestService : System.Web.Services.WebService {
[WebMethod]
public int TestMethod(string message)
{
try
{
File.WriteAllText("D:\\abc.xml", message);
return 0;
}
catch (Exception ex)
{
return 1;
}
}
}
You're not specifying which method to call or the parameters of that method in your body. You'll need something like this:
<env:Body>
<m:TestMethod xmlns:m="http://tempuri.org">
<message>Test</message>
</m:TestMethod>
</env:Body>

Adding WCF service to existing application?

I have an existing application which now has the requirement to be interacted with from a mobile device. The mobile device has a wifi connection, and would be connecting to the PC hosting the main application on a LAN. The mobile device simply needs to add/edit/find/delete objects the main application is maintaining. The main application already encapsulates his functionality in some simple repository classes.
I believe the approach would be to add a WCF service to the main application which exposes a set of methods the mobile device can call against. However I have looked up WCF today and tried to setup an example application, but when called the WCF methods it is unable to access any data, as such I feel the WCF service is running in its own application domain and as such has no access to the same static classes in the main application.
If I setup a WCF service project in VS 2008/2010, how can I run it under the same application domain as the main WinForms application, so that a remote application on the LAN can communicate with it to get data from the application.
Below is my sample WinForm
using System;
using System.ServiceModel;
using System.Windows.Forms;
using DataProject;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public TestDataProject.DataStore Datastore = TestDataProject.DataStore.GetInstance();
public Form1()
{
InitializeComponent();
Datastore.Add(new MyObj { ID = 1, Data = "hello" });
Datastore.Add(new MyObj { ID = 2, Data = "world" });
Datastore.Add(new MyObj { ID = 3, Data = "item3" });
Datastore.Add(new MyObj { ID = 4, Data = "item4" });
Datastore.Add(new MyObj { ID = 5, Data = "fiver" });
}
}
}
What I need from a WCF service, is access to TestDataProject.DataStore.GetInstance();
Edit
I achieved this by
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Windows.Forms;
using DataProject;
using TestDataProject;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public TestDataProject.DataStore Datastore = TestDataProject.DataStore.GetInstance();
public Form1()
{
InitializeComponent();
Datastore.Add(new MyObj { ID = 1, Data = "hello" });
Datastore.Add(new MyObj { ID = 2, Data = "world" });
Datastore.Add(new MyObj { ID = 3, Data = "item3" });
Datastore.Add(new MyObj { ID = 4, Data = "item4" });
Datastore.Add(new MyObj { ID = 5, Data = "fiver" });
ServiceHost host = new ServiceHost(typeof(SimpleService),
new Uri("http://localhost:8001/MetadataSample"));
try
{
// Check to see if the service host already has a ServiceMetadataBehavior
ServiceMetadataBehavior smb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
// If not, add one
if (smb == null)
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Add MEX endpoint
host.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex"
);
// Add application endpoint
host.AddServiceEndpoint(typeof(ISimpleService), new WSHttpBinding(), "");
// Open the service host to accept incoming calls
host.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
//host.Close();
}
catch (CommunicationException commProblem)
{
Console.WriteLine("There was a communication problem. " + commProblem.Message);
Console.Read();
}
}
public void Display(string msg)
{
MessageBox.Show(msg);
}
}
[ServiceContract]
public interface ISimpleService
{
[OperationContract]
string Test();
[OperationContract]
string GetOBJDesc(int id);
[OperationContract]
MyObj GetObject(int id);
}
public class SimpleService : ISimpleService
{
#region Implementation of ISimpleService
public string Test()
{
return "Hello world";
}
public string GetOBJDesc(int value)
{
MyObj obj = DataStore.GetInstance().Get(value);
if (obj != null)
{
return obj.Data;
}
return "";
}
public MyObj GetObject(int id)
{
return DataStore.GetInstance().Get(id);
}
#endregion
}
}
With app.config containing
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="WindowsFormsApplication1.SimpleService">
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SimpleServiceBehavior">
<serviceMetadata httpGetEnabled="True" policyVersion="Policy15" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
I could then use WCF Test Client on the url http://localhost:8001/MetadataSample
The main issue I suffered from was my Service starting automatically, this can be disabled in VS2010 by a project setting. And the other issue was UAC, given Visual studio was not set to be a administrator the debugger failed to host a service, this was fixed by adding a WindowsFormApplication1.MANIFEST file containing
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">”
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">”
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
You have created a WCF Web Service project, which will be running inside the web service process (normally IIS), not inside your windows forms process and so it won't have any access to any data in static classes and properties in the Windows Forms process.
It sounds like your simplest option is to host the WCF service inside your windows forms application instead. I don't want to go into too much detail on how to do this as there are a number of resources already available on the web (also I can hardly claim to be an expert!), but you might want to try the following article as a starting point:
Hosting WCF services in a Windows Forms Application
WCF services are deployed in the same assembly as the rest of the application and should be able to access any classes in it. Perhaps you have used a different namespace. If this is the case, use a fully qualified name or a using statement.

Invoke a SOAP method with namespace prefixes

My C# web service client sends following soap message to Java-based web service:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<getData>
<request>
<requestParameters xmlns="http://b...">
<equals>
...
</equals>
</requestParameters>
</request>
</getData>
</soap:Body>
</soap:Envelope>
and Java-based web service returns error:
500 Internal Server Error
...
Cannot find dispatch method for {}getData
...
Client written in Java, which works, sends the following message:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ns2:getData xmlns:ns2="http://a...">
<ns2:request>
<ns3:requestParameters xmlns:ns3="http://b...">
<ns3:equals>
...
</ns3:equals>
</ns3:requestParameters>
</ns2:request>
</ns2:getData>
</soap:Body>
</soap:Envelope>
Is there an easy way in C# to send SOAP messages the same way Java client sends: with namespace prefixes?
Following is C# code that sends message:
// class MyService is auto-generated using wsdl.exe tool
MyService service = new MyService();
RequestMessage request = new RequestMessage();
...
ResponseMessage response = service.getData(request);
...
UPDATE:
Here is RequestMessage class:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://uri.etsi.org/02657/v1.5.1#/RetainedData")]
public partial class RequestMessage
{
private byte[] requestPriorityField;
private RequestConstraints requestParametersField;
private string deliveryPointHIBField;
private string maxHitsField;
private NationalRequestParameters nationalRequestParametersField;
private System.Xml.XmlElement anyField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType="hexBinary", Order=0)]
public byte[] requestPriority
{
get
{
return this.requestPriorityField;
}
set
{
this.requestPriorityField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=1)]
public RequestConstraints requestParameters
{
get
{
return this.requestParametersField;
}
set
{
this.requestParametersField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=2)]
public string deliveryPointHIB
{
get
{
return this.deliveryPointHIBField;
}
set
{
this.deliveryPointHIBField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType="integer", Order=3)]
public string maxHits
{
get
{
return this.maxHitsField;
}
set
{
this.maxHitsField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=4)]
public NationalRequestParameters nationalRequestParameters
{
get
{
return this.nationalRequestParametersField;
}
set
{
this.nationalRequestParametersField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAnyElementAttribute(Order=5)]
public System.Xml.XmlElement Any
{
get
{
return this.anyField;
}
set
{
this.anyField = value;
}
}
}
UPDATE #2:
The reason why Java-based web service didn't like my C# client produced SOAP message is not omission of namespace prefixes, but only because of omission of xmlns in getData element, so if my message looks like this:
...
<getData xmlns="http://a...">
...
</getData>
...
it works!
I managed to put xmlns inside getData by manually editing SoapRpcMethodAttribute in wsdl.exe-produced source code. Here is excerpt:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.3038")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(
Name="AxxxPortTypeBinding", Namespace="http://a...")]
public partial class AxxxService
: System.Web.Services.Protocols.SoapHttpClientProtocol {
...
/// <remarks/>
[System.Web.Services.Protocols.SoapRpcMethodAttribute(
"http://a.../getData",
RequestNamespace = "http://a...",
ResponseNamespace = "http://a...",
Use = System.Web.Services.Description.SoapBindingUse.Literal)]
[return: System.Xml.Serialization.XmlElementAttribute("response")]
public ResponseMessage getData(RequestMessage request) {
object[] results = this.Invoke("getData", new object[] {
request});
return ((ResponseMessage)(results[0]));
}
...
}
Before my change, SoapRpcMethodAttribute had following constructor:
[System.Web.Services.Protocols.SoapRpcMethodAttribute(
"", RequestNamespace = "", ResponseNamespace = "",
Use = System.Web.Services.Description.SoapBindingUse.Literal)]
Now, the question is: what to put in WSDL file so that SoapRpcMethodAttribute have those strings in constructor (filled by the wsdl.exe tool) in the first place?
I've seen this problem before where the WSDL.exe tool did not properly pull in the namespace when generating the service code. Check the request object definition in your generated code. My guess is that there is no XmlRootAttribute attribute defined on the class definition for the request object.
Adding the attribute [XmlRootAttribute(Namespace "http://a...")] to the class definition for the request object should fix this issue.
As a side note, I recommend adding this additional attribute in a separate code file using a partial class definition. Defining the attribute in a separate file will allow you to regenerate the web service code using WSDL.exe whenever neccessary without overwriting the fix to set the root element's namespace properly.
The generated proxy class should have had the correct namespace in it. I recommend two things:
1) Run WSDL.EXE from the command line and note whether there are any errors or warnings. If so, please edit your question to include them.
2) Unless you are stuck using .NET 2.0, you should try to create your proxy classes using either "Add Service Reference" or the equivalent "SVCUTIL.EXE" these will use the modern, WCF infrastructure on the client side, which is much more likely to have been fixed to resolve this problem.
I had the same issue and solved it changing the value of the property Use in System.Web.Services.Protocols.SoapDocumentMethodAttribute attribute corrisponding to each web service method.
The System.Web.Services.Description.SoapBindingUse.Literal default value was replaced with System.Web.Services.Description.SoapBindingUse.Encoded.

Categories