Nancy : parsing "multipart/form-data" requests - rest

I have a RestSharp Client and Nancy Self Host Server.
What I want is
To send multipart form data from client and parse that data easily
from server :
Send binary file and Json data As Multipart Form Data from RestSharp client
and able to get binary file and Json object from Nancy Server
At Client using Restsharp : [ http://restsharp.org/ ] I try to send "multipart/form-data" requests which contains a binary file plus some meta data in json format:
var client = new RestClient();
...
IRestRequest restRequest = new RestRequest("AcmeUrl", Method.POST);
restRequest.AlwaysMultipartFormData = true;
restRequest.RequestFormat = DataFormat.Json;
// I just add File To Request
restRequest.AddFile("AudioData", File.ReadAllBytes("filePath"), "AudioData");
// Then Add Json Object
MyObject myObject = new MyObject();
myObject.Attribute ="SomeAttribute";
....
restRequest.AddBody(myObject);
client.Execute<MyResponse>(request);
At Server using Nancy[ http://nancyfx.org/ ], Itry to get File and Json Object [Meta Data ]
// Try To Get File : It Works
var file = Request.Files.FirstOrDefault();
// Try To Get Sended Meta Data Object : Not Works.
// Can Not Get MyObject Data
MyObject myObject = this.Bind<MyObject>();

For multipart data, Nancy's code is a bit more complex.
Try something like this:
Post["/"] = parameters =>
{
try
{
var contentTypeRegex = new Regex("^multipart/form-data;\\s*boundary=(.*)$", RegexOptions.IgnoreCase);
System.IO.Stream bodyStream = null;
if (contentTypeRegex.IsMatch(this.Request.Headers.ContentType))
{
var boundary = contentTypeRegex.Match(this.Request.Headers.ContentType).Groups[1].Value;
var multipart = new HttpMultipart(this.Request.Body, boundary);
bodyStream = multipart.GetBoundaries().First(b => b.ContentType.Equals("application/json")).Value;
}
else
{
// Regular model binding goes here.
bodyStream = this.Request.Body;
}
var jsonBody = new System.IO.StreamReader(bodyStream).ReadToEnd();
Console.WriteLine("Got request!");
Console.WriteLine("Body: {0}", jsonBody);
this.Request.Files.ToList().ForEach(f => Console.WriteLine("File: {0} {1}", f.Name, f.ContentType));
return HttpStatusCode.OK;
}
catch (Exception ex)
{
Console.WriteLine("Error!!!!!! {0}", ex.Message);
return HttpStatusCode.InternalServerError;
}
};
Have a look at:
http://www.applandeo.com/en/net-and-nancy-parsing-multipartform-data-requests/

Related

Pushing an update to VSTS via REST API

I am struggling with pushing an update to an existing file in a VSTS Git repository.
The documentation https://learn.microsoft.com/en-us/rest/api/vsts/git/pushes/create#update_a_file shows what to do.
When I use GET instead of POST, I get all existing pushes. So basically the request is working. This is my URL https://mine.visualstudio.com/_apis/git/repositories/ad3d7615-6e4a-4988-bd3f-ca80d7ec9791/pushes?api-version=4.1-preview.
I am testing the request from a console application. The body is created from a dynamic that is serialized later. The filecontent is a string var x=1;.
dynamic body = new
{
refUpdates = new[] { new { name = "refs/heads/master", oldObjectId = branchObjectId } },
commits = new[] {
new {
comment = "Updated Configuration" ,
changes = new[] {
new {
changeType = "edit",
item = new { path = "/files/filename.js"},
newContent = new { content = filecontent, contentType = "rawtext" }
}
}
}
}
};
The request looks like this:
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
byte[] login = Encoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalaccesstoken));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(login));
var content = new StringContent(body, Encoding.UTF8, "application/json");
using (HttpResponseMessage response = client.PostAsync(url, content).Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
responseBodyAction.Invoke(responseBody);
}
}
It is not successful, as the following Exception is thrown:
System.Net.Http.HttpRequestException: 'Response status code does not
indicate success: 404 (Not Found).
The body is a valid JSON.
Some API calls need the url .vsrm.visualstudio.com instead of .visualstudio.com. But not in this case.
I am out of ideas. Did anybody successfully push an update to VSTS and can show me how to do it?
As mentioned in a comment I was able to add instead of edit.
The solution is to first check in a file and then send an update/edit.
If there is no existing file, an edit will fail with the Exception.

making a REST calls from unity3d - Need to pass JSON object using HTTPWebRequest

How to make a REST call in unity3d? especially POST method. I have tried with GET request. Pls see the below get request. so i need to write POST request in Unity3d. The post rquest should be in JSON format. I tried with below code. It's hit the my service but the receiving JSON object is null. Hope your support.
var httpWebReq = WebRequest.Create("http://localhost:6091/UserService.svc/RegisterUser/") as HttpWebRequest;
httpWebReq.ContentType = "text/json;charset=utf-8";
httpWebReq.Method= "POST";
using(var streamWriter = new StreamWriter(httpWebReq.GetRequestStream()))
{
string user = "{UserID:0," +
"Email:'ruwan#gmail.com'," +
"Password:'ruwan123'," +
"NickName:'ruwa'," +
"Age:35" +
"}";
byte[] formData = UTF8Encoding.UTF8.GetBytes(user);
httpWebReq.ContentLength = formData.Length;
streamWriter.Write(formData);
}
var httpResponse = (HttpWebResponse)httpWebReq.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var responseText = streamReader.ReadToEnd();
//Now you have your response.
//or false depending on information in the response
Debug.Log(responseText);
}
use WWW for GET,
use WWW with WWWForm for POST.
Finally i got the solution thanks to all for help.
Actually the easiest way to make rest call for WCF is we have to add the Newtonsoft.Json. Finally my code is-
GET --
WebClient myWebClient = new WebClient();
myWebClient.Encoding = Encoding.UTF8;
myWebClient.Headers.Add("Content-Type", "text/json");
var json = JsonConvert.DeserializeObject<Room[] >(new WebClient().DownloadString("Your URL"));
List<yourclass> test1= new List<yourclass>();
foreach (var test in json)
{
test1.Add(new yourclass()
{
yourclass.property1 = test.property1
});
}
Debug.Log(test1);
POST---
WebClient myWebClient = new WebClient();
var Test = JsonConvert.SerializeObject(new
{
YourProperty= 0
}, new JsonSerializerSettings() { Formatting = Newtonsoft.Json.Formatting.None });
myWebClient.Encoding = Encoding.UTF8;
myWebClient.Headers.Add("Content-Type", "text/json");
string responsebody = myWebClient.UploadString("Your URL", "POST", Test );
//if(responsebody == true)
Debug.Log(responsebody);

Azure REST WebClient PUT Blob

I'm trying to simply upload a new blob to an Azure Storage countainer using WebClient like this :
var sas = "[a new generated sas with Read, Write, List & Delete permissions]";
var sData = "This is a test!";
var sEndPoint = "http://myaccount.blob.core.windows.net/mycontainer/MyTest.txt" + sas;
var clt = new WebClient();
var res = await clt.UploadStringTaskAsync(sEndPoint, "PUT", sData);
This is giving me a "(400) Bad Request." error. Am I doing anything wrong here?
Thanks
(By the way, I need to use REST instead of Client API since I'm in a Silverlight project)
You would need to define a request header (x-ms-blob-type) for blob type and set it's value to BlockBlob. Also for Put requests you would need to define the Content-Length request header as well. I wrote a blog post on Shared Access Signatures and performing some blob operations using that (with both REST API and Storage Client library) which you can read here: http://gauravmantri.com/2013/02/13/revisiting-windows-azure-shared-access-signature/.
and here's the code from that post on uploading blob. It uses HttpWebRequest/HttpWebResponse instead of WebClient:
static void UploadBlobWithRestAPISasPermissionOnBlobContainer(string blobContainerSasUri)
{
string blobName = "sample.txt";
string sampleContent = "This is sample text.";
int contentLength = Encoding.UTF8.GetByteCount(sampleContent);
string queryString = (new Uri(blobContainerSasUri)).Query;
string blobContainerUri = blobContainerSasUri.Substring(0, blobContainerSasUri.Length - queryString.Length);
string requestUri = string.Format(CultureInfo.InvariantCulture, "{0}/{1}{2}", blobContainerUri, blobName, queryString);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
request.Method = "PUT";
request.Headers.Add("x-ms-blob-type", "BlockBlob");
request.ContentLength = contentLength;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(Encoding.UTF8.GetBytes(sampleContent), 0, contentLength);
}
using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
{
}
}
When testing against the blob emulator this is the code I need to get it working:
var connection = ConfigurationManager.AppSettings["AzureStorageConnectionString"];
var storageAccount = CloudStorageAccount.Parse(connection);
var client = new WebClient();
client.Headers.Add("x-ms-blob-type", "BlockBlob");
client.Headers.Add("x-ms-version", "2012-02-12");
client.UploadData(string.Format(#"{0}/$root/{1}{2}", storageAccount.BlobEndpoint, myFileName, sharedAccessSignature), "PUT", _content);

Working with Azure in Winrt with Rest API, trouble with signature

I'm trying to work with azure storage in winrt. Since the azure storage client is not compatible with winrt I am trying to use azure's rest API. I am having a heck of a time getting the signature right and I could use another set of eyes to help me see where I'm going wrong.
Azure Account provides a name and key property, this method builds up the request right now simply listing all blobs.
'private async void BuildHTTPRequest(AzureAccount account)
{
System.Net.Http.HttpClient request = new HttpClient();
request.BaseAddress = new Uri(string.Format("http://{0}.blob.core.windows.net/", account.Name));
// Always have to use UTC date/time
request.DefaultRequestHeaders.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
string fmtStringToSign = "{0}\n{1}\n{2}\n{3:R}\n{4}{5}";
request.DefaultRequestHeaders.Add("x-ms-version", "2011-08-18");
string hdr = CanonicalizeHeaders(request.DefaultRequestHeaders);
string authValue = string.Format(fmtStringToSign, "GET", "", "", "", hdr, "");
byte[] signatureByteForm = System.Text.Encoding.UTF8.GetBytes(authValue);
string hashKey = account.Key;
MacAlgorithmProvider macAlgorithmProvider = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA256");
BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
var messageBuffer = CryptographicBuffer.ConvertStringToBinary(authValue, encoding);
IBuffer keyBuffer = CryptographicBuffer.ConvertStringToBinary(hashKey, encoding);
CryptographicKey hmacKey = macAlgorithmProvider.CreateKey(keyBuffer);
IBuffer signedMessage = CryptographicEngine.Sign(hmacKey, messageBuffer);
string hashedString = CryptographicBuffer.EncodeToBase64String(signedMessage);
String authHeader = String.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", "SharedKey",
account.Name, hashedString);
request.DefaultRequestHeaders.Add("Authorization", authHeader);
// Send the request to the queue
try
{
var test1 = request.GetAsync("?comp=list").Result;
if (test1.IsSuccessStatusCode)
{
}
}
catch (WebException ex) { }
}
This should set the headers up for signing...
public string CanonicalizeHeaders(System.Net.Http.Headers.HttpRequestHeaders hdrCollection)
{
StringBuilder retVal = new StringBuilder();// Look for header names that start with "x-ms-" // Then sort them in case-insensitive manner.
List<string> httpStorageHeaderNameArray = new List<string>();
Dictionary<string, string> ht = new Dictionary<string, string>();
foreach (var key in hdrCollection)
{
if (key.Key.ToLowerInvariant().StartsWith("x-ms-", StringComparison.Ordinal))
{
if (ht.ContainsKey(key.Key.ToLowerInvariant()))
{
ht[key.Key.ToLowerInvariant()] = string.Format("{0},{1}", ht[key.Key.ToLowerInvariant()],
hdrCollection.FirstOrDefault(m => m.Key == key.Key).ToString().Replace("\n", string.Empty).Replace("\r", string.Empty).Trim());
}
else
{
httpStorageHeaderNameArray.Add(key.Key.ToLowerInvariant());
ht.Add(key.Key.ToLowerInvariant(),
hdrCollection.FirstOrDefault(m => m.Key == key.Key).Value.FirstOrDefault().ToString().Replace("\n", string.Empty).Replace("\r", string.Empty).Trim());
}
}
}
httpStorageHeaderNameArray.Sort();// Now go through each header's values in the sorted order and append them to the canonicalized string.
foreach (string key in httpStorageHeaderNameArray)
{
retVal.AppendFormat("{0}:{1}\n", key.Trim(), ht[key]);
}
return retVal.ToString();
}
'
Latest version of storage client library supports WinRT. You can read more about it here: http://blogs.msdn.com/b/windowsazurestorage/archive/2012/10/29/introducing-windows-azure-storage-client-library-2-0-for-net-and-windows-runtime.aspx. What I did was download the source code from Github: https://github.com/WindowsAzure/azure-sdk-for-net, opened the solution in VS 2012 and built RT project to get the necessary winmd files.
Coming to your problem, I believe you're running into this issue because you're passing an empty string for canonicalized resource string:
string authValue = string.Format(fmtStringToSign, "GET", "", "", "", hdr, "")
Please see this link for more details on creating canonicalized resource string: http://msdn.microsoft.com/en-us/library/windowsazure/dd179428.aspx.

REST Windows Phone Photo upload

I'm trying to upload a photo to a REST api in a Windows Phone 7 application using RestSharp for my Gets/Posts.
The post parameters are as follows:
photo:
The photo, encoded as multipart/form-data
photo_album_id:
Identifier of an existing photo album, which may be an event or group
album
I've created my request, but every time I get back "{\"details\":\"missing photo parameter\",\"problem\":\"The API request is malformed\"}\n
My photo parameter looks like this:
"---------------------------8cd9bfbafb3ca00\r\nContent-Disposition: form-data; name=\"filename\"; filename=\"somefile.jpg\"\r\nContent-Type: image/jpg\r\n\r\n(some binary junk listed here)\r\n-----------------------------8cd9bfbafb3ca00--"
I'm not quite sure if it's a problem with how I'm presenting the binary data for the image (currently in my PhotoTaskCompleted event, I read the contents of e.ChosenPhoto into a byte[] and pass that to a helper method to create the form data) or if I just don't create the form correctly.
I'm just trying to do this a simple as possible, then I can refactor once I know how it all works.
void ImageObtained(object sender, PhotoResult e)
{
var photo = ReadToEnd(e.ChosenPhoto);
var form = PostForm(photo);
var request = new RequestWrapper("photo", Method.POST);
request.AddParameter("photo_album_id", _album.album_id);
request.AddParameter("photo", form);
request.Client.ExecuteAsync<object>(request, (response) =>
{
var s = response.Data;
});
}
private string CreateBoundary()
{
return "---------------------------" + DateTime.Now.Ticks.ToString("x");
}
private string PostForm(byte[] data)
{
string boundary = CreateBoundary();
StringBuilder post = new StringBuilder();
post.Append(boundary);
post.Append("\r\n");
post.Append("Content-Disposition: form-data; name=\"filename\"; filename=\"somefile.jpg\"");
post.Append("\r\n");
post.Append("Content-Type: image/jpg");
post.Append("\r\n\r\n");
post.Append(ConvertBytesToString(data));
post.Append("\r\n");
post.Append("--");
post.Append(boundary);
post.Append("--");
return post.ToString();
}
public static string ConvertBytesToString(byte[] bytes)
{
string output = String.Empty;
MemoryStream stream = new MemoryStream(bytes);
stream.Position = 0;
using (StreamReader reader = new StreamReader(stream))
{
output = reader.ReadToEnd();
}
return output;
}
Hammock for Windows Phone makes this real simple.
You just add the file to the request using the AddFile method and pass it the photo stream.
var request = new RestRequest("photo", WebMethod.Post);
request.AddParameter("photo_album_id", _album.album_id);
request.AddFile("photo", filename, e.ChosenPhoto);
Hum are you sure that your PostForm is correct ? The content-* params should be set in the headers of your POST and not in the body ?
var request = (HttpWebRequest)WebRequest.Create(url);
request.Headers.Add(HttpRequestHeader.Authorization,"blabla");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";