How to add content header to Flurl - rest

I would like to know how to add a content header to a flurl-statement.
The onedrive implementation requires me to add a content-type header to the content, and tried every possible solution with no luck.
I'm forced to use the regular httpclient with the following code.
Public Async Function UploadFile(folder As String, filepath As String) As Task(Of Boolean) Implements ICloud.UploadFile
Dim data As Byte() = File.ReadAllBytes(filepath)
Dim uploadurl As String = "drive/items/" + folder + ":/" + Path.GetFileName(filepath) + ":/" + "content?access_token=" + Token.access_token
Using client As New HttpClient()
client.BaseAddress = New Uri(ApiUrl)
Dim request As HttpRequestMessage = New HttpRequestMessage(HttpMethod.Put, uploadurl)
request.Content = New ByteArrayContent(data)
request.Content.Headers.Add("Content-Type", "application/octet-stream")
request.Content.Headers.Add("Content-Length", data.Length)
Dim response = Await client.SendAsync(request)
Return response.IsSuccessStatusCode
End Using
End Function
I already tried the regular PutJsonAsync method of Flurl, but with no luck.
It's the only non-flurl piece remaining in my code.
Thanx in advance.

The real issue here is that there's currently no out-of-the-box support for sending streams or byte arrays in Flurl. I plan to add some soon, but with the implementation details you already have it's easy to add this yourself with an extension method. (Forgive the C#, hopefully you can translate to VB.)
public static Task<HttpResponseMessage> PutFileAsync(this FlurlClient client, string filepath)
{
var data = File.ReadAllBytes(filepath);
var content = new ByteArrayContent(data);
content.Headers.Add("Content-Type", "application/octet-stream");
content.Headers.Add("Content-Length", data.Length);
return client.SendAsync(HttpMethod.Put, content: content);
}
The above works if you already have a FlurlClient, but as the docs describe it's a good idea to have corresponding string and Url extensions, which can just delegate to the above method:
public static Task<HttpResponseMessage> PutFileAsync(this Url url, string filepath)
{
return new FlurlClient(url).PutFileAsync(filepath);
}
public static Task<HttpResponseMessage> PutFileAsync(this string url, string filepath)
{
return new FlurlClient(url).PutFileAsync(filepath);
}
Tuck those away in a static helper class and they should work seamlessly with Flurl:
await uploadurl.PutFileAsync(filepath)

Related

Azure Function Request POST Body Encoding

I'm wondering whether encoding is something I have to explicitly handle in an Azure function (v3) triggered by an http POST.
For example... which approach is correct (s1, s2, or s3):
[Function("MyFancyFunction")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req,
FunctionContext executionContext)
{
// What charset/encoding will be used if not specified?
var s1 = req.ReadAsString();
// Surely it can't be save to assume UTF8?
var s2 = req.ReadAsString(Encoding.UTF8);
// Use the charset of the first content-type
var ct = MediaTypeHeaderValue.Parse(req.Headers.GetValues("content-type").First());
var s3 = req.ReadAsString(ct.Encoding);
...
}
Thanks!
I put together a simple test function and simple http POST console app.
The console app explicitly encodes the POST payload as Win-1252 (only chosen because it has characters from 0-255). The payload includes a 0x0080 character (utf-8 start sequence) which should cause problems if not decoded using the appropriate encoding.
Here's what the POST payload program looks like:
var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, " http://localhost:7071/api/Function1");
req.Content = new StringContent("d\u0080d");
req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
req.Content.Headers.ContentEncoding.Add("win-1252");
client.Send(req);
The test function looks like:
[Function("Function1")]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var s1 = req.ReadAsString(); // s1 == "d\u0080d"... yippie!
var response = req.CreateResponse(HttpStatusCode.OK);
return response;
}
The end result is that YES Azure functions will pay attention to a content-type's charset instruction and use the appropriate Encoding.
which approach is correct (s1, s2, or s3)
Below is the sample code which I used in my environment for encoding data and it worked for me rather than these 3 approaches I suggest to use the below given code.
public static async Task<string> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("SendMessage function requested");
string body = string.Empty;
using (var reader = new StreamReader(req.Body, Encoding.UTF8))
{
body = await reader.ReadToEndAsync();
log.LogInformation($"Message body : {body}");
}
log.LogInformation($"SendMessage processed.");
return body;
}
And here is the Output with the Encoded Data

How to transform PageRequest object to query string?

I'm writing an RESTful API which consumes another RESTful Data API, and I'm using Spring Data.
The client side send the page request using query params like:
http://api.mysite.com/products?page=1&size=20&sort=id,asc&sort=name,desc
And I transform the params to PageRequest object and transfer it to the Service Layer.
In the service, I would like to use TestTemplate to interact with the Data API which use a URL, and How can I transform the PageRequest object to a query string like
page=1&size=20&sort=id,asc&sort=name,desc
then I can request data like:
restTemplate.getForEntity("http://data.mysite.com/products?page=1&size=20&sort=id,asc&sort=name,desc",String.class)
I know I am a bit late on this answer, but I too was not able to found an already implemented way to do this. I ended up developing my own routine for doing this:
public static String encodeURLComponent(String component){
try {
return URLEncoder.encode(component, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("You are dumm enough that you cannot spell UTF-8, are you?");
}
}
public static String queryStringFromPageable(Pageable p){
StringBuilder ans = new StringBuilder();
ans.append("page=");
ans.append(encodeURLComponent(p.getPageNumber() + ""));
// No sorting
if (p.getSort() == null)
return ans.toString();
// Sorting is specified
for(Sort.Order o : p.getSort()){
ans.append("&sort=");
ans.append(encodeURLComponent(o.getProperty()));
ans.append(",");
ans.append(encodeURLComponent(o.getDirection().name()));
}
return ans.toString();
}
It is not complete, there probably are a couple of details that I am missing, but for my user case (and I think for most of them) this works.
you can pass as new :
new PageRequest(int page,int size)
And in repository layer you can write as:
Page<User> findByName(String name,Pageable page)
in place of Pageable page you have to pass new PageRequest(int page,int size)
For ascending order you can refer this:
List<Todo> findByTitleOrderByTitleAsc(String title);
I think you just need to iterate through the request parameters from your other API request, and then pass all the parameters values into the new Url for a new API request.
sudo code could be:
//all query params can be found in Request.QueryString
var queryParams = Request.QueryString;
private string ParseIntoQuery(NameValueCollection values)
{
//parse the identified parameters into a query string format
// (i.e. return "?paramName=paramValue&paramName2=paramValue2" etc.)
}
in your code, you will then do this:
restTemplate.getForEntity(urlAuthority + ParseIntoQuery(Request.QueryString));
simple as that. Hope that answers?
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl("http://data.mysite.com/products");
addRequestParam(uriComponentsBuilder, "page", pageable.getPageNumber());
addRequestParam(uriComponentsBuilder, "size", pageable.getPageSize());
String uri = uriComponentsBuilder.toUriString() + sortToString(pageable.getSort());
addRequestParam method:
private static void addRequestParam(UriComponentsBuilder uriBuilder, String parameterName, Object parameter) {
if (parameter != null) {
uriBuilder.queryParam(parameterName, parameter);
}
}
implement sortToString(Sort sort) method as #Shalen. You have to obtain something like this: &sort=name,asc&sort=name2,desc. Return "" if Sort is null;

Can the Facebook API be used to create link posts with summaries?

When adding a link post in Facebook, a nice looking description (containing a snippet of text from the linked page) and thumbnail are automatically added to the post.
Is there a way to do this automatically using the Facebook API? I am inclined to think that there is not, because posts added by IFTTT, a popular web application that uses the Facebook API, do not contain descriptions. I am unclear as to whether this is a limitation with the Facebook API, and whether there is any way around it.
Yes, it's possible. You can use the Graph Api Method /profile_id/feed. The method receives the arguments message, picture, link, name, caption, description, source, place and tags. The facebook organize the parameters in a "nice looking summary and thumbnail".
You can get more information in the publishing section in the link http://developers.facebook.com/docs/reference/api/
In c#:
public static bool Share(string oauth_token, string message, string name, string link, string picture)
{
try
{
string url =
"https://graph.facebook.com/me/feed" +
"?access_token=" + oauth_token;
StringBuilder post = new StringBuilder();
post.AppendFormat("message={0}", HttpUtility.UrlEncode(message));
post.AppendFormat("&name={0}", HttpUtility.UrlEncode(name));
post.AppendFormat("&link={0}", HttpUtility.UrlEncode(link));
post.AppendFormat("&picture={0}", HttpUtility.UrlEncode(picture));
string result = Post(url, post.ToString());
}
catch (Exception)
{
return false;
}
return true;
}
private static string Post(string url, string post)
{
WebRequest webRequest = WebRequest.Create(url);
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Method = "POST";
byte[] bytes = Encoding.ASCII.GetBytes(post);
webRequest.ContentLength = bytes.Length;
Stream stream = webRequest.GetRequestStream();
stream.Write(bytes, 0, bytes.Length);
stream.Close();
WebResponse webResponse = webRequest.GetResponse();
StreamReader streamReader = new StreamReader(webResponse.GetResponseStream());
return streamReader.ReadToEnd();
}
UPDATE:
Open graph protocol meta tags: http://developers.facebook.com/docs/opengraphprotocol/

multiple pages with C# Web browser control

I am trying to download HTML content from any URL through webbrowser control in C#.net.
I choose webrowser to handle Javascript issues. I am using webbrowser control without placing
it on the form. It works great for one url, but when I call it for multiple urls I am unable
to download the page.
Here is the code
GetWebpage()
{
System.Windows.Forms.WebBrowser wb = new System.Windows.Forms.WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.Navigate(sURI, false);
bDocumentLoaded = false;
while (!bDocumentLoaded)
{
Application.DoEvents();
Thread.Sleep(100);
}
sHTML = wb.DocumentText;
bDocumentLoaded = false;
}
Event:
private void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
System.Windows.Forms.WebBrowser webBrowser1;
webBrowser1 = sender as WebBrowser;
string strTit = webBrowser1.DocumentTitle;
string str = webBrowser1.DocumentText;
bDocumentLoaded = true;
}
Cheers,
Karthik
You can use webclient object to fetch data from some url.
Try using Downloading String
public static void DownloadString (string address)
{
WebClient client = new WebClient ();
string reply = client.DownloadString (address);
Console.WriteLine (reply);
}
You can also use ASYC method of same downloading string.
I think your problem is that some sites are detecting specific browsertype and then they are returning HTML
Try setting the HeaderProperty of WebClient Object this is a list of HttpWebRequest Object
For Example
myWebClient.Headers.Add("Content-Type","application/x-www-form-urlencoded");
Modify the useragent of HTTPWEBRequest then add to headers.
HTTPWEBRequest.UserAgent=".NET Framework Test Client";
You can check more information about this in MSDN Link
I might recommend using the mshtml and SHDocVW libraries and using approach found in the answer here:
Unable to to locate and click a submit button using mshtml.HTMLInputElement

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";