RESTful WCF receiving Base64 message. Need to convert message back to XML - c#

I am having some trouble trying to write code within a RESTful WCF service. I have made a method available to a calling client application and I am receiving a message that is of the format Ax27834...... which is a Base64 Binary message. The issue is that following receiving this I need to be able to convert it back to the original xml version of that message that was sent from the client. How can I achieve this in the code snippet below. On line 6 below you will see where the code needs to go. I have searched for a solution but not managed to find anything suitable. I have to receive a message rather than a stream.
I should highlight that the service works fine in the respect of receiving the request. I am just struggling to get the message into a form that I can use.
The receiving code
public Message StoreMessage(Message request)
{
//Store the message
try
{
string message = [NEED SOLUTION HERE]
myClass.StoreNoticeInSchema(message, DateTime.Now);
}
catch (Exception e)
{
log4net.Config.XmlConfigurator.Configure();
ILog log = LogManager.GetLogger(typeof(Service1));
if (log.IsErrorEnabled)
{
log.Error(String.Format("{0}: Notice was not stored. {1} reported an exception. {2}", DateTime.Now, System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, e.Message));
}
}
XElement responseElement = new XElement(XName.Get("elementName", "url"));
XDocument resultDocument = new XDocument(responseElement);
return Message.CreateMessage(OperationContext.Current.IncomingMessageVersion, "elementName", resultDocument.CreateReader());
}
The client code
public string CallPostMethod()
{
const string action = "StoreNotice/New";
TestNotice testNotice = new TestNotice();
const string url = "http://myaddress:myport/myService.svc/StoreNotice/New";
string contentType = String.Format("application/soap+xml; charset=utf-8; action=\"{0}\"", action);
string xmlString = CreateSoapMessage(url, action, testNotice.NoticeText);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] bytesToSend = encoding.GetBytes(xmlString);
request.Method = "POST";
request.ContentLength = bytesToSend.Length;
request.ContentType = contentType;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(bytesToSend, 0, bytesToSend.Length);
requestStream.Close();
}
string responseFromServer;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (Stream dataStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(dataStream))
responseFromServer = reader.ReadToEnd();
dataStream.Close();
}
XDocument document = XDocument.Parse(responseFromServer);
string nameSpace = "http://www.w3.org/2003/05/soap-envelope";
XElement responseElement = document.Root.Element(XName.Get("Body", nameSpace))
.Element(XName.Get(#action + "Response", "http://www.wrcplc.co.uk/Schemas/ETON"));
return responseElement.ToString();
}
Code to create SOAP message
protected string CreateSoapMessage(string url, string action, string messageContent)
{
return String.Format(
#"<?xml version=""1.0"" encoding=""utf-8""?>
<soap12:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap12=""http://www.w3.org/2003/05/soap-envelope""><soap12:Body>{0}</soap12:Body>
</soap12:Envelope>
", messageContent, action, url);
}
NOTE: The TestNotice() object contains a large xml string which is the body of the message.

With a Message object you usually use GetReaderAtBodyContents() to get an XML representation of the body content, unless you know what type the body has then you can use GetBody<>. Try using those to get the string, and then decode it if you still need to. Which you can do as follows:
byte[] encodedMessageAsBytes = System.Convert.FromBase64String(requestString);
string message = System.Text.Encoding.Unicode.GetString(encodedMessageAsBytes);
From there you can reconstruct the xml from the string
Edit: to answer the last part from the comment, the content type should be: text/xml

Related

C# Web API REST Service POST

I originally asked a question regarding a WCF web service that I was trying to write and then found that the ASP.net web API was more appropriate to my needs, due to some feedback on here.
I've now found a good tutorial that tells me how to create a simple REST service using Web API which works well pretty much out of the box.
My question
I have a POST method in my REST service server:
// POST api/values/5
public string Post([FromBody]string value)
{
return "Putting value: " + value;
}
I can POST to this using POSTER and also my C# client code.
However the bit I don't understand is why I have to prepend an '=' sign to the POST data so that it reads: "=Here is my data which is actually a JSON string"; rather than just sending: "Here is my data which is actually a JSON string";
My C# Client that talks to the REST service is written as follows:
public string SendPOSTRequest(string sFunction, string sData)
{
string sResponse = string.Empty;
// Create the request string using the data provided
Uri uriRequest = GetFormRequest(m_sWebServiceURL, sFunction, string.Empty);
// Data to post
string sPostData = "=" + sData;
// The Http Request obj
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriRequest);
request.Method = m_VERB_POST;
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
Byte[] byteArray = encoding.GetBytes(sPostData);
request.ContentLength = byteArray.Length;
request.ContentType = m_APPLICATION_FORM_URLENCODED;
try
{
using (Stream dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
sResponse = reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
//Log exception
}
return sResponse;
}
private static Uri GetFormRequest(string sURL, string sFunction, string sParam)
{
StringBuilder sbRequest = new StringBuilder();
sbRequest.Append(sURL);
if ((!sURL.EndsWith("/") &&
(!string.IsNullOrEmpty(sFunction))))
{
sbRequest.Append("/");
}
sbRequest.Append(sFunction);
if ((!sFunction.EndsWith("/") &&
(!string.IsNullOrEmpty(sParam))))
{
sbRequest.Append("/");
}
sbRequest.Append(sParam);
return new Uri(sbRequest.ToString());
}
Is anybody able to explain why I have to prepend the '=' sign as in the above code (string sPostData = "=" + sData;)?
Many thanks in advance!
The content type x-www-form-urlencoded is a key-value format. With form bodies you are only able to read a single simple type from a request body. As a name is expected, but in this case not allowed, you have to prefix the equal sign to indicate that there is no name with the followed value.
However, you should lean away from accepting simple types within the body of your web-api controller actions.
You are limited to only a single simple type if you attempt to pass data in the body of an HttpPost/HttpPut without directly implementing your own MediaTypeFormatter, which is unlikely to be reliable. Creating a light-weight complex type is generally much more preferable, and will make interoperating with other content-types, like application/json, much easier.

Problems encoding string data that contains percent sign

I'm working on a project where I have to send product information via HTTP POST in XML string to a web server. It turns out that certain product names may have a % sign in their name, such as ".05% Topical Cream". Whenever I attempt to send XML data that contained a % sign in the product name, I get an error apparently because when encoding the XML string data the percent sign caused the data to become malformed.
How can I encode and send the XML string data with % sign in product name safely?
XML Data:
<node>
<product>
<BrandName>amlodipine besylate (bulk) 100 % Powder</BrandName>
</product>
</node>
Web request code:
public string MakeWebServerRequest(string url, string data)
{
var parms = System.Web.HttpUtility.UrlEncode(data);
byte[] bytes = Encoding.UTF8.GetBytes("xml=" + parms);
string webResponse = String.Empty;
try
{
System.Web.HttpUtility.UrlEncode(data);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = bytes.Length;
using (Stream reqStream = req.GetRequestStream())
{
reqStream.WriteTimeout = 3000;
reqStream.Write(bytes, 0, bytes.Length);
reqStream.Close();
}
using (HttpWebResponse response = (HttpWebResponse)req.GetResponse())
{
using (StreamReader rdr = new StreamReader(response.GetResponseStream()))
{
webResponse = rdr.ReadToEnd();
rdr.Close();
}
response.Close();
}
}
Should I be creating the web request differently? What can I do to resolve, while maintaining the product name?
Corrected - and working now. Thanks
Thanks
You need to construct request propely. application/x-www-form-urlencoded means that each parameter is Url-encoded. In your case xml parameter must have value correctly encoded, not just blindly concatenated. Below is sample that should give you stared... hopefully you'll be able to avoid string concateneation to construct XML (and insane way of constructing string constant with queotes you have in original code):
var parameterValue = System.Web.HttpUtility.UrlEncode("<xml>" + data);
byte[] bytes = Encoding.UTF8.GetBytes("xml=" + parameterValue);
There are also plenty of samples how to correctly construct requests of this kind. I.e. C# web request with POST encoding question

XML via HTTP to send a page with a script

I'm trying send XML document to a page .asp, and get the answer, but I get following error:
System.UriFormatException: Invalid URI: The URI scheme is not valid. at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind) at System.Net.WebRequest.Create(String requestUriString) at GNS_ZalkarBank.GNSTaskServiceZalkarBank.CreateRequest(String requestData, String address) at GNS_ZalkarBank.GNSTaskServiceZalkarBank.SendRequest(String requestString, String address) at GNS_ZalkarBank.GNSTaskServiceZalkarBank.processData(TaskInfo& taskInfo, Object& data) at Task.RegistryTemplate.RegistryTaskTemplate.execute(DataSet& dataSet)`
I implemented a method for sending data to a page with asp server script:
private string SendRequest(String requestString, String address)
{
address = "https://myadress/osmp_gni_xml.asp";
HttpWebRequest httpRequest = this.CreateRequest(requestString, address);
string response = GetResponse(httpRequest);
return response;
}
private HttpWebRequest CreateRequest(string requestData, string address)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
request.Method = "POST";
//request.UserAgent = "Test";
byte[] data = Encoding.UTF8.GetBytes(requestData);
request.ContentType = "text/xml; encoding='utf-8'";
request.ContentLength = data.Length;
using (Stream dataStream = request.GetRequestStream())
{
dataStream.Write(data, 0, data.Length);
dataStream.Close();
}
return request;
}
private string GetResponse(HttpWebRequest httpWebRequest)
{
string responseString;
HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
using (Stream dataStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(dataStream))
{
responseString = reader.ReadToEnd();
}
}
return responseString;
}
Server side (script page: osmp_gni_xml.asp):
<%# Language=VBScript CODEPAGE="65001"%>
<%
Sub AddSubNode(Parent, Name, Value)
Set subNode = XMLDoc.createElement(Name)
Parent.appendChild(subNode)
subNode.appendChild(XMLDoc.createTextNode(Value))
End Sub
Function Stream_BinaryToString(Binary, CharSet)
Const adTypeText = 2
Const adTypeBinary = 1
'Create Stream object
Dim BinaryStream 'As New Stream
Set BinaryStream = CreateObject("ADODB.Stream")
'Specify stream type - we want To save text/string data.
BinaryStream.Type = adTypeBinary
'Open the stream And write text/string data To the object
BinaryStream.Open
BinaryStream.Write Binary
'Change stream type To binary
BinaryStream.Position = 0
BinaryStream.Type = adTypeText
'Specify charset For the source text (unicode) data.
If Len(CharSet) > 0 Then
BinaryStream.CharSet = CharSet
Else
BinaryStream.CharSet = "us-ascii"
End If
'Open the stream And get binary data from the object
Stream_BinaryToString = BinaryStream.ReadText
End Function
result=300
OK="incomplete request"
Dim PostData
Dim biData
PostData = ""
If Request.TotalBytes>0 Then
biData = Request.BinaryRead(Request.TotalBytes)
PostData=Stream_BinaryToString(biData, "utf-8")
ProvStr = "Provider=sqloledb;Data Source=TEST;Initial Catalog=TESTOsmp;User Id=tests_osmp;Password=tests;"
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open ProvStr
Set cmdUA = Server.CreateObject("ADODB.Command")
cmdUA.ActiveConnection = Conn
cmdUA.CommandText = "GNI_Import"
cmdUA.CommandType = 4
cmdUA.Parameters.Append cmdUA.CreateParameter("Reestr", 202, 1, 2000, PostData)
Set RS = cmdUA.Execute
result = RS("result")
RS.Close
Conn.Close
Set Conn = Nothing
Set RS = Nothing
End If
'Create XML
Set XMLDoc = Server.CreateObject("Microsoft.XMLDOM")
Set pi = XMLDoc.createProcessingInstruction("xml"," version=""1.0"" encoding=""utf-8""")
XMLDoc.appendChild(pi)
'Main
Set mainNode = XMLDoc.createElement("response")
XMLDoc.appendChild(mainNode)
If result=0 Then
OK="Ok"
Else
result=300
OK="incomplete request"
End If
AddSubNode mainNode, "result", result
AddSubNode mainNode, "comment", OK
Response.ContentType = "text/xml"
Response.Write XMLDoc.XML
Set mainNode = Nothing
Set XMLDoc = Nothing
%>
Whats wrong?
The provided error text comes from here:
private HttpWebRequest CreateRequest(string requestData, string address)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address); // <- THIS LINE
...
}
The error description is complaining that the address https://myadress/osmp_gni_xml.asp is invalid, being https the most likely causes are connection issues.
Trying to connect via https but not having setup https server-side in the first place is very common: verify the address is reachable, security is properly setup, and the sort. A console tool like F12 developer console (part of IE9), FireBug (Firefox extension) or Fiddler (desktop application) are the best tools to find out what's happening when it comes to external connections.
I suspect the code you've provided isn't the code that has the problem, see this:
private string SendRequest(String requestString, --> String address <--)
{
--> address = "https://myadress/osmp_gni_xml.asp"; <--
HttpWebRequest httpRequest = this.CreateRequest(requestString, address);
string response = GetResponse(httpRequest);
return response;
}
You're passing address to the method but then hard-wiring it on the first line. The problem's with the format of address, but it probably won't exist with this code.
What may be happening is that you're reading the address from a file or database and as a human you're seeing "https://myadress/osmp_gni_xml.asp" because you're using a flawed unescaping mechanism but what the code is seeing is something like:
https/:////myaddress//osmp_gni_xml.asp
which gives the same error.
In your actual code, what is the exact value of address in the place where you've overwritten it in SendRequest

POST Json String to Remote PHP Script using Json.NET

I am encountering an unusually strange behavior when POSTing a Json string to a PHP webserver. I use the JsonTextWriter object to create the Json string. I then send the Json string as a POST request. Please see comments. The HTML response in the code is returning the correct output, but when viewed in a browser, the web page displays either NULL or array(0) { }.
private void HttpPost(string uri, string parameters)
{
WebRequest webRequest = WebRequest.Create(uri);
webRequest.ContentType = "application/x-www-form-urlencoded"; // <- Should this be "application/json" ?
webRequest.Method = "POST";
byte[] bytes = Encoding.UTF8.GetBytes(parameters);
string byteString = Encoding.UTF8.GetString(bytes);
Stream os = null;
try
{ // Send the Post Data
webRequest.ContentLength = bytes.Length;
os = webRequest.GetRequestStream();
os.Write(bytes, 0, bytes.Length);
Console.WriteLine(String.Format(#"{0}", byteString)); // <- This matches the Json object
}
catch (WebException ex)
{ //Handle Error }
try
{ // Get the response
WebResponse webResponse = webRequest.GetResponse();
if (webResponse == null) { return null; }
StreamReader sr = new StreamReader(webResponse.GetResponseStream());
Console.WriteLine(sr.ReadToEnd().Trim()); // <- Server returns string response (full HTML page)
}
catch (WebException ex)
{ //Handle Error }
}
Relevant PHP code on the server:
$json = json_encode($_POST); # Not 'standard way'
var_dump(json_decode($json));
Any suggestions would be greatly appreciated.
Thanks
Try using "application/json" as the content type. Also, check the request logs or maybe do a port 80 trace if you can to view what's being sent to the server in the request body.
You can also narrow the scope of the problem -- is it the C# code or the PHP code that's bad -- by writing a quick JQuery ajax function that sends some JSON to the PHP server. This isolation of the PHP code from the C# code will tell you if the PHP is at least working correctly. If it is, then the problem is in the C# code.

What is the correct way in .NET to encode an XML string for Posting to a URL?

For the first time, I am having to use a vendor's API by posting an XML dataset to a URL. If I encode the dataset using http://meyerweb.com/eric/tools/dencoder/, and manually paste the URL & dataset into a browser, I get a successful response.
However, when trying to do the same thing with .NET, I get a response from the vendor API indicating that my dataset is invalid.
Here's my code (which I copied from elsewhere on the good ol' interweb):
public void PostXML(string value1, string value2, string value3, out string responseStatus, out string responseBody)
{
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create("https://www.vendorURL.com/API/page.html?input=");
// Set the Method property of the request to POST.
request.Method = "POST";
// Create POST data and convert it to a byte array.
string postData = "<DATASET>";
postData += "<FIELD1>" + value1+ "</FIELD1>";
postData += "<FIELD2>" + value2 + "</FIELD2>";
postData += "<FIELD3>" + value3 + "</FIELD3>";
postData += "</DATASET>";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Set the ContentType property of the WebRequest.
request.ContentType = "application/x-www-form-urlencoded";
// Set the ContentLength property of the WebRequest.
request.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream();
// Write the data to the request stream.
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close();
// Get the response.
WebResponse response = request.GetResponse();
// Assign the status.
responseStatus = ((HttpWebResponse)response).StatusDescription;
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
responseBody = reader.ReadToEnd();
// Clean up the streams.
reader.Close();
dataStream.Close();
response.Close();
}
Keep in mind, I am getting a response from the vendor API. It's just telling me that my dataset is invalid. I have already contacted the vendor's customer support. That have confirmed that my dataset itself is correct, but somewhere along the way I'm encoding it incorrectly for it to be recognizable by the vendor API.
Several things are missing.
You should probably start with an xml declaration
<?xml version="1.0"?>
Also are you sure the dataset tags are upper case? Case is significant in Xml.
<?xml version="1.0"?>
<dataset><field>value</field></dataset>
Additionally you might try XmlWriter:
using(var writer = XmlWriter.Create(stream))
{
writer.WriteStartDocument();
writer.WriteStartElement("dataset");
writer.WriteElementString("field1", "value");
writer.WriteElementString("field2", "value");
writer.WriteElementString("field3", "value");
writer.WriteEndElement();
writer.WriteEndDocument();
}
Finally contact the vendor to send you sample code/API...
Try using System.Web.HttpUtility.UrlEncode on your post data before invoking "Encoding.UTF8.GetBytes(postData);".

Categories

Resources