I am new to webservices. I'm trying to call one this way just to see the result:
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show(getServiceResult("http://prod.sivaonline.pt/SAG.WS.SIVA.SVOLB2C/ViaturasNovas.asmx?wsdl"));
}
public string getServiceResult(string serviceUrl)
{
HttpWebRequest HttpWReq;
HttpWebResponse HttpWResp;
HttpWReq = (HttpWebRequest)WebRequest.Create(serviceUrl);
HttpWReq.Method = "GetMarcas";
HttpWResp = (HttpWebResponse)HttpWReq.GetResponse();
if (HttpWResp.StatusCode == HttpStatusCode.OK)
{
//Consume webservice with basic XML reading, assumes it returns (one) string
XmlReader reader = XmlReader.Create(HttpWResp.GetResponseStream());
while (reader.Read())
{
reader.MoveToFirstAttribute();
if (reader.NodeType == XmlNodeType.Text)
{
return reader.Value;
}
}
return String.Empty;
}
else
{
throw new Exception("Error on remote IP to Country service: " + HttpWResp.StatusCode.ToString());
}
}
Now, it doesn't give me any message box. Is this normal? I want to add some parameters, like:
configurador=true
Visual Studio makes it easy to call web services by creating proxy classes for them on the client side. You create an object of the proxy class and calls its respective methods, which are internally converted into SOAP calls by the framework. Simply right-click on your project and use Add Service Reference instead of using HttpWebRequest.
Related
I have some data that needs to be send in SOAP format to a server. This server will immediately acknowledge that it received the messages. After a few hours I get (possibly from another server) a SOAP message that contains information about the processed data.
I read Stackoverflow: How to send SOAP request and receive response. However, the answers are 8 years old. Although they may still work, It may be that there are newer techniques.
And indeed it seems: Microsoft has System.Web.Services.Protocols, with classes like SoapMessage, SoapClientMessage, SoapServerMessage, etc.
Looking at the classes I find a lot of SOAP like classes (headers, extensions, client messages, server messages... Normally the provided examples give me an indication to how these classes work together and how to use them. In the MSDN documents I can only find examples of how to process already existing SOAP messages.
Given some data that needs to be sent, how can I wrap this data somehow in one of these SOAP classes and send this message?
Are these classes meant for this purpose? Or should I stick to the 2011 method where you'd create a SOAP Web request by formatting the XML data in soap format yourself, as the above mentioned Stackoverflow question suggests?
I'm awfully sorry, normally I would write things I have tried. Alas I don't see the relation between the provided SoapMessage classes. I haven't got a clue how to use them.
Addition after comments
I'm using windows server / visual studio (newest versions) / .NET (newest versions) / C# (newest versions).
The communication with the server is mutual authenticated. The certificate that I need to use to communicate with the server, is in PEM (CER / CRT) format. The privated key is RSA. This certificate is issued by a proper CA, the server will also use certificates used by a proper CA. So I don't need to create a new certificate (in fact, it won't be accepted). If needed, I'm willing to convert the certificates using programs like OpenSsl and the like.
I've tried to use Apache TomCat to communicate, but I have the feeling that that's way too much for the task of sending one SOAP message per day and waiting for one answer per day.
Maybe because java is a complete new technique for me, it was difficult for me to see the contents of the received messages. So back to C# and .NET.
I was planning to create a DLL, to be used by a console app. The function would have some data in a stream as input. It would create the soap message, send it, wait for reply that the message was received correctly, and wait (possible several hours) for a new Soap message containing the results of the processed data. To make proper reporting, and cancellation possible, I guess it is best to do this using async-await
If sending the order and waiting for the result can't be done in one application, I'm willing to create a windows service that that listens to the input, but I prefer to keep it simple.
The (virtual) computer will only be used for this task, so no one else will need to listen to port 443. There will be one order message send per day, and one result message per day.
Here is sample C# Console client and server code (they are in the same sample but this is only for demo purpose, of course) that uses HTTPS.
For the client side, we reuse the SoapHttpClientProtocol class, but for the server side, unfortunately, we cannot reuse anything because classes are completely tied to ASP.NET's (IIS) HttpContext class
For the server side, we use HttpListener, so, depending on your configuration, the server side will probably require admin rights to be able to call HttpListener's Prefixes.Add(url).
The code doesn't uses client certificate, but you can add this where I placed // TODO comments
The code assumes there is a certificate associated with the url and port used. If there's not (use netsh http show sslcert to dump all associated certs), you can use the procedure described here to add one: https://stackoverflow.com/a/11457719/403671
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
namespace SoapTests
{
class Program
{
static void Main(string[] args)
{
// code presumes there is an sslcert associated with the url/port below
var url = "https://127.0.0.1:443/";
using (var server = new MyServer(url, MyClient.NamespaceUri))
{
server.Start(); // requests will occur on other threads
using (var client = new MyClient())
{
client.Url = url;
Console.WriteLine(client.SendTextAsync("hello world").Result);
}
}
}
}
[WebServiceBinding(Namespace = NamespaceUri)]
public class MyClient : SoapHttpClientProtocol
{
public const string NamespaceUri = "http://myclient.org/";
public async Task<string> SendTextAsync(string text)
{
// TODO: add client certificates using this.ClientCertificates property
var result = await InvokeAsync(nameof(SendText), new object[] { text }).ConfigureAwait(false);
return result?[0]?.ToString();
}
// using this method is not recommended, as async is preferred
// but we need it with this attribute to make underlying implementation happy
[SoapDocumentMethod]
public string SendText(string text) => SendTextAsync(text).Result;
// this is the new Task-based async model (TAP) wrapping the old Async programming model (APM)
public Task<object[]> InvokeAsync(string methodName, object[] input, object state = null)
{
if (methodName == null)
throw new ArgumentNullException(nameof(methodName));
return Task<object[]>.Factory.FromAsync(
beginMethod: (i, c, o) => BeginInvoke(methodName, i, c, o),
endMethod: EndInvoke,
arg1: input,
state: state);
}
}
// server implementation
public class MyServer : TinySoapServer
{
public MyServer(string url, string namespaceUri)
: base(url)
{
if (namespaceUri == null)
throw new ArgumentNullException(nameof(namespaceUri));
NamespaceUri = namespaceUri;
}
// must be same as client namespace in attribute
public override string NamespaceUri { get; }
protected override bool HandleSoapMethod(XmlDocument outputDocument, XmlElement requestMethodElement, XmlElement responseMethodElement)
{
switch (requestMethodElement.LocalName)
{
case "SendText":
// get the input
var text = requestMethodElement["text", NamespaceUri]?.InnerText;
text += " from server";
AddSoapResult(outputDocument, requestMethodElement, responseMethodElement, text);
return true;
}
return false;
}
}
// simple generic SOAP server
public abstract class TinySoapServer : IDisposable
{
private readonly HttpListener _listener;
protected TinySoapServer(string url)
{
if (url == null)
throw new ArgumentNullException(nameof(url));
_listener = new HttpListener();
_listener.Prefixes.Add(url); // this requires some rights if not used on localhost
}
public abstract string NamespaceUri { get; }
protected abstract bool HandleSoapMethod(XmlDocument outputDocument, XmlElement requestMethodElement, XmlElement responseMethodElement);
public async void Start()
{
_listener.Start();
do
{
var ctx = await _listener.GetContextAsync().ConfigureAwait(false);
ProcessRequest(ctx);
}
while (true);
}
protected virtual void ProcessRequest(HttpListenerContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
// TODO: add a call to context.Request.GetClientCertificate() to validate client cert
using (var stream = context.Response.OutputStream)
{
ProcessSoapRequest(context, stream);
}
}
protected virtual void AddSoapResult(XmlDocument outputDocument, XmlElement requestMethodElement, XmlElement responseMethodElement, string innerText)
{
if (outputDocument == null)
throw new ArgumentNullException(nameof(outputDocument));
if (requestMethodElement == null)
throw new ArgumentNullException(nameof(requestMethodElement));
if (responseMethodElement == null)
throw new ArgumentNullException(nameof(responseMethodElement));
var result = outputDocument.CreateElement(requestMethodElement.LocalName + "Result", NamespaceUri);
responseMethodElement.AppendChild(result);
result.InnerText = innerText ?? string.Empty;
}
protected virtual void ProcessSoapRequest(HttpListenerContext context, Stream outputStream)
{
// parse input
var input = new XmlDocument();
input.Load(context.Request.InputStream);
var ns = new XmlNamespaceManager(new NameTable());
const string soapNsUri = "http://schemas.xmlsoap.org/soap/envelope/";
ns.AddNamespace("soap", soapNsUri);
ns.AddNamespace("x", NamespaceUri);
// prepare output
var output = new XmlDocument();
output.LoadXml("<Envelope xmlns='" + soapNsUri + "'><Body/></Envelope>");
var body = output.SelectSingleNode("//soap:Body", ns);
// get the method name, select the first node in our custom namespace
bool handled = false;
if (input.SelectSingleNode("//x:*", ns) is XmlElement requestElement)
{
var responseElement = output.CreateElement(requestElement.LocalName + "Response", NamespaceUri);
body.AppendChild(responseElement);
if (HandleSoapMethod(output, requestElement, responseElement))
{
context.Response.ContentType = "application/soap+xml; charset=utf-8";
context.Response.StatusCode = (int)HttpStatusCode.OK;
var writer = new XmlTextWriter(outputStream, Encoding.UTF8);
output.WriteTo(writer);
writer.Flush();
handled = true;
}
}
if (!handled)
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
}
public void Stop() => _listener.Stop();
public virtual void Dispose() => _listener.Close();
}
}
Personally, I use ServiceStack to create both client and server
https://docs.servicestack.net/soap-support
Or SoapHttpClient nuget
https://github.com/pmorelli92/SoapHttpClient
Or my example from way back when
Is it possible that I can convert simple string to SOAP Message and send it?
The answer depends on what framework or libraries do you plan to use?
The simplest modern answer is to declare a simple class that defines the structure of your message and then serialize it using HttpClient to send it.
However, SOAP is a standard built for description based messaging so the still relevant recommendation is to generate your client code from the wsdl description using a "service reference" then use the generated client object.
I would however recommend, like others have pointed out that you try to move to REST services instead (assuming this is possible).
The code is less complex, the system is far simpler to use and it's a global standard.
Here is a comparison and example of both ...
https://smartbear.com/blog/test-and-monitor/understanding-soap-and-rest-basics/
I am using Abot in a way that I have a WPF application which displays a browser control (CefSharp).
The user logs in and whichever possible custom authentication the site is using will work while crawling in the same way as if the user were actually browsing the site.
Thus, when I crawl, I want to use this browser control to make the request and simply return the page data.
Therefore I've implemented my custom PageRequester, complete listing below.
The problem is that with CefSharp, as with other browser controls, it's not possible to get the HttpWebRequest/Response associated with a CrawlPage.
Without setting these two properties, Abot does not proceed the crawl further.
Is there something I can do to circumvent this problem?
Code listing:
using Abot.Core;
using Abot.Poco;
using CefSharp.Wpf;
using System;
using System.Net;
using System.Text;
using System.Threading;
public class CefPageRequester : IPageRequester
{
private MainWindowDataContext DataContext;
private ChromiumWebBrowser ChromiumWebBrowser;
private CrawlConfiguration CrawlConfig;
private volatile bool _navigationCompleted;
private string _pageSource;
public CefPageRequester(MainWindowDataContext dataContext, ChromiumWebBrowser chromiumWebBrowser, CrawlConfiguration crawlConfig)
{
this.DataContext = dataContext;
this.ChromiumWebBrowser = chromiumWebBrowser;
this.CrawlConfig = crawlConfig;
this.ChromiumWebBrowser.FrameLoadEnd += ChromiumWebBrowser_FrameLoadEnd;
}
public CrawledPage MakeRequest(Uri uri)
{
return this.MakeRequest(uri, cp => new CrawlDecision() { Allow = true });
}
public CrawledPage MakeRequest(Uri uri, Func<CrawledPage, CrawlDecision> shouldDownloadContent)
{
if (uri == null)
throw new ArgumentNullException("uri");
CrawledPage crawledPage = new CrawledPage(uri);
try
{
//the browser control is bound to the address of the data context,
//if we set the address directly it breaks for some reason, although it's a two way binding.
this.DataContext.Address = uri.AbsolutePath;
crawledPage.RequestStarted = DateTime.Now;
crawledPage.DownloadContentStarted = crawledPage.RequestStarted;
while (!_navigationCompleted)
Thread.CurrentThread.Join(10);
}
catch (WebException e)
{
crawledPage.WebException = e;
}
catch
{
//bad luck, we should log this.
}
finally
{
//TODO must add these properties!!
//crawledPage.HttpWebRequest = request;
//crawledPage.HttpWebResponse = response;
crawledPage.RequestCompleted = DateTime.Now;
crawledPage.DownloadContentCompleted = crawledPage.RequestCompleted;
if (!String.IsNullOrWhiteSpace(_pageSource))
crawledPage.Content = this.GetContent("UTF-8", _pageSource);
_navigationCompleted = false;
_pageSource = null;
}
return crawledPage;
}
private void ChromiumWebBrowser_FrameLoadEnd(object sender, CefSharp.FrameLoadEndEventArgs e)
{
if (!e.IsMainFrame)
return;
this.ChromiumWebBrowser.Dispatcher.BeginInvoke(
(Action)(() =>
{
_pageSource = this.ChromiumWebBrowser.GetSourceAsync().Result;
_navigationCompleted = true;
}));
}
private PageContent GetContent(string charset, string html)
{
PageContent pageContent = new PageContent();
pageContent.Charset = charset;
pageContent.Encoding = this.GetEncoding(charset);
pageContent.Text = html;
pageContent.Bytes = pageContent.Encoding.GetBytes(html);
return pageContent;
}
private Encoding GetEncoding(string charset)
{
Encoding e = Encoding.UTF8;
if (charset != null)
{
try
{
e = Encoding.GetEncoding(charset);
}
catch { }
}
return e;
}
}
The question can also be phrased as: how to avoid having to create a HttpWebResponse from a stream? Which seems impossible, given MSDN says:
You should never directly create an instance of the HttpWebResponse
class. Instead, use the instance returned by a call to
HttpWebRequest.GetResponse.
I would have to actually post the request to get the response, which is precisely what I want to avoid by having a web browser control.
As you are aware, lots of functionality depends on the HttpWebRequest and HttpWebResponse being set. I've ordered a few options for you off the top of my head...
1) Refactor Abot to use some POCO Abstraction instead of those classes. Then just have an converter that converts the real HttpWebRequest and HttpWebResponse to those POCO types as well as a converter that converts your browser objects response into those POCOs.
2) Create a CustomHttpWebRequest and CustomHttpWebResponse that inherit from the .net classes so you can access/override the public/protected properties which may allow you to manually create an instance that models the request/response that your browser component returns to you. I know this can be tricky but may work (I've never done it so I can't say for sure).
3) [I HATE THIS IDEA. It SHOULD BE YOUR LAST RESORT] Create a real instance of these classes and use reflection to set whatever properties/values need to be set to satisfy all of Abot's usages.
4) [I HATE THIS IDEA EVEN WORSE] Use MS Fakes to create shims/stubs/fakes to the properties and methods of the HttpWebRequest and HttpWebResponse. Then you could configure it to return your values. This tool is usually only used for testing but I believe it can be used for production code if you are desperate, don't care about performance and/or are insane.
I also included the terrible ideas as well to just in case they help you spark some thought. Hope that helps...
Dears,
I know that the title seems popular and easy but what I'm facing is too strange.
Simply, I have a RESTful C# .NET 4.0 web service published on the server, and I want to consume it through Java in my Android application, easy?
The problem is: whenever I call the .NET web service and get the response, java could not parse the return string into Json.
Error message:
org.json.JSONException: Value {lhs:"1 Euro",rhs: "1.3711 U.S. dollars",error: "",icc: true} of type java.lang.String cannot be converted to JSONObject
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONObject.<init>(JSONObject.java:158)
at org.json.JSONObject.<init>(JSONObject.java:171)
at com.itrack21.mobileapp.LoginActivity.getUserInfo(LoginActivity.java:151)
at com.itrack21.mobileapp.LoginActivity$1$1.run(LoginActivity.java:114)
at java.lang.Thread.run(Thread.java:841)
threadid=17: thread exiting with uncaught exception (group=0x4164d700)
Java Code:
public void getRate(String link) {
try {
URL url = new URL(link);
URLConnection connection = url.openConnection();
InputStream str = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(str);
BufferedReader bufReader = new BufferedReader(reader);
String line=new String();
StringBuffer buffer = new StringBuffer();
while( (line=bufReader.readLine()) != null) {
buffer.append(line);
}
JSONObject obj = null;
try {
obj = new JSONObject(buffer.toString());
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String rhs = "";
try {
rhs = obj.getString("rhs");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.i("getRate","Converted Currency = " + rhs.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Debugging:
I did many debugging scenarios but with no luck, here I will use strangest one to describe the problem.
I use my java code to consume Google web service for currency converting using the following link http://www.google.com/ig/calculator?hl=en&q=1USD=?EUR and I got the response as follows: {lhs: "1 U.S. dollar",rhs: "0.726321906 Euros",error: "",icc: true}. And here my code is working perfectly.
I copy exact Json value return from Google web service and working fine with my code, and return it as a HARD CODED String in my RESTful C# service as follows:
C# Code:
public string GetInfo()
{
return "{lhs:\"1 Euro\",rhs: \"1.3711 U.S. dollars\",error: \"\",icc: true}";
}
I request my C# .NET web service using the same code used to invoke Google web service bu I got error listed above org.json.JSONException: Value {lhs:"1 Euro",rhs: "1.3711 U.S. dollars",error: "",icc: true} of type java.lang.String cannot be converted to JSONObject at org.json.JSON.typeMismatch(JSON.java:111).
When I copy and paste the return value into my code as a HARD CODE, it works just fine.
JSONObject obj = null;
try {
obj = new JSONObject("{lhs:\"1 Euro\",rhs: \"1.3711 U.S. dollars\",error: \"\",icc: true}");
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Many "Crazy" debugging scenario done including changing the server itself, but with no luck.
What I'm doing wrong?
Regards,
In JSON, you need to wrap the keys in quotation marks too. So for this:
{lhs: "1 U.S. dollar",rhs: "0.726321906 Euros",error: "",icc: true},
you will have to do this:
{"lhs": "1 U.S. dollar","rhs": "0.726321906 Euros","error": "","icc": "true"}
You can check your JSON online at : http://jsonlint.com/
After long debugging process, I got the cause, and then the solution. Here we go:
Visit Google Maps Web Service through Chrome browser, and you will get Json response. For debugging purpose, right click on page and select View page source. Note that the string of Json is pure Json with no additions.
When you repeat same procedure with the C# .NET response through Chrome browser, you will notice that the response is NOT pure Json, it is surrounded by this: <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">{JSON String HERE}</string>.
Solution:
To remove this string surrounding the response, you have to return Json string as System.IO.Stream. Here the required code changes:
public System.IO.Stream GetInfo()
{
OutgoingWebResponseContext context = WebOperationContext.Current.OutgoingResponse;
context.ContentType = "text/plain";
return new System.IO.MemoryStream(ASCIIEncoding.Default.GetBytes("{lhs:\"1 Euro\",rhs: \"1.3711 U.S. dollars\",error: \"\",icc: true}"));
}
This solution works fine with me, but is it optimal solution? converting from object to Json, and then from Json string to bytes is little overhead, what do think? suggestions?
In my console application I added a service reference to http://www.ibanbic.be/IBANBIC.asmx .
The operations in it I need to use.
Now, this is my little piece of code, and from what I saw in tutorials, this should be enough to connect to it and get a value. But the only value I get is "String is Empty".
using (BANBICSoapClient WebService = new BANBICSoapClient())
{
string bban = "*****";
try
{
string resultIban = WebService.BBANtoIBAN(bban);
if (resultIban != string.Empty)
{
Console.WriteLine(resultIban);
}
else
{
Console.WriteLine("String is empty.");
}
}
catch(Exception msg)
{
Console.WriteLine(msg);
}
}
Console.ReadLine();
Can anyone give me some more information about what is wrong?
Are you passing a valid BBAN or just the string of asterixes? Do you have a sample of valid data?
Calling the web service with data I just mocked up, e.g. (12345, *) looks to return an empty string, so that's what it might return in the event of invalid data.
I am writing an series of web interfaces to some data. I have WebMethods to return the data in DataSet and XmlDataDocument format (The XmlDataDocument removes all the schema overhead.)
[WebMethod]
public XmlDataDocument Search_XML( string query ) {
return new XmlDataDocument( Search_DataSet( query ) );
}
[WebMethod]
public DataSet Search_DataSet( string query ) {
DataSet result = new DataSet( "SearchResults" );
//... Populate DataSet here
return result;
}
I have also created a function that accepts an XSL formatting string and returns the formated results, allowing the client to format an HTML response they can inject right into their webpage:
public string Search_XSL( string query, string xsl ) {
string result = "";
XmlDataDocument resultxml = Search_XML( query );
XslCompiledTransform transform = new XslCompiledTransform();
using ( StringReader xslstringreader = new StringReader( xsl ) ) {
using ( XmlReader xslxmlreader = XmlReader.Create( xslstringreader ) ) {
using ( MemoryStream transformedmemorystream = new MemoryStream() ) {
using ( StreamWriter transformedstreamwriter = new StreamWriter( transformedmemorystream ) ) {
try {
transform.Load( xslxmlreader );
transform.Transform( resultxml, null, transformedstreamwriter );
transformedstreamwriter.Flush();
transformedmemorystream.Position = 0;
using ( StreamReader transformedreader = new StreamReader( transformedmemorystream ) ) {
result = transformedreader.ReadToEnd();
}
}
catch ( Exception ex ) {
result = ex.InnerException.ToString();
}
}
}
}
}
return result;
}
My question is, how do I implement a WebMethod-like interface for this Search_XSL() function so that I can return the resulting string exactly as the function does, without the XML encoding the WebMethod puts around it? Would that be a new Web Form? How do I implement a Web Form with no actual HTML, just accepting form parameters? Not sure where to start here.
Edit: It looks like a "Generic Handler" .ashx file is the way to go. Is this the right approach?
If you need an HTTP endpoint that processes an HttpContext and returns a custom response, then using IHttpHandler via a Generic Web handler (*.ashx) would be the correct approach to take.
You would read the values from the request query string and then process the request. Your generic handler would use the HttpContext.Response to set the content type of the output stream to text/html and would write the resulting HTML you wish to inject.
WCF is the way to go in .net. You can configure your methods to return json or any number of of other type of serializations. While a generic handler could work there is much better support for wcf. Check out this question for more info.
WebMethods use SOAP, which means you can't remove the SOAP envelope at the service layer. Your original idea is right, here's what you need to finish:
protected void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "text/html";
Response.Write("Hello world");
}
protected override void Render(HtmlTextWriter writer)
{
}
It's a quick and dirty solution, but it works.