NuSoap Server Works with SoapUI, but not C#.NET 3.5 - c#
I'm setting up a web service to accept orders from a third party, and I've been having some troubles some odd errors. I'm running CodeIgniter with a NuSoap server handling the request. They're running C# .NET 3.5 to send the request. Every time they submit the request through their app, it returns an error: "Object Reference not set to an Instance of an object."
I asked them to send me the SOAP request and response, and here they are (unfortunately it's proprietary, so I can't provide all the info and I've changed some names)
<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://InspectionService/SendInspectionRequestTPA</Action>
<h:AuthenticateHeader xmlns="http://InspectionService/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:h="http://InspectionService/">
<UserName>USERNAME</UserName>
<Password>PASSWORD</Password>
</h:AuthenticateHeader>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SendInspectionRequestTPA xmlns="http://InspectionService/">
<RequesterName>Bob</RequesterName>
<RequesterExt>5555555555</RequesterExt>
<RequesterEmail>email#test.net</RequesterEmail>
<SaleDate>2013-08-04T00:00:00</SaleDate>
<VehicleYear>2010</VehicleYear>
<VehicleMake>MITSUBISHI</VehicleMake>
<VehicleModel>GALANT ES/SE</VehicleModel>
<Mileage>60000</Mileage>
<VIN>12341234123412341</VIN>
<ContractNumber>SEP00001025NVSC</ContractNumber>
<ClaimNumber>C000001061</ClaimNumber>
<InspectionType>Automotive</InspectionType>
<InspectionReason>
<anyType xsi:type="xsd:string">RIGHT FRONT WINDOW IS NOT WORKING</anyType>
<anyType xsi:type="xsd:string">MOTOR SHORTED</anyType>
<anyType xsi:type="xsd:string">REPLACE RIGHT FRONT WINDOW MOTOR</anyType>
<anyType xsi:type="xsd:string">THIS IS A TEST INSPECTION.</anyType>
</InspectionReason>
<RepairSite>CLAIM DEMO DEALER-SEP 2</RepairSite>
<Address1>ADDR</Address1>
<Address2 />
<City>OMAHA</City>
<State>NE</State>
<Zip>68144</Zip>
<Contact>JOE SERVICER</Contact>
</SendInspectionRequestTPA>
</s:Body>
</s:Envelope>
(Note that I removed some sensitive lines in the request and changed others)
And the response:
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>... stream ...</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
(Edit: This was part of our issue, in that this debug response was truncated by the company--so we never got the actual debug info from their side)
The thing here is that this request works perfectly (read: as expected) when submitted through SOAPUI. Here's the code from my CodeIgniter Controller:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Carwash extends CI_Controller
{
public function __construct()
{
parent::__construct();
error_reporting(0);
ini_set('display_errors','Off');
$ns = 'http://'.$_SERVER['HTTP_HOST'].'/carwash';
$ep = 'http://'.$_SERVER['HTTP_HOST'].'/carwash';
$this->ns = $ns;
$this->load->library("Nusoap_library"); // load nusoap toolkit library in controller
$this->nusoap_server = new soap_server(); // create soap server object
$this->nusoap_server->configureWSDL("EACCarwash", $this->ns, $ep); //, "rpc", 'http://schemas.xmlsoap.org/soap/http', array(),); // wsdl configuration
$this->nusoap_server->wsdl->schemaTargetNamespace = $ns; // server namespace
$this->nusoap_server->namespaces['soap'] ='http://schemas.xmlsoap.org/soap/encoding/';
$this->load->model('carwash_model');
}
public function index()
{
$fi_input = array(
"RequesterName" => "xsd:string",
"RequesterExt" => "xsd:string",
"RequesterEmail" => "xsd:string",
"ContractHolder" => "xsd:string",
"SaleDate" => "xsd:dateTime",
"SaleOdometer" => "xsd:string",
"VehicleYear" => "xsd:string",
"VehicleMake" => "xsd:string",
"VehicleModel" => "xsd:string",
"Mileage" => "xsd:string",
"VIN" => "xsd:string",
"ContractNumber" => "xsd:string",
"ClaimNumber" => "xsd:string",
"InspectionType" => "xsd:string",
"InspectionReason" => "xsd:struct",
"RepairSite" => "xsd:string",
"Address1" => "xsd:string",
"Address2" => "xsd:string",
"City" => "xsd:string",
"State" => "xsd:string",
"Zip" => "xsd:string",
"Phone" => "xsd:string",
"Contact" => "xsd:string",
"TpaCode" => "xsd:string"
);
$fi_return = array(
"SendInspectionRequestTPAResult" => "xsd:string"
);
$this->nusoap_server->register('SendInspectionRequestTPA',
$fi_input,
$fi_return,
"urn:SOAPServerWSDL",
"urn:".$this->ns."/SendInspectionRequestTPA",
"rpc",
"encoded",
"DESCRIPTION HERE",
"http://schemas.xmlsoap.org/soap/encoding/");
function SendInspectionRequestTPA($req_name, $req_ext, $req_email, $contract_holder, $sale_date, $sale_odometer, $v_year, $v_make, $v_model, $mileage, $vin, $contract_no, $claim_no, $insp_type, $insp_reason, $repair_site, $addr1, $addr2, $city, $state, $zip, $phone, $contact_name, $tpa_code)
{
$CI =& get_instance();
if($CI->check_creds() == FALSE)
{
return new soap_fault('Client','','Not authorized');
}
else
{
if(!$req_name){ return new soap_fault('Client','','Requester Name Required');}
if(!$sale_date){ return new soap_fault('Client','','Sale Date Required');}
if(!$v_year){ return new soap_fault('Client','','Vehicle Year Required');}
if(!$v_make){ return new soap_fault('Client','','Vehicle Make Required');}
if(!$v_model){ return new soap_fault('Client','','Vehicle Model Required');}
if(!$mileage){ return new soap_fault('Client','','Mileage Required');}
if(!$contract_no){ return new soap_fault('Client','','Contract Number Required');}
if(!$claim_no){ return new soap_fault('Client','','Claim Number Required');}
if(!$insp_type){ return new soap_fault('Client','','Inspection Type Required');}
if(!$insp_reason){ return new soap_fault('Client','','Inspection Reason Required');}
if(!$repair_site){ return new soap_fault('Client','','Repair Site Required');}
if(!$contact_name){ return new soap_fault('Client','','Contact Required');}
if(!$tpa_code){ return new soap_fault('Client','','TPA Code Required');}
$id = $CI->carwash_model->add_fni_insp($req_name, $req_ext, $req_email, $contract_holder, $sale_date, $sale_odometer, $v_year, $v_make, $v_model, $mileage, $vin, $contract_no, $claim_no, $insp_type, $insp_reason, $repair_site, $addr1, $addr2, $city, $state, $zip, $phone, $contact_name, $tpa_code) or die(new soap_fault('Server','','Error Processing Request'));
return (string) ((int) $id);
//else{return new soap_fault('Server', '','Error processing request');}
}
}
$this->soapy = file_get_contents("php://input");
$this->nusoap_server->service($this->soapy);
}
public function hookTextBetweenTags($string, $tagname) {
$pattern = "/<$tagname ?.*>(.*)<\/$tagname>/i";
preg_match($pattern, $string, $matches);
return $matches[1];
}
public function check_creds()
{
if(isset($this->soapy))
{
$sUsername = $this->hookTextBetweenTags($this->soapy, 'Username');
$sPassword = $this->hookTextBetweenTags($this->soapy, 'Password');
$verify = $this->carwash_model->check_fni_user($sUsername, $sPassword);
//var_dump($verify);
return $verify;
}
}
}//end class
I will confess that I'm relatively new with setting up SOAP Servers, so if something's really stupid or out of place, I would really love to hear about that, too.
When I googled the "Object Reference not set to an instance of an object" error, it seems that essentially the server was expecting an object and didn't get one. The only place that would happen is the "inspectionreason" field, which is well-formed in their request. That's pretty much the only thing that I could pinpoint as to where that error comes from. But since it works with SOAPUI, I'm inclined to think that the error is related to the request--and not the server.
Does anyone see any glaring errors? Or is there something with NuSoap that I should be configuring?
Let me know if I can provide any more details. Thanks!
Related
Twilio c# TwiML not dialing number. Saying it instead
I'm setting up masked calling. When I get the following TwiML response after calling the masked number, it isn't dialing the number that I'm specifying. It's just saying the number instead after saying the content of the say Here is the TwiML <?xml version="1.0" encoding="utf-8"?> <Response> <Say>Your call will be charged blah blah.</Say> <Dial action="http://mywebsite.com/Call/CallComplete" callerId="+441XXXXX"> <Number>+44795XXXXX</Number> </Dial> </Response> And here is the c# public static string TwiMLDial(string maskedNumber, string to, string actionURL) { var response = new Twilio.TwiML.VoiceResponse(); response.Say("Your call will be charged blah blah."); var dial = new Twilio.TwiML.Dial(action: actionURL, callerId: maskedNumber); dial.Number(to); response.Dial(dial); return response.ToString(); } I'm using c# .Net core. And have the following in my startup.cs which may be relevant: services.AddMvc(config => { // Add XML Content Negotiation config.RespectBrowserAcceptHeader = true; config.InputFormatters.Add(new XmlSerializerInputFormatter()); config.OutputFormatters.Add(new XmlSerializerOutputFormatter()); }) .AddJsonOptions(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver(); options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });
Twilio developer evangelist here. The issue is that your endpoint is returning the response with the type text/plain and Twilio takes that to mean, just read this out. You need to set your response Content-Type to text/xml or application/xml. I'm not a C# developer I'm afraid, but hopefully that points you in the right direction.
Non-wsdl SOAP request in PHP
I need to post SOAP request to some server. I know exactly that the right example of SOAP request as follows: <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <CreateOrderBroker xmlns="http://tempuri.org/"> <shortApp xmlns:a="http://schemas.datacontract.org/2004/07/ScroogeCbformsService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:PIB>John Doe</a:PIB> <a:agreeId>3155</a:agreeId> <a:formId>55</a:formId> <a:stateCode>1234567890</a:stateCode> <a:telephone>1511528945</a:telephone> </shortApp> </CreateOrderBroker> </s:Body> </s:Envelope> Also I have working C# example: public partial class frmMain : Form { public frmMain() { InitializeComponent(); } public EndpointAddress EndPointAddr { get { return new EndpointAddress("https://194.126.180.186:77/ScroogeCbForms.svc?wsdl"); } } private void btnSend_Click(object sender, EventArgs e) { ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(IgnoreCertificateErrorHandler); ServicePointManager.Expect100Continue = false; ServiceICreditTest.CreateOrderResponse response = new CreateOrderResponse(); ScroogeSiteGist client = new ScroogeSiteGist(Binding(), EndPointAddr); shortApplicationBroker shortAp = new shortApplicationBroker() { agreeId = 3155, PIB = "John Doe", stateCode = "1234567890", formId = 55, telephone = "1511528945" }; //response = client.CreateOrder("1012021013"); response = client.CreateOrderBroker(shortAp); txtText.Text = string.Format("id = {0} ErrorId = {1}", response.OrderId, response.ReturnValue); } } I'm trying to make same code in PHP 5.3: <?php $client = new SoapClient("https://194.126.180.186:77/ScroogeCbForms.svc?wsdl", array('soap_version' => SOAP_1_1, 'trace' => 1)); $params = array( 'agreeId' => 3155, 'PIB' => 'John Doe', 'stateCode' => '3289013768', 'formId' => 55, 'telephone' => '0661254877' ); $client->CreateOrderBroker($params); But request and callback from this code is next: <?php ... echo "REQUEST:<pre>".htmlspecialchars($client->__getLastRequest()) ."</pre>"; echo "CALLBACK:<pre>".htmlspecialchars($client->__getLastResponse())."</pre>"; REQUEST: <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"> <SOAP-ENV:Body><ns1:CreateOrderBroker/> </SOAP-ENV:Body> </SOAP-ENV:Envelope> CALLBACK: <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body><CreateOrderBrokerResponse xmlns="http://tempuri.org/"><CreateOrderBrokerResult xmlns:a="http://schemas.datacontract.org/2004/07/ScroogeCbformsService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:OrderId>0</a:OrderId> <a:ReturnValue>Object reference not set to an instance of an object.</a:ReturnValue> </CreateOrderBrokerResult> </CreateOrderBrokerResponse> </s:Body> </s:Envelope> It seems that body of request is empty. What does it mean? If call made in wsdl-mode and request body is empty then wsdl-schema is broken, right? If wsdl is broken what is the way to construct initial right SOAP request manually? Can anyone give an example? Moreover, the data given in initial right SOAP request is enough to construct this request manually? Or I need some extra (namespaces, etc.)
Try the following code: $client = new SoapClient("https://194.126.180.186:77/ScroogeCbForms.svc?wsdl", array('soap_version' => SOAP_1_1, 'trace' => 1)); class shortApp { function __construct() { $this->agreeId = 3155; $this->PIB = 'John Doe'; $this->stateCode = '3289013768'; $this->formId = 55; $this->telephone = '0661254877'; } } $sa = new shortApp(); $shortApp = new SoapVar($sa, SOAP_ENC_OBJECT, 'shortApp', 'http://soapinterop.org/xsd'); $response = $client->CreateOrderBroker(new SoapParam($shortApp, 'shortApp')); This code should give you the following request: <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="http://soapinterop.org/xsd" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <ns1:CreateOrderBroker> <shortApp xsi:type="ns2:shortApp"> <agreeId xsi:type="xsd:int">3155</agreeId> <PIB xsi:type="xsd:string">John Doe</PIB> <stateCode xsi:type="xsd:string">3289013768</stateCode> <formId xsi:type="xsd:int">55</formId> <telephone xsi:type="xsd:string">0661254877</telephone> </shortApp> </ns1:CreateOrderBroker> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
How to change WebService code behind class dynamically?
I'm facing an issue with different versions of a Web Service. Because there are several versions of the Web service, sometimes the parameters are changed and/or WebMethods are added and removed. I want to have a single asmx file, but depending on the client installation (the version they are running), be able to change the code behind of the asmx at runtime. Instead of having different asmx per version, just have one asmx file that dynamically can load the code behind with the accurate version. In this case I do have a V1Methods.cs, V2Methods.cs, V10Methods.cs <%# WebService Language="C#" Class="DynamicClass" %> If the customer is running Version2, the asmx code behind class should be V2Methods.cs and so on. Is it possible?
In short no, that is not possible. I was going to suggest using the webservice as a facade but by the sounds of it the method signatures on each version are different, which would make that more difficult. If the client application is dependent on a particular version of your webservice, can't you just deploy all versions of your service with different names (i.e. servicev1.asmx, servicev2.asmx etc), and add some config to your client to tell it which one to call ?
OK - I have a possible solution for you that is not award-winning for elegance but I've just tested it and it works. You can expose one WebMethod that returns object and takes a params object[] parameter, allowing you to pass whatever you like to it (or nothing) and return whatever you want. This compiles to legal WSDL using the 'anyType' type. If you can identify which actual method to call based on the number and datatype of parameters passed to this method, you can call the appropriate method and return whatever value you want. The service: - [WebMethod] public object Method(params object[] parameters) { object returnValue = null; if (parameters != null && parameters.Length != 0) { if (parameters[0].GetType() == typeof(string) && parameters[1].GetType() == typeof(int)) { return new ServiceImplementation().StringIntMethod(parameters[0].ToString(), Convert.ToInt32(parameters[1])); } else if (parameters[0].GetType() == typeof(string) && parameters[1].GetType() == typeof(string)) { return new ServiceImplementation2().StringStringMethod(parameters[0].ToString(), parameters[1].ToString()); } } return returnValue; } My test service implementation classes: - public class ServiceImplementation { public string StringIntMethod(string someString, int someInt) { return "StringIntMethod called"; } } public class ServiceImplementation2 { public float StringStringMethod(string someString, string someOtherString) { return 3.14159265F; } } An example of use: - var service = new MyTestThing.MyService.WebService1(); object test1 = service.Method(new object[] { "hello", 3 }); Console.WriteLine(test1.ToString()); object test2 = service.Method(new object[] { "hello", "there" }); Console.WriteLine(test2.ToString()); I've tested this and it works. If you're interested, the WSDL that "Method" generates: - POST /test/WebService1.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://tempuri.org/Method" <?xml version="1.0" encoding="utf-8"?> <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> <Method xmlns="http://tempuri.org/"> <parameters> <anyType /> <anyType /> </parameters> </Method> </soap:Body> </soap:Envelope> Incase you're wondering, yes I am bored at work and I'm in the mood for helping people :)
Can I remove the default namespace when creating raw Soap XML?
I'm trying to send a soap request to a WCF service. I am building the soap request using the System.ServiceModel.Channels.Message.CreateMessage() method. I haven't gotten super deep into building the body, but here is what I have... Message msg = Message.CreateMessage( MessageVersion.Soap11WSAddressing10, "MethodName" ); msg.Headers.MessageId = new UniqueId( Guid.NewGuid().ToString() ); msg.Headers.Add( Message.CreateHeader( "Security", "", new Security() { TimeStamp = new TimeStampType() { Created = DateTime.Now, Expires = Created.AddDays( 1 ) }, UsernameToken = new UsernameToken() { Username = "stackoverflow", Password = new Password() { Type = "hashed", Value = "Password" } } } ) ) ); string s = msg.ToString(); When I run this, I get the following output. I'm using the Visual Studio XML Visualizer btw. <s:Envelope> <s:Header> <Action>MethodName</Action> <MessageID>GUIDVALUE</MessageID> <Security> <Timestamp xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1"> ..... </s:Header> <s:Body /> </s:Envelope> My question is, can I remove xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1" from the xml? It shows up in Timestamp and in UsernameToken. Thanks
set Namespace to empty in datacontract in a class that wrap your security and timestamp [DataContract(Namespace = "")]
Post byte array from PHP to .NET WCF Service
I got a WCF service with a method to receive files, looking something like this public bool UploadFile(string fileName, byte[] data) { //... } What I'd like to do is to post the data to this method in the WCF service from PHP, but are unaware if it's even possible to post byte arrays from PHP to a .NET method hosted by a WCF service. So I was thinking of something like this $file = file_get_contents($_FILES['Filedata']['tmp_name']); // get the file content $client = new SoapClient('http://localhost:8000/service?wsdl'); $params = array( 'fileName' => 'whatever', 'data' => $file ); $client->UploadFile($params); Would this be possible or are there any general recommendations out there I should know about?
Figured it out. The official php documentation tells that the file_get_contents returns the entire file as a string (http://php.net/manual/en/function.file-get-contents.php). What noone tells is that this string is compatible with the .NET bytearray when posted to a WCF service. See example below. $filename = $_FILES["file"]["name"]; $byteArr = file_get_contents($_FILES['file']['tmp_name']); try { $wsdloptions = array( 'soap_version' => constant('WSDL_SOAP_VERSION'), 'exceptions' => constant('WSDL_EXCEPTIONS'), 'trace' => constant('WSDL_TRACE') ); $client = new SoapClient(constant('DEFAULT_WSDL'), $wsdloptions); $args = array( 'file' => $filename, 'data' => $byteArr ); $uploadFile = $client->UploadFile($args)->UploadFileResult; if($uploadFile == 1) { echo "<h3>Success!</h3>"; echo "<p>SharePoint received your file!</p>"; } else { echo "<h3>Darn!</h3>"; echo "<p>SharePoint could not receive your file.</p>"; } } catch (Exception $exc) { echo "<h3>Oh darn, something failed!</h3>"; echo "<p>$exc->getTraceAsString()</p>"; } Cheers!