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 writing a self-hosting server on .NET based on REST architecture with Nancy(version 1.4.4). I preferred to do self-hosting(Nancy.Hosting.Self is version 1.4.1) and one of requires functionalities is to respond for a request with a video file. To make picture clear, my partner writes React application and he needs this video.
I've tried different options:
First I tried Response.AsFile() but when I try to access it by a link, I get 404 with label "The resource you have requested cannot be found." and I have no idea why...
public class HelloModule : NancyModule
{
public HelloModule()
{
Get["/"] = parameters =>
{
return Response.AsFile(#"C:\7\video112018.mp4","video/mp4");
};
}
}
Second variant was to use GenericFileResponce like in the code below, but it leads to the same problem:
public class HelloModule : NancyModule
{
public HelloModule()
{
Get["/"] = parameters =>
{
GenericFileResponse fileResponse = new GenericFileResponse(#"C:\7\video112018.mp4");
return fileResponse;
};
}
}
Last option I tried was to write directly to response stream like in the code below, but in this case an error "The specified network name is no longer available" occurs. And what makes it tricky is that this error occurs sometimes but I didn't find any dependency where it comes from...
public class HelloModule : NancyModule
{
public HelloModule()
{
Get["/"] = parameters =>
{
return new Response
{
ContentType = "video/mp4",
Contents = s =>
{
String fileName = #"C:\7\video112018.mp4";
using (var stream = new FileStream(fileName, FileMode.Open))
stream.CopyTo(s);
s.Flush();
s.Close();
}
};
};
}
}
I would really appreciate if you have any suggestions about these solutions or give another one.
P. S. I also tried to send an image, it works with third approach but not with first or second
P.P. S. don't judge the code strictly, because it's only an example)
I was able to do very basic streaming of video files by doing this:
Get["/"] = p =>
{
var file = new FileStream(#"PATH_TO_FILE", FileMode.Open);
return new StreamResponse(() => file, "video/mp4");
}
This allowed video files to play, but there was no seeking.
In the end I found this post. Adding these extensions allows for the video to be seeked.
I am using Google API for the first time and I want to use Natural Language API.
But I don't know how.
So I search the internet and found an example code.
But when it uses APIs, the program throws an exception.
problem source code and thrown exception:
using Google.Cloud.Language.V1;
using System;
namespace GoogleCloudSamples
{
public class QuickStart
{
public static void Main(string[] args)
{
// The text to analyze.
string text = "Hello World!";
try
{
var client = LanguageServiceClient.Create();
var response = client.AnalyzeSentiment(new Document()
{
Content = text,
Type = Document.Types.Type.PlainText
});
var sentiment = response.DocumentSentiment;
Console.WriteLine($"Score: {sentiment.Score}");
Console.WriteLine($"Magnitude: {sentiment.Magnitude}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
The console output:
The Application Default Credentials are not available.
They are available if running on Google Compute Engine.
Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials.
See https://developers.google.com/accounts/docs/application-default-credentials for more information.
What should I do?
I had used NLP a year ago for entity analysis. I had used Google.Apis.CloudNaturalLanguage.v1. I put together my code with what you have written for sentiment analysis. You will need the API key for this:
var service = new CloudNaturalLanguageService(new CloudNaturalLanguageService.Initializer { ApiKey = ApiKey });
var req = new AnalyzeSentimentRequest {
Document = new Document()
{
Content = text,
Type = Document.Types.Type.PlainText
},
EncodingType = "UTF8"
};
var output = service.Documents.AnalyzeSentiment(req);
var exe = output.Execute();
Hope this works.
Here i have a webjob function using servicebus triggers and outputs. I'd like to set a different configuration for output and input.
public static void OnPush(
[ServiceBusTrigger("%PushProcessor.InputTopicName%", "%PushProcessor.InputTopicSubscriptionName%", AccessRights.Listen)]
BrokeredMessage message,
[ServiceBus("%PushProcessor.OutputTopicName%", AccessRights.Send)]
out BrokeredMessage output
)
I see in latest api that one can control the job host with service bus extensions.
JobHostConfiguration config = new JobHostConfiguration
{
StorageConnectionString = ConfigHelpers.GetConfigValue("AzureWebJobsStorage"),
DashboardConnectionString = ConfigHelpers.GetConfigValue("AzureWebJobsDashboard"),
NameResolver = new ByAppSettingsNameResolver()
};
config.UseServiceBus(new ServiceBusConfiguration
{
MessageOptions = new OnMessageOptions {
MaxConcurrentCalls = 2,
AutoRenewTimeout = TimeSpan.FromMinutes(1),
AutoComplete = true,
},
ConnectionString = ConfigHelpers.GetConfigValue("InputServiceBusConnectionString"),
});
Unfortunately i see no control for the connection string for the output. I'd like a different connection string (different namespace/access rights) to be used for inputs versus outputs.
Perhaps the api can support registering named jobhostconfigurations to a jobhost, and referring to that name in the attributes for the trigger/output. Anyways if there is a way to do this let me know.
Yes, also in the latest beta1 release you'll see that there is now a ServiceBusAccountAttribute that you can apply along with the ServiceBusTrigger/ServiceBus attributes. For example:
public static void Test(
[ServiceBusTriggerAttribute("test"),
ServiceBusAccount("testaccount")] BrokeredMessage message)
{
. . .
}
We've done the same for all the other attribute types (Queue/Blob/Table) via StorageAccountAttribute. These account attributes can be applied at the class/method/parameter level. Please give this new feature a try and let us know how it works for you. Also, see the release notes for more details.
I'm using TuesPechkin for my web application, which I'm testing locally on IIS with VS2013. The user clicks a button and the page's current HTML is saved to a PDF file, which is then emailed out. This process is going to be run regularly as the site's data changes.
When converter.Convert(document) first runs, it converts without problem. Every subsequent attempt, however, results in the process hanging and me needing to restart VS.
Below is some default code I've been using to test.
public void MakePDF()
{
var document = new HtmlToPdfDocument
{
GlobalSettings =
{
ProduceOutline = true,
DocumentTitle = "Pretty Websites",
PaperSize = PaperKind.A4, // Implicit conversion to PechkinPaperSize
Margins =
{
All = 1.375,
Unit = TuesPechkin.Unit.Centimeters
}
},
Objects = {
new ObjectSettings { HtmlText = "<h1>Pretty Websites</h1><p>This might take a bit to convert!</p>" }
}
};
IConverter converter =
new ThreadSafeConverter(
new PdfToolset(
new Win32EmbeddedDeployment(
new TempFolderDeployment())));
byte[] result = converter.Convert(document);
}
Can anyone point me in the right direction on this? Most of my troubleshooting so far has led to some discussions on threading and pooling, but no concrete code solutions for running TuesPechkin more than once.
Have you tried the ThreadSafeConverter? The StandardConverter is only suitable for small console apps.
IConverter converter =
new ThreadSafeConverter(
new RemotingToolset<PdfToolset>(
new Win32EmbeddedDeployment(
new TempFolderDeployment())));
byte[] result = converter.Convert(document);
Note that you should keep the converter somewhere static, or as a singleton instance (as mentioned here).
Since this app on IIS, could get singleton converter, and use RemotingToolset
var toolSet = new RemotingToolset<PdfToolset>(winAnyCpuEmbeddedDeployment);
// Then
using TuesPechkin.Wkhtmltox.AnyCPU;
...
var converter = PDFHelper.Factory.GetConverter();
var result = converter.Convert(This.Document);
Reference : https://github.com/tloy1966/TuesPechkin