Restful api + special characres - c#

I have the following Rest interface:
[OperationContract(AsyncPattern = true)]
[WebInvoke(Method = "POST", UriTemplate = "/Save/servers/{serverName}/databases{databaseName}")]
Task Save(string subscriptionId, string serverName, string databaseName, Stream policyStream);
I invoke using that client`s method
private async Task<RestResponse> SendRequestWithPayloadAsync(string verbName, string methodName, string requestBody, IEnumerable<object> parameters = null)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string address = BuildAddress(methodName, parameters);
if (requestBody == null)
{
requestBody = String.Empty;
}
byte[] payload = Encoding.UTF8.GetBytes(requestBody);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
SetCertificateSettings(request);
request.Method = verbName;
request.ContentType = "text/xml;charset=utf-8";
request.ContentLength = payload.Length;
// send request over the network
using (Stream dataStream = await request.GetRequestStreamAsync())
{
await dataStream.WriteAsync(payload, 0, payload.Length);
}
return await GetResponse(request, stopwatch, payload.Length);
}
When the databaseName ends with "#" I don`t get it in Save method
For example if I send
databases\hgは常#
I get hgは常 databases argument value.
Any ideas what causes that? and how to solve the issue?

The reason for that is because '#' character is a special character when being used in URLs (you can find more information here: http://en.wikipedia.org/wiki/Fragment_identifier - There are more reserved characters - see the section of RFC 3986)
You should escape URLs using Percent-Encoding (http://en.wikipedia.org/wiki/Percent-encoding)
So, if you want to include a hash-mark (#) in your parameters - pass '%23' instead.

Related

Why is my web request received as "GET" when I'm setting the method to "POST"?

I am posting data to the Amazon MerchantFullfillment uri using the WebRequest class (as detailed in https://msdn.microsoft.com/en-us/library/debx8sh9.aspx)
I get
<?xml version="1.0"?>
<ErrorResponse xmlns="http://mws.amazonservices.com/MerchantFulfillment/2015-06-01">
<Error>
<Type>Sender</Type>
<Code>InvalidParameterValue</Code>
<Message>Either Action or Operation query parameter must be present.</Message>
</Error>
<RequestID>03f0e2ca-4c30-4012-9888-c33acc83446f</RequestID>
</ErrorResponse>
I am sending the Action query parameter "&Action=GetEligibleShippingServices" in the postData. Here is my code--I changed the endpoint url to try to see the request.
public void CreateAWSMessage(DateTime timestamp, ShipmentRequestDetails srd)
{
NameValueCollection awsField = new NameValueCollection();
string stringToSign = StringToSign(timestamp, srd, awsField);
string signature = CreateMessageSignature(srd, awsField, stringToSign);
string signedString = stringToSign + "&Signature=" + signature;
//string url = #"https://mws.amazonaws.com/MerchantFulfillment/2015-06-01";
string url = #"http://localhost/WebApplication1/Service.aspx";
WebRequest gess = WebRequest.Create(url);
HttpWebRequest httpGESS = (HttpWebRequest)gess;
string tmp = gess.Method;
httpGESS.Method = "POST";
httpGESS.ContentType = "x-www-form-urlencoded";
byte[] bytes = Encoding.UTF8.GetBytes(signedString);
httpGESS.ContentLength = bytes.Length;
using (Stream stream = httpGESS.GetRequestStream())
{
stream.Write(bytes, 0, bytes.Length);
}
string responseString;
using (StreamReader streamResponse = new StreamReader(httpGESS.GetResponse().GetResponseStream()))
{
responseString = streamResponse.ReadToEnd();
}
}
When I look at the request in debugging WebApplication1 (the endpoint), it shows the content as empty, and the method as "GET". That explains why the API is not receiving the Action query parameter. Why does the endpoint not see the request payload, or that it's a "POST"?
Here is the sanitized signedString:
AWSAccessKeyId=myAWSAccessKeyID&Action=GetEligibleShippingServices&MWSAuthToken=myMWSAuthToken&SellerId=mySellerID&ShipmentRequestDetails.AmazonOrderId=TST-1234567-1234568&ShipmentRequestDetails.ItemList.Item.1.OrderItemId=Item1&ShipmentRequestDetails.ItemList.Item.1.Quantity=2&ShipmentRequestDetails.MustArriveByDate=2016-12-31T01%3a15%3a52Z&ShipmentRequestDetails.PackageDimensions.Height=9&ShipmentRequestDetails.PackageDimensions.Length=7&ShipmentRequestDetails.PackageDimensions.Unit=inches&ShipmentRequestDetails.PackageDimensions.Width=8&ShipmentRequestDetails.SellerOrderId=TST-1234567-1234568&ShipmentRequestDetails.ShipDate=2016-12-27T01%3a15%3a52Z&ShipmentRequestDetails.ShipFromAddress.AddressLine1=799%20Central%20Ave&ShipmentRequestDetails.ShipFromAddress.AddressLine2=&ShipmentRequestDetails.ShipFromAddress.AddressLine3=&ShipmentRequestDetails.ShipFromAddress.City=Los%20Angeles&ShipmentRequestDetails.ShipFromAddress.CountryCode=US&ShipmentRequestDetails.ShipFromAddress.Email=jp%40example.com&ShipmentRequestDetails.ShipFromAddress.Name=JP%20test&ShipmentRequestDetails.ShipFromAddress.Phone=310-555-1212&ShipmentRequestDetails.ShipFromAddress.PostalCode=90021&ShipmentRequestDetails.ShipFromAddress.StateOrProvinceCode=CA&ShipmentRequestDetails.ShippingServiceOptions.CarrierWillPickUp=False&ShipmentRequestDetails.ShippingServiceOptions.DeliveryExperience=DeliveryConfirmationWithoutSignature&ShipmentRequestDetails.Weight.Unit=ounces&ShipmentRequestDetails.Weight.Value=120&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2016-12-26T21%3a53%3a05Z&Version=2015-06-01&Signature=d9rvl0XO7CbKuR+wNvCwh56ux5oNY/wv3I6z79+fG1M

Unable to Post on Pinterest using their API

I'm receiving a 400 Bad Request error message when posting a pin on Pinterest. It works using Postman, but doesn't work programmatically. Using C#, has anyone been able to successfully post a pin on Pinterest without using the pinsharp wrapper?
private void postPinterest(string messages, string id, string usertoken, string image, string boardname, string username)
{
string link = null;
boardname = boardname.Replace(" ", "-");
string board = username + "/" + boardname;
string url = "https://api.pinterest.com/v1/pins?access_token=" + usertoken;
StringBuilder sb = new StringBuilder();
if (!string.IsNullOrEmpty(board))
sb.Append("&board=" + HttpUtility.UrlEncode(board));
if (!string.IsNullOrEmpty(messages))
sb.Append("&note=" + HttpUtility.UrlEncode(messages));
if (!string.IsNullOrEmpty(link))
sb.Append("&image_url=" + HttpUtility.UrlEncode(link));
string postdata = sb.ToString().Substring(1);
PostData(url, postdata);
}
private object PostData(string url, string postdata)
{
object json=null;
try
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
// req.Accept = "application/json";
using (var stream = req.GetRequestStream())
{
byte[] bindata = Encoding.ASCII.GetBytes(postdata);
stream.Write(bindata, 0, bindata.Length);
}
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
string response = new StreamReader(resp.GetResponseStream()).ReadToEnd();
json = JsonConvert.DeserializeObject<dynamic>(response);
return json;
}
catch (WebException wex)
{
if (wex.Response != null)
{
using (var errorResponse = (HttpWebResponse)wex.Response)
{
using (var reader = new StreamReader(errorResponse.GetResponseStream()))
{
string error = reader.ReadToEnd();
return json;
}
}
}
}
return json;
}
EDIT:
It doesn't work using the JSON format or x-www-form-urlencoded format.
I changed the content type to application/x-www-form-urlencoded and now I'm receiving the error message below. I receive 400 Bad Request error using JSON format:
"{\n \"message\": \"405: Method Not Allowed\",\n \"type\": \"http\"\n}"
The problem is the the parameter that you are posting.
In the Api i could find board as a parameter but both note and image comes under field parameter which specifies the return type JSON.
As per documentation on this page you can post in this format
https://api.pinterest.com/v1/boards/anapinskywalker/wanderlust/pins/?
access_token=abcde&
limit=2&
fields=id,link,counts,note
So I tried the following and its getting response
https://api.pinterest.com/v1/boards/?access_token="YourTokenWithoutQuotes"&fields=id%2Ccreator
Would suggest you to first test the Api you are hitting putting a breakpoint inside the PostData function and check if the passed url is in the correct format and compare it with Pininterest API Explorer.
As you might have already received authorization code and access token so I am assuming your post function should be working fine.
public string postPinterest(string access_token,string boardname,string note,string image_url)
{
public string pinSharesEndPoint = "https://api.pinterest.com/v1/pins/?access_token={0}";
var requestUrl = String.Format(pinSharesEndPoint, accessToken);
var message = new
{
board = boardname,
note = note,
image_url = image_url
};
var requestJson = new JavaScriptSerializer().Serialize(message);
var client = new WebClient();
var requestHeaders = new NameValueCollection
{
{"Content-Type", "application/json" },
{"x-li-format", "json" }
};
client.Headers.Add(requestHeaders);
var responseJson = client.UploadString(requestUrl, "POST", requestJson);
var response = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(responseJson);
return response;
}

Sending data to php from windows phone

I need to send some data from windows phone 7 to php page through POST method, I have the following code at wp7 side
public void SendPost()
{
var url = "http://localhost/HelpFello/profile.php";
// Create the web request object
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
// Start the request
webRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), webRequest);
MessageBox.Show("data sent");
}
void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
// End the stream request operation
Stream postStream = webRequest.EndGetRequestStream(asynchronousResult);
// Create the post data
// Demo POST data
string postData = "user_id=3&name=danish&email_id=mdsiddiquiufo&password=12345&phone_Number=0213&about_me=IRuel2&rating=5";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Add the post data to the web request
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
// Start the web request
webRequest.BeginGetResponse(new AsyncCallback(GetResponseCallback), webRequest);
}
void GetResponseCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response;
// End the get response operation
response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamReader = new StreamReader(streamResponse);
var Response = streamReader.ReadToEnd();
streamResponse.Close();
streamReader.Close();
response.Close();
}
catch (WebException e)
{
MessageBox.Show(e.ToString());
}
}
and following on my localhost, to send the data to database
<?php
require_once("constants.php");
$user_id = $_POST['user_id'];
$name = $_POST['name'];
$email_id = $_POST['email_id'];
$password = $_POST['password'];
$phone_number = $_POST['phone_number'];
$about_me = $_POST['about_me'];
$rating = $_POST['rating'];
$query="INSERT INTO profile(User_ID,Name,Email_ID,password,Phone_Number,About_Me,Rating) VALUES ({$user_id},'{$name}','{$email_id}','{$password}',{$phone_number}, '{$about_me}' , {$rating})";
mysql_query($query,$connection);
mysql_close($connection);
?>
When I run the code I have no errors it means code is working fine, but no data is inserted in the database.
I think there is a better way than HttpWebRequest. That is WebClient. You can change the method there and append data like you do in get string. key=value&key2=value then when you invoke that request and get the response try debugging and getting the output from VS or if that is difficult simply assign he string to a textblock in the code. You will get to know if that page has been ever executed or not.
A sample code :
WebClient wc = new WebClient();
wc.UploadStringCompleted += new UploadStringCompletedEventHandler(wc_UploadStringCompleted);
wc.Headers["Content-Type"] = "application/x-www-form-urlencoded";
wc.Encoding = Encoding.UTF8;
Parameters prms = new Parameters();
prms.AddPair("email", email);
prms.AddPair("password", password);
wc.UploadStringAsync(new Uri(loginUrl), "POST", prms.FormPostData(), null);
private void wc_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
// e.Result will contain the page's output
}
// This is my Parameters and Parameter Object classes
public class Parameters
{
public List<ParameterObject> prms;
public Parameters()
{
prms = new List<ParameterObject>();
}
public void AddPair(string id, string val)
{
prms.Add(new ParameterObject(id, val));
}
public String FormPostData()
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < prms.Count; i++)
{
if (i == 0)
{
buffer.Append(System.Net.HttpUtility.UrlEncode(prms[i].id) + "=" + System.Net.HttpUtility.UrlEncode(prms[i].value));
}
else
{
buffer.Append("&" + System.Net.HttpUtility.UrlEncode(prms[i].id) + "=" + System.Net.HttpUtility.UrlEncode(prms[i].value));
}
}
return buffer.ToString();
}
}
public class ParameterObject
{
public string id;
public string value;
public ParameterObject(string id, string val)
{
this.id = id;
this.value = val;
}
}
First error: assuming that no error messages means success
Second error: gaping SQL injection holes
first fix: always assume queries will fail, and check for that condition:
$result = mysql_query($query) or die(mysql_error());
second fix: ditch the mysql_() functions and switch to PDO using prepared statements with placeholders. Boom. No more injection problems, and your code won't stop working on you when mysql_() is removed in a future PHP version.
ps..
3rd error: no quotes on your phone number value. So someone submits 867-5309, and you end up inserting -4442 because mysql saw it as two numbers being subtracted, not a string.

Can't Login to cPanel with C# WebRequest

I am struggling to develop a C# class to login to cPanel on a web host (Hostgator).
In PHP it is quite easy using the Curl extension as follows:
$url = "http://mysite.com:2082/";
$c = curl_init($url);
curl_setopt($c, CURLOPT_USERPWD, 'user:password');
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($c);
if ($result === false)
$result = curl_error($c);
curl_close($c);
file_put_contents('log.txt', $result);
//print_r($result);
Now here is my C# class with the various attempts to make it work commented out:
class HTTPHandler
{
public static string Connect (string url, string userName, string password)
{
string result;
try
{
// An initial # symbol in the password must be escaped
if (password.Length > 0)
if (password[0] == '#')
password = "\\" + password;
// Create a request for the URL.
WebRequest request = WebRequest.Create(url);
request.PreAuthenticate = true;
request.Credentials = new NetworkCredential(userName, password);
/*
var credCache = new CredentialCache();
credCache.Add(new Uri(url), "Basic",
new NetworkCredential(userName, password));
request.Credentials = credCache;
*/
//request.Method = "POST";
//request.ContentType = "application/x-www-form-urlencoded";
/*
// Create POST data and convert it to a byte array.
string postData = string.Format("user={0}&pass={1}", userName, password);
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentLength = byteArray.Length;
request.ContentType = "application/x-www-form-urlencoded";
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
*/
// Get the response.
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Get the stream containing content returned by the server.
Stream dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Display the content.
result = string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());
// Cleanup the streams and the response.
reader.Close();
dataStream.Close();
response.Close();
}
catch (Exception e)
{
result = string.Format("There was an error:\n{0}", e.Message);
}
return result;
}
}
}
But I keep getting an error 401 (Unauthorized) at the GetResponse stage.
When I compare the $_SERVER vars in my local host test page between the PHP and C# submissions, I get the same data apart from the sender port being a bit different. The crucial PHP_AUTH_USER and PHP_AUTH_PW are the same.
My OS is Windows 7 64 bit and I am using Visual C# 2010.
I guess the solution is really simple, but so far I am baffled. But a relative newbie to C#. I hope somebody can help.
You don't really need to set PreAuthenticate, just let the request figure it out. Also I would suggest using HttpWebRequest instead of WebRequest. The main difference is that you can set CookieContainer property to enable cookies. This is a bit confusing since by default it will have cookies disabled and all you need to do is to set it to new CookieContainer(); to enable cookies for your request.
This matters because of the redirects that happen during authentication and the auth cookie that records the fact that you successfully authenticated.
Also a coding style note: please make sure to wrap all the IDisposables (such as response, stream and reader) in the using() statement.
Also I am unclear why are you escaping # in the password. Request should take care of all your encoding needs automagically.
Complete sample code:
var request = WebRequest.CreateHttp(url);
request.Credentials = new NetworkCredential(username, password);
request.CookieContainer = new CookieContainer(); // needed to enable cookies
using (var response = (HttpWebResponse)request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(response.CharacterSet)))
return string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());
edit: Sorry for all the edits. I was writing code by memory and was struggling a bit with getting the encoding part right.
This is using System.Web where I had to set the project properties to use the full .NET Framework 4 to gain access to this assembly for the HttpUtility and add a reference to System.Web in References.
I didn't test all the overloaded methods but the main thing is the cPanel connection where the authentication credentials are added to the http header when a userName is present.
Also, for cPanel I needed to set request.AllowAutoRedirect = false; so that I control page by page access since I didn't manage to capture cookies.
Here is the code for the HTTP Helper Class that I came up with:
class HTTPHandler
{
// Some default settings
const string UserAgent = "Bot"; // Change this to something more meaningful
const int TimeOut = 1000; // Time out in ms
// Basic connection
public static string Connect(string url)
{
return Connect(url, "", "", UserAgent, "", TimeOut);
}
// Connect with post data passed as a key : value pair dictionary
public static string Connect(string url, Dictionary<string, string> args)
{
return Connect(url, "", "", UserAgent, ToQueryString(args), TimeOut);
}
// Connect with a custom user agent specified
public static string Connect(string url, string userAgent)
{
return Connect(url, "", "", userAgent, "", TimeOut);
}
public static string Connect(string url, string userName, string password, string userAgent, string postData, int timeOut)
{
string result;
try
{
// Create a request for the URL.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
if (userAgent == null)
userAgent = UserAgent;
request.UserAgent = userAgent;
request.Timeout = timeOut;
if (userName.Length > 0)
{
string authInfo = userName + ":" + password;
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
request.Headers["Authorization"] = "Basic " + authInfo;
request.AllowAutoRedirect = false;
}
if (postData.Length > 0)
{
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
// Create POST data and convert it to a byte array.
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentLength = byteArray.Length;
using (Stream dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
}
}
// Get the response.
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
// Get the stream containing content returned by the server.
Stream dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
using (StreamReader reader = new StreamReader(dataStream))
{
result = string.Format("Server response:\n{0}\n{1}", response.StatusDescription, reader.ReadToEnd());
}
}
}
catch (Exception e)
{
result = string.Format("There was an error:\n{0}", e.Message);
}
return result;
}
public static string ToQueryString(Dictionary<string, string> args)
{
List<string> encodedData = new List<string>();
foreach (KeyValuePair<string, string> pair in args)
{
encodedData.Add(HttpUtility.UrlEncode(pair.Key) + "=" + HttpUtility.UrlEncode(pair.Value));
}
return String.Join("&", encodedData.ToArray());
}
}

Unable to cast object of type 'System.Net.HttpWebRequest' to type 'System.Net.FileWebRequest'

I try download file from server with FileWebRequest. But I get error:
Method on download is here:
public string HttpFileGetReq(Uri uri, int reqTimeout, Encoding encoding)
{
try
{
string stringResponse;
var req = (FileWebRequest)WebRequest.Create(uri);
req.Timeout = reqTimeout;
req.Method = WebRequestMethods.File.DownloadFile;
var res = (FileWebResponse)req.GetResponse();
//using (var receiveStream = res.GetResponseStream())
//using (var readStream = new StreamReader(receiveStream,encoding))
//{
// stringResponse = readStream.ReadToEnd();
//}
return stringResponse="0K";
}
catch (WebException webException)
{
throw webException;
}
}
Usage is here:
public dynamic LoadRoomMsg(IAccount account, string roomId)
{
try
{
string uri = string.Format("http://www-pokec.azet.sk/_s/chat/nacitajPrispevky.php?{0}&lok={1}&lastMsg=0&pub=0&prv=0&r=1295633087203&changeroom=1" , account.SessionId, roomId);
var htmlStringResult = HttpFileGetReq(new Uri(uri), ReqTimeout, EncodingType);
//var htmlStringResult = _httpReq.HttpGetReq(new Uri(string.Format("{0}{1}?{2}&lok=", PokecUrl.RoomMsg,account.SessionId,roomId)),
// ReqTimeout, account.Cookies, EncodingType);
if (!string.IsNullOrEmpty(htmlStringResult))
{
return true;
}
return false;
}
catch (Exception exception)
{
throw exception;
}
}
URL on file is here.
I would like read this file to string variable, that’s all. If anyone have some time and can help me I would be very glad to him.
Your URL (http://...) will produce a HttpWebRequest. You can check with the debugger.
Form MSDN:
The FileWebRequest class implements
the WebRequest abstract base class for
Uniform Resource Identifiers (URIs)
that use the file:// scheme to request
local files.
Note the file:// and local files in there.
Tip: Just use the WebClient class.
Rather than implement your own web streams allow the .NET framework to do it all for you with WebClient, for example:
string uri = string.Format(
"http://www-pokec.azet.sk/_s/chat/nacitajPrispevky.php?{0}&lok={1}&lastMsg=0&pub=0&prv=0&r=1295633087203&changeroom=1",
account.SessionId,
roomId);
System.Net.WebClient wc = new System.Net.WebClient();
string webData = wc.DownloadString(uri);
...parse the webdata response here...
Looking at the response from the URL you posted:
{"reason":0}
parsing that should be a simple task with a little string manipulation.
Change FileWebRequest and FileWebResponse to HttpWebRequest and HttpWebResponse.
It doesn't matter that what you're downloading may be a file; as far as the .NET Framework is concerned, you're just retrieving a page from a website.
FileWebRequest is for file:// protocols. Since you're using an http:// url, you want to use HttpWebRequest.
public string HttpFileGetReq(Uri uri, int reqTimeout, Encoding encoding)
{
string stringResponse;
var req = (HttpWebRequest)WebRequest.Create(uri);
req.Timeout = reqTimeout;
var res = (HttpWebResponse)req.GetResponse();
using (var receiveStream = res.GetResponseStream())
{
using (var readStream = new StreamReader(receiveStream,encoding))
{
return readStream.ReadToEnd();
}
}
}

Categories