How to download shared image in Mirror API using .NET - google-mirror-api

I have subscribed timeline notification in my Glassware app. I got notification when user shared an image with Glassware contest. Now I need to download that image to my Glassware application to do the processing.
Notification notification =
new NewtonsoftJsonSerializer().Deserialize<Notification>(Request.InputStream);
String userId = notification.UserToken;
MirrorService service = new MirrorService(new BaseClientService.Initializer()
{
Authenticator = Utils.GetAuthenticatorFromState(Utils.GetStoredCredentials(userId))
});
if (notification.Collection == "timeline")
{
foreach (UserAction action in notification.UserActions)
{
if (action.Type == "SHARE")
{
TimelineItem item = service.Timeline.Get(notification.ItemId).Fetch();
//i have to download content here.
break;
}
else
{
Console.WriteLine(
"I don't know what to do with this notification: " + action.ToString());
}
}
}

Downloading an attachment is described in the reference guide:
using System;
using Google.Apis.Mirror.v1;
using Google.Apis.Mirror.v1.Data;
using System.Net;
using System.IO;
public class MyClass {
// ...
/// <summary>
/// Print an attachment's metadata.
/// </summary>
/// <param name="service">Authorized Mirror service.</param>
/// <param name="itemId">ID of the timeline item the attachment belongs to.</param>
/// <param name="attachmentId">ID of the attachment to print metadata for.</param>
public static void PrintAttachmentMetadata(
MirrorService service, String itemId, String attachmentId) {
try {
Attachment attachment = service.Timeline.Attachments.Get(itemId, attachmentId).Fetch();
Console.WriteLine("Attachment content type: " + attachment.ContentType);
Console.WriteLine("Attachment content URL: " + attachment.ContentUrl);
} catch (Exception e) {
Console.WriteLine("An error occurred: " + e.Message);
}
}
/// <summary>
/// Download a timeline items's attachment.
/// </summary>
/// <param name="service">Authorized Mirror service.</param>
/// <param name="attachment">Attachment to download content for.</param>
/// <returns>Attachment's content if successful, null otherwise.</returns>
public static System.IO.Stream DownloadAttachment(
MirrorService service, Attachment attachment) {
try {
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
new Uri(attachment.ContentUrl));
service.Authenticator.ApplyAuthenticationToRequest(request);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK) {
return response.GetResponseStream();
} else {
Console.WriteLine(
"An error occurred: " + response.StatusDescription);
return null;
}
} catch (Exception e) {
Console.WriteLine("An error occurred: " + e.Message);
return null;
}
}
// ...
}

Related

Read in output stream through ssh connection using Renci.SshNet

I've looked at a lot of other stack overflow posts but none of them really were similar to what I am trying to accomplish. Basically, I am trying to connect to a device running Windows CE through an SSH connection and capture any output that is printed to the terminal. When I connect via ssh using Putty I can see many print statements in the terminal which are used for debugging. I am trying to capture these debugging statements and use them in my wpf application. These debugging statements are not a response to a command, they are just printed to the terminal.
So far I am able to send a command and receive a single response but what I am looking for is to be able to receive a response indefinitely, until the user closes the connection or the application.
I am using Renci.SshNet to send my commands and I was messing around with using a ShellStream but was not able to get it working. Here is what I have so far:
using System;
using System.Threading;
using System.Windows;
using Renci.SshNet;
namespace TestSshConsole
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private SshClient _sshConnection;
private ShellStream _shellStream;
private delegate void UpdateTextCallback(string message);
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// When the user presses connect, connect to the device
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
// Connect to device
_sshConnection = new SshClient(hostname.Text, int.Parse(port.Text), username.Text, password.Text);
_sshConnection.Connect();
// Create a shell stream
_shellStream = _sshConnection.CreateShellStream("test", 80, 60, 800, 600, 65536);
MessageBox.Show("Connected!");
}
catch (Exception exception)
{
MessageBox.Show($"Error {exception.Message}");
}
}
/// <summary>
/// Start a new thread used to receive SSH data when the window is loaded
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ThreadStart threadStart = new ThreadStart(RecvSshData);
Thread thread = new Thread(threadStart);
thread.IsBackground = true;
thread.Start();
}
/// <summary>
/// Receive SSH data and write it to the textbox
/// </summary>
private void RecvSshData()
{
while (true)
{
try
{
if (_shellStream != null && _shellStream.DataAvailable)
{
string data = _shellStream.Read();
textBox.Dispatcher.Invoke(new UpdateTextCallback(UpdateText), data);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
Thread.Sleep(200);
}
}
/// <summary>
/// Write message to the textbox
/// </summary>
/// <param name="message"></param>
private void UpdateText(string message)
{
textBox.AppendText(message + "\r\n");
}
}
}
From what I have read in other posts it seems like this should work and should capture all of the data but it does not. There could be something I am doing wrong in my implementation or they may even be a better way to do it.
Any input with help or recommendations is appreciated.
I got it working to a certain extent. "StartRecording" begins to record the stream on a separate thread which works and just writes it to the console for now. This is able to receive all of the data that is printed to the terminal on my device.
The only issue that I am having now is that the data stops coming through after about a minute. I'm not sure what is happening yet but I think the ShellStream is disconnecting at some point and I'm not sure why.
private SshClient _sshClient;
private ShellStream _shellStream;
private StreamReader _reader;
private StreamWriter _writer;
public Recorder()
{
try
{
_sshClient = new SshClient(_hostname, _port, _username, _password);
_sshClient.Connect();
_shellStream = _sshClient.CreateShellStream("Terminal", 80, 60, 800, 600, 65536);
_reader = new StreamReader(_shellStream, Encoding.UTF8, true, 1024, true);
_writer = new StreamWriter(_shellStream) { AutoFlush = true };
}
catch (Exception e)
{
// TODO
Console.WriteLine(e);
}
}
/// <summary>
/// Begin recording the output of "routediagnostic on" command
/// </summary>
public void StartRecording()
{
try
{
IsRecording = true;
WriteStream("routediagnostic on");
// Start a background thread that will read in the data from the Pyng terminal
ThreadStart threadStart = ReceiveData;
Thread thread = new Thread(threadStart) {IsBackground = true};
thread.Start();
}
catch (Exception e)
{
// TODO
Console.WriteLine(e);
}
finally
{
IsRecording = false;
}
}
private void ReceiveData()
{
while (true)
{
try
{
if (_reader != null)
{
StringBuilder result = new StringBuilder();
string line;
while ((line = _reader.ReadLine()) != null)
{
result.AppendLine(line);
}
if (!string.IsNullOrEmpty(result.ToString()))
{
// TODO - Parse data at this point
Console.WriteLine(result.ToString());
}
}
}
catch (Exception e)
{
// TODO
Console.WriteLine(e);
}
Thread.Sleep(200);
}
}
private void WriteStream(string cmd)
{
_writer.WriteLine(cmd);
while (_shellStream.Length == 0)
{
Thread.Sleep(500);
}
}

Can't do API call from Module in WinForms

When my startup is Module, the api call just kills the application altogether. I need my entry point to be a module. How can i accomplish this?
Module EDIDownloaderModule
Sub Main(args As String())
ProcessApi()
End Sub
Private Async Sub ProcessApi()
Dim apiUrl As String = "http://localhost:3554/MWAPI/Projects/GetProjectByCustomerAndOrderIds?customerId=abc&customerOrderId=xyz"
Dim apiResult As ApiCallResult(Of Project) = Await ApiCrudCallHelper.Get(Of Project)(apiUrl)
Dim msg As String = apiResult.Message
End Sub
End Module
When my startup is a form, i can do api calls without any issue
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
ProcessApi()
End Sub
Private Async Sub ProcessApi()
Dim apiUrl As String = "http://localhost:3554/API/Projects/GetByCustomerAndOrder?customerId=abc&customerOrderId=xyz"
Dim apiResult As ApiCallResult(Of Project) = Await ApiCrudCallHelper.Get(Of Project)(apiUrl)
Dim msg As String = apiResult.Message
End Sub
End Class
And here is the Helper code for the API calls
public class ApiCallResult<X> where X : class
{
public HttpStatusCode StatusCode { get; set; }
public string ReasonPhrase { get; set; }
public bool IsError { get; set; }
public bool IsException { get; set; }
public string Message { get; set; }
public X ResponseObject { get; set; } //deserialized object, could be List, int string or just a single object
}
public static class ApiCrudCallHelper
{
/// <summary>
/// Performs Post and returns ApiCallResult
/// </summary>
/// <typeparam name="T">model to Post, could be null, T, List T</typeparam>
/// <typeparam name="X">return model by API, could be X, List X, string </typeparam>
/// <param name="data">data to post of type T, List T</param>
/// <param name="apiUrl">api full URL like http://localhost:65152/API/Test if executing custom action, provide that as well at the end </param>
/// <returns>
/// ApiCallResult
/// StatusCode: status code returned by the API
/// ReasonPhrase: reason phrase returned by the API
/// IsError: true/false
/// IsException: true/false
/// Message: error message, exception message, or result of OK etc results by API
/// X ResponseObject: model returned by the API, it might not be available in all cases. Could be X, List X or string as provided by X above
/// </returns>
public static async Task<ApiCallResult<X>> Post<T, X>(T data, string apiUrl) where X : class
{
var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
try
{
//json string
var jsonString = JsonConvert.SerializeObject(data);
using (var client = new HttpClient())
{
var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
var response = await client.PostAsync(apiUrl, httpContent);
var jsonResponseString = await response.Content.ReadAsStringAsync();
//fill
if (response.IsSuccessStatusCode)
{
//deserialize
if (!typeof(X).Equals(typeof(string)))
{
apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
}
apiCallResult.IsError = false;
}
else
{
try
{
ApiErrorMessage myMessage = JsonConvert.DeserializeObject<ApiErrorMessage>(jsonResponseString);
if (!string.IsNullOrWhiteSpace(myMessage?.Message))
{
jsonResponseString = myMessage.Message;
}
}
catch (Exception e)
{}
jsonResponseString = jsonResponseString.Trim('"');
}
apiCallResult.StatusCode = response.StatusCode;
apiCallResult.ReasonPhrase = response.ReasonPhrase;
apiCallResult.Message = jsonResponseString;
}
}
catch (Exception ex)
{
apiCallResult.IsException = true;
apiCallResult.Message = ex.Message;
}
return apiCallResult;
}
/// <summary>
/// Performs Put and returns ApiCallResult
/// </summary>
/// <typeparam name="T">model to Post, could be null, T, List T</typeparam>
/// <typeparam name="X">return model by API, could be X, List X, string </typeparam>
/// <param name="data">data to post of type T, List T</param>
/// <param name="apiUrl">api full URL including the Id like http://localhost:65152/API/Test/12345 if executing custom action, provide that as well </param>
/// <returns>
/// ApiCallResult
/// HttpStatusCode StatusCode: status code returned by the API
/// string ReasonPhrase: reason phrase returned by the API
/// bool IsError: true/false
/// bool IsException: true/false
/// string Message: error message, exception message, or result of OK etc results by API
/// X ResponseObject: model returned by the API, it might not be available in all cases. Could be X, List X or string as provided by X above
/// </returns>
public static async Task<ApiCallResult<X>> Put<T, X>(T data, string apiUrl) where X : class
{
var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
try
{
//json string
var jsonString = JsonConvert.SerializeObject(data);
using (var client = new HttpClient())
{
var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
var response = await client.PutAsync(apiUrl, httpContent);
var jsonResponseString = await response.Content.ReadAsStringAsync();
//fill
if (response.IsSuccessStatusCode)
{
//deserialize
if (!typeof(X).Equals(typeof(string)))
{
apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
}
apiCallResult.IsError = false;
}
else
{
try
{
ApiErrorMessage myMessage = JsonConvert.DeserializeObject<ApiErrorMessage>(jsonResponseString);
if (!string.IsNullOrWhiteSpace(myMessage?.Message))
{
jsonResponseString = myMessage.Message;
}
}
catch (Exception e)
{ }
jsonResponseString = jsonResponseString.Trim('"');
}
apiCallResult.StatusCode = response.StatusCode;
apiCallResult.ReasonPhrase = response.ReasonPhrase;
apiCallResult.Message = jsonResponseString;
}
}
catch (Exception ex)
{
apiCallResult.IsException = true;
apiCallResult.Message = ex.Message;
}
return apiCallResult;
}
/// <summary>
/// Performs Delete and returns ApiCallResult
/// </summary>
/// <typeparam name="X">return model by API, could be X, List X, string. Usually you'll only get Ok result etc for delete, so specify string </typeparam>
/// <param name="apiUrl">api full URL including the Id like http://localhost:65152/API/Test/12345 if executing custom action, provide that as well </param>
/// <returns>
/// ApiCallResult
/// HttpStatusCode StatusCode: status code returned by the API
/// string ReasonPhrase: reason phrase returned by the API
/// bool IsError: true/false
/// bool IsException: true/false
/// string Message: error message, exception message, or result of OK etc results by API
/// X ResponseObject: will only be available if api is returning a model (should not), in most cases it will not be available. Could be X, List X or string as provided by X above
/// </returns>
public static async Task<ApiCallResult<X>> Delete<X>(string apiUrl) where X : class
{
var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
try
{
using (var client = new HttpClient())
{
var response = await client.DeleteAsync(apiUrl);
var jsonResponseString = await response.Content.ReadAsStringAsync();
//fill
if (response.IsSuccessStatusCode)
{
//deserialize
if (!typeof(X).Equals(typeof(string)))
{
apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
}
apiCallResult.IsError = false;
}
else
{
try
{
ApiErrorMessage myMessage = JsonConvert.DeserializeObject<ApiErrorMessage>(jsonResponseString);
if (!string.IsNullOrWhiteSpace(myMessage?.Message))
{
jsonResponseString = myMessage.Message;
}
}
catch (Exception e)
{ }
jsonResponseString = jsonResponseString.Trim('"');
}
apiCallResult.StatusCode = response.StatusCode;
apiCallResult.ReasonPhrase = response.ReasonPhrase;
apiCallResult.Message = jsonResponseString;
}
}
catch (Exception ex)
{
apiCallResult.IsException = true;
apiCallResult.Message = ex.Message;
}
return apiCallResult;
}
/// <summary>
/// Performs Get and returns ApiCallResult
/// </summary>
/// <typeparam name="X">return model by API, could be X, List X, string. </typeparam>
/// <param name="apiUrl">api full URL </param>
/// <returns>
/// ApiCallResult
/// HttpStatusCode StatusCode: status code returned by the API
/// string ReasonPhrase: reason phrase returned by the API
/// bool IsError: true/false
/// bool IsException: true/false
/// string Message: error message, exception message, or result of OK etc results by API
/// X ResponseObject: Could be X, List X or string as provided by X above
/// </returns>
public static async Task<ApiCallResult<X>> Get<X>(string apiUrl) where X : class
{
var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
try
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(apiUrl);
var jsonResponseString = await response.Content.ReadAsStringAsync();
//fill
if (response.IsSuccessStatusCode)
{
//deserialize
if (!typeof(X).Equals(typeof(string)))
{
apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
}
apiCallResult.IsError = false;
}
else
{
try
{
ApiErrorMessage myMessage = JsonConvert.DeserializeObject<ApiErrorMessage>(jsonResponseString);
if (!string.IsNullOrWhiteSpace(myMessage?.Message))
{
jsonResponseString = myMessage.Message;
}
}
catch (Exception e)
{ }
jsonResponseString = jsonResponseString.Trim('"');
}
apiCallResult.StatusCode = response.StatusCode;
apiCallResult.ReasonPhrase = response.ReasonPhrase;
apiCallResult.Message = jsonResponseString;
}
}
catch (Exception ex)
{
apiCallResult.IsException = true;
apiCallResult.Message = ex.Message;
}
return apiCallResult;
}
}
Two changes worked...
Had to change the process sub to Function
In Main introduced .Wait()
Module EDIDownloaderModule
Sub Main(args As String())
ProcessApi().Wait()
End Sub
Private Async Function ProcessApi() As Task(Of Boolean)
Dim isRun As Boolean = Await TestHelper.ProcessApi()
Return isRun
End Function
End Module

WCF Duplex client idle wont update userinterface

I have clients thats is connected to a duplex wcf service 24/6 it is restarted every sunday.
In the client iam using a listview to display some information.
The listview itemssource is binded to a custom ObservableCollection.
The client is calling a keepalive method every minute to the wcf service.
My problem here is the client works fine when there is activity in the client. But when there is no activity and the application just run the keepalive method for about 10-16 hours. And iam trying to add and remove data to the listview it seems that nothing works. But the wcf service logging the method add and remove is working fine. Its like the userinterface isnt updating. When i restart the application everything works fine.
how do i solve this problem ?
My custom ObservableCollection object code :
public class ObservableOrderResponseQueue : INotifyCollectionChanged, IEnumerable<OrderResponse>
{
public event NotifyCollectionChangedEventHandler CollectionChanged = (o, e) => { };
private List<OrderResponse> _list = new List<OrderResponse>();
/// <summary>
/// Adds to the list.
/// </summary>
/// <param name="orderResponse">OrderResponse.</param>
public void Add(OrderResponse orderResponse)
{
//Only 6 items in list is possible if more then 6 remove the first item.
if (_list.Count >= 6)
{
RemoveAt(0);
}
this._list.Add(orderResponse);
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, orderResponse, (_list.Count - 1)));
}
/// <summary>
/// Remove from list.
/// </summary>
/// <param name="index">Item index to remove.</param>
public void RemoveAt(int index)
{
OrderResponse order = this._list[index];
this._list.RemoveAt(index);
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, order, index));
}
/// <summary>
/// Remove from list.
/// </summary>
/// <param name="orderResponse">Item to be removed.</param>
public void Remove(OrderResponse orderResponse)
{
if (_list.Count == 0) return;
var item = _list.Where(o => o.OrderDetail.TrayCode == orderResponse.OrderDetail.TrayCode).FirstOrDefault();
int index = _list.IndexOf(item);
if (index == -1) return;
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
this._list.RemoveAt(index);
}
#region IEnumerable<OrderResponse> Members
public IEnumerator<OrderResponse> GetEnumerator()
{
return _list.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return _list.GetEnumerator();
}
#endregion
}
Here is how i bind the usercontrol to the class.
//Set up client callbacks events
App.clientBack.ClientNotified += new ClientNotifiedEventHandler(clientBack_ClientNotified);
App.clientBack.AddToDisplayEvent += new AddToDisplayEventHandler(clientBack_AddToDisplayEvent);
App.clientBack.RemoveFromDisplayEvent += new RemoveFromDisplayEventHandler(clientBack_RemoveFromDisplayEvent);
App.clientBack.UpdateQueueDisplayEvent += new UpdateQueueDisplayEventHandler(clientBack_UpdateQueueDisplayEvent);
//Show one chair or many.
if (_settings.IsOneChair)
{
userControlOneChair.ItemSource = _queueProductionItems;
}
else
{
userControlChairs.ItemsSource = _queueProductionItems;
}
Remove and add methods
void clientBack_RemoveFromDisplayEvent(object sender, RemoveFromDisplayEventArgs e)
{
try
{
_logger.Info("Remove from display.");
userControlChairs.Dispatcher.Invoke((Action)(() =>
{
_queueProductionItems.Remove(e.OrderResponse);
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void clientBack_AddToDisplayEvent(object sender, AddToDisplayEventArgs e)
{
try
{
_logger.Info("Add to display.");
userControlChairs.Dispatcher.Invoke((Action)(() =>
{
_queueProductionItems.Add(e.OrderResponse);
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Thanks for help!
What i did was implementing a Heartbeat mechanism. And it all worked out.

Async CTP for a PostSubmitter with Cancelltion Support (CancellationTokenSource) and Progress report

fellow devs!
I have a class for posting to website using a POST or GET and read the response. It's all Async now and doesn't cause the UI to hang.
I need to upgrade it to handle cancellation now. All the Async methods being used are NOT accepting the cancellation token. I need to understand why and what are my alternatives. If its possible, should i create the CancellationTokenSource object within the class or parametrize it from the UI?
Secondly, I need to expose the progress of the PostData() method. How would I do that?
The class:
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
using System.Collections.Generic;
using RESTClient.Core.UploadFile;
using System.Threading;
namespace RESTClient.Core {
/// <summary>
/// Submits post data to a url.
/// </summary>
public class PostSubmitter {
#region Backing Store
private string _URL = string.Empty;
private NameValueCollection _PostValues = new NameValueCollection();
private PostTypeEnum _PostType = PostTypeEnum.GET;
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
public PostSubmitter() {
}
/// <summary>
/// Constructor that accepts a url as a parameter
/// </summary>
/// <param name="url">The url where the post will be submitted to.</param>
public PostSubmitter(string url)
: this() {
_URL = url;
}
/// <summary>
/// Constructor allowing the setting of the url and items to post.
/// </summary>
/// <param name="url">the url for the post.</param>
/// <param name="values">The values for the post.</param>
public PostSubmitter(string url, NameValueCollection values)
: this(url) {
_PostValues = values;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the url to submit the post to.
/// </summary>
public string Url {
get {
return _URL;
}
set {
_URL = value;
}
}
/// <summary>
/// Gets or sets the name value collection of items to post.
/// </summary>
public NameValueCollection PostItems {
get {
return _PostValues;
}
set {
_PostValues = value;
}
}
/// <summary>
/// Gets or sets the type of action to perform against the url.
/// </summary>
public PostTypeEnum Type {
get {
return _PostType;
}
set {
_PostType = value;
}
}
#endregion
/// <summary>
/// Posts the supplied data to specified url.
/// </summary>
/// <returns>a string containing the result of the post.</returns>
public async Task<String> Post() {
StringBuilder parameters = new StringBuilder();
for (int i = 0; i < _PostValues.Count; i++) {
EncodeAndAddItem(ref parameters, _PostValues.GetKey(i), _PostValues[i]);
}
string result = await PostData(_URL, parameters.ToString());
return result;
}
/// <summary>
/// Posts the supplied data to specified url.
/// </summary>
/// <param name="url">The url to post to.</param>
/// <returns>a string containing the result of the post.</returns>
public async Task<String> Post(string url) {
_URL = url;
return await this.Post();
}
/// <summary>
/// Posts the supplied data to specified url.
/// </summary>
/// <param name="url">The url to post to.</param>
/// <param name="values">The values to post.</param>
/// <returns>a string containing the result of the post.</returns>
public async Task<String> Post(string url, NameValueCollection values) {
_PostValues = values;
return await this.Post(url);
}
/// <summary>
/// Posts data to a specified url. Note that this assumes that you have already url encoded the post data.
/// </summary>
/// <param name="postData">The data to post.</param>
/// <param name="url">the url to post to.</param>
/// <returns>Returns the result of the post.</returns>
private async Task<String> PostData(string url, string postData) {
HttpWebRequest request = null;
if (_PostType == PostTypeEnum.POST) {
Uri uri = new Uri(url);
request = WebRequest.Create(uri) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postData.Length;
using (Stream writeStream = await request.GetRequestStreamAsync()) {
UTF8Encoding encoding = new UTF8Encoding();
byte[] bytes = encoding.GetBytes(postData);
writeStream.Write(bytes, 0, bytes.Length);
}
}
else {
Uri uri = new Uri(url + "?" + postData);
request = WebRequest.Create(uri) as HttpWebRequest;
request.Method = "GET";
}
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync()) {
using (Stream responseStream = response.GetResponseStream()) {
using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8)) {
return await readStream.ReadToEndAsync();
}
}
}
}
/// <summary>
/// Encodes an item and ads it to the string.
/// </summary>
/// <param name="baseRequest">The previously encoded data.</param>
/// <param name="dataItem">The data to encode.</param>
/// <returns>A string containing the old data and the previously encoded data.</returns>
private void EncodeAndAddItem(ref StringBuilder baseRequest, string key, string dataItem) {
if (baseRequest == null) {
baseRequest = new StringBuilder();
}
if (baseRequest.Length != 0) {
baseRequest.Append("&");
}
baseRequest.Append(key);
baseRequest.Append("=");
baseRequest.Append(HttpUtility.UrlEncode(dataItem));
}
public async void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc) {
//log.Debug(string.Format("Uploading {0} to {1}", file, url));
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
HttpWebRequest wr = WebRequest.Create(url) as HttpWebRequest;
wr.ContentType = "multipart/form-data; boundary=" + boundary;
wr.Method = "POST";
wr.KeepAlive = true;
wr.Credentials = CredentialCache.DefaultCredentials;
Stream rs = await wr.GetRequestStreamAsync();
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
foreach (string key in nvc.Keys) {
await rs.WriteAsync(boundarybytes, 0, boundarybytes.Length);
string formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = Encoding.UTF8.GetBytes(formitem);
await rs.WriteAsync(formitembytes, 0, formitembytes.Length);
}
await rs.WriteAsync(boundarybytes, 0, boundarybytes.Length);
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
string header = string.Format(headerTemplate, paramName, file, contentType);
byte[] headerbytes = Encoding.UTF8.GetBytes(header);
rs.WriteAsync(headerbytes, 0, headerbytes.Length);
FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0) {
await rs.WriteAsync(buffer, 0, bytesRead);
}
fileStream.Close();
byte[] trailer = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
await rs.WriteAsync(trailer, 0, trailer.Length);
rs.Close();
WebResponse wresp = null;
try {
wresp = await wr.GetResponseAsync();
Stream stream2 = wresp.GetResponseStream();
StreamReader reader2 = new StreamReader(stream2);
//log.Debug(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd()));
}
catch (Exception ex) {
//log.Error("Error uploading file", ex);
if (wresp != null) {
wresp.Close();
wresp = null;
}
}
finally {
wr = null;
}
/**
NameValueCollection nvc = new NameValueCollection();
nvc.Add("id", "TTR");
nvc.Add("btn-submit-photo", "Upload");
HttpUploadFile("http://your.server.com/upload", #"C:\test\test.jpg", "file", "image/jpeg", nvc);
**/
}
public async Task<String> ExecutePostRequest(Uri url, Dictionary<string, string> postData, FileInfo fileToUpload, string fileMimeType, string fileFormKey) {
HttpWebRequest request = WebRequest.Create(url.AbsoluteUri) as HttpWebRequest;
request.Method = "POST";
request.KeepAlive = true;
String boundary = Utility.CreateFormDataBoundary();
request.ContentType = "multipart/form-data; boundary=" + boundary;
Stream requestStream = await request.GetRequestStreamAsync();
postData.WriteMultipartFormData(requestStream, boundary);
if (fileToUpload != null) {
//TODO: Need async here...
fileToUpload.WriteMultipartFormData(requestStream, boundary, fileMimeType, fileFormKey);
}
byte[] endBytes = Encoding.UTF8.GetBytes("--" + boundary + "--");
await requestStream.WriteAsync(endBytes, 0, endBytes.Length);
requestStream.Close();
using (WebResponse response = await request.GetResponseAsync()) {
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
return await reader.ReadToEndAsync();
}
}
}
}
}
Note: There are three method in the end that are for file uploading. I still need to figure then out and before I do, I need to understand the Cancellation and Progress reporting.
Related question Async CTP for a PostSubmitter
Any help would be much appreciated.
You support progress and cancellation by taking IProgress<T> and CancellationToken parameters.
For cancellation, periodically check whether cancellation has been requested by calling CancellationToken.ThrowIfCancellationRequested. For more information, see Cancellation on MSDN.
For progress, you need to first decide what kind of "progress" makes sense. E.g., if "progress" is just a number of bytes transferred, then you can use IProgress<int>. Once you've decided on your progress type, then call IProgress<T>.Report to report the progress. There are two things to be aware of for IProgress<T>:
The IProgress<T> parameter may be null.
IProgress<T>.Report operates asynchronously. This means that you must either: A) use a value type for T in IProgress<T>; B) perform a deep copy of every T object passed to IProgress<T>.Report; or C) create a new T object each time you call IProgress<T>.Report.

The remote server returned an error: (400) Bad Request.

I am trying to post a message on a Facebook user's profile using Facebook API. It happens sometimes after authentication to send a message but the second time it always returns this error (just to mention that I use this for asp.net webapplication):
An error occurred:The remote server returned an error: (400) Bad Request.
System.Net.WebException: The remote server returned an error: (400) Bad Request. at System.Net.HttpWebRequest.GetResponse() at Facebook.FacebookAPI.MakeRequest(Uri url, HttpVerb httpVerb, Dictionary`2 args) in \wwwroot\App_Code\FacebookAPI.cs:line 185 at Facebook.FacebookAPI.Call(String relativePath, HttpVerb httpVerb, Dictionary`2 args) in \wwwroot\App_Code\FacebookAPI.cs:line 134 at Facebook.FacebookAPI.Post(String relativePath, Dictionary`2 args) in \wwwroot\App_Code\FacebookAPI.cs:line 107 at Views_Publish_to_facebook.btnSend_Click(Object sender, EventArgs e) in \wwwroot\Views\MarketingTool\Publish_to_facebook.aspx.cs:line 90
What could I do?
this is the login button
<fb:login-button autologoutlink='true' perms='read_stream,publish_stream,offline_access' onlogin='window.location.reload()'>
</fb:login-button>
Facebook C# SDK
This is my Facebook API code.
/*
* Copyright 2010 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Web;
using System.Web.Script.Serialization;
namespace Facebook
{
enum HttpVerb
{
GET,
POST,
DELETE
}
/// <summary>
/// Wrapper around the Facebook Graph API.
/// </summary>
public class FacebookAPI
{
/// <summary>
/// The access token used to authenticate API calls.
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// Create a new instance of the API, with public access only.
/// </summary>
public FacebookAPI()
: this(null) { }
/// <summary>
/// Create a new instance of the API, using the given token to
/// authenticate.
/// </summary>
/// <param name="token">The access token used for
/// authentication</param>
public FacebookAPI(string token)
{
AccessToken = token;
}
/// <summary>
/// Makes a Facebook Graph API GET request.
/// </summary>
/// <param name="relativePath">The path for the call,
/// e.g. /username</param>
public JSONObject Get(string relativePath)
{
return Call(relativePath, HttpVerb.GET, null);
}
/// <summary>
/// Makes a Facebook Graph API GET request.
/// </summary>
/// <param name="relativePath">The path for the call,
/// e.g. /username</param>
/// <param name="args">A dictionary of key/value pairs that
/// will get passed as query arguments.</param>
//public JSONObject Get(string relativePath,
// Dictionary<string, string> args)
public JSONObject Get(string relativePath,
Dictionary<string, string> args)
{
return Call(relativePath, HttpVerb.GET, args);
}
/// <summary>
/// Makes a Facebook Graph API DELETE request.
/// </summary>
/// <param name="relativePath">The path for the call,
/// e.g. /username</param>
public JSONObject Delete(string relativePath)
{
return Call(relativePath, HttpVerb.DELETE, null);
}
/// <summary>
/// Makes a Facebook Graph API POST request.
/// </summary>
/// <param name="relativePath">The path for the call,
/// e.g. /username</param>
/// <param name="args">A dictionary of key/value pairs that
/// will get passed as query arguments. These determine
/// what will get set in the graph API.</param>
public JSONObject Post(string relativePath,
Dictionary<string, string> args)
{
return Call(relativePath, HttpVerb.POST, args);
}
/// <summary>
/// Makes a Facebook Graph API Call.
/// </summary>
/// <param name="relativePath">The path for the call,
/// e.g. /username</param>
/// <param name="httpVerb">The HTTP verb to use, e.g.
/// GET, POST, DELETE</param>
/// <param name="args">A dictionary of key/value pairs that
/// will get passed as query arguments.</param>
private JSONObject Call(string relativePath,
HttpVerb httpVerb,
Dictionary<string, string> args)
{
Uri baseURL = new Uri("https://graph.facebook.com");
//relativePath = "/me";
Uri url = new Uri(baseURL, relativePath);
if (args == null)
{
args = new Dictionary<string, string>();
}
if (!string.IsNullOrEmpty(AccessToken))
{
args["access_token"] = AccessToken;
}
JSONObject obj = JSONObject.CreateFromString(MakeRequest(url,
httpVerb,
args));
if (obj.IsDictionary && obj.Dictionary.ContainsKey("error"))
{
throw new FacebookAPIException(obj.Dictionary["error"]
.Dictionary["type"]
.String,
obj.Dictionary["error"]
.Dictionary["message"]
.String + " -> " + url + " " + httpVerb + " " + args);
}
return obj;
}
/// <summary>
/// Make an HTTP request, with the given query args
/// </summary>
/// <param name="url">The URL of the request</param>
/// <param name="verb">The HTTP verb to use</param>
/// <param name="args">Dictionary of key/value pairs that represents
/// the key/value pairs for the request</param>
private string MakeRequest(Uri url, HttpVerb httpVerb,
Dictionary<string, string> args)
{
if (args != null && args.Keys.Count > 0 && httpVerb == HttpVerb.GET)
{
url = new Uri(url.ToString() + EncodeDictionary(args, true));
}
//throw new Exception(url.AbsoluteUri + " ---- " + url.AbsolutePath);
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = httpVerb.ToString();
if (httpVerb == HttpVerb.POST)
{
string postData = EncodeDictionary(args, false);
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] postDataBytes = encoding.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postDataBytes.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(postDataBytes, 0, postDataBytes.Length);
requestStream.Close();
}
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
try
{
//using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
//throw new Exception(response.ResponseUri + " tra " + response.Server);
{
StreamReader reader
= new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
}
catch (WebException e)
{
throw new FacebookAPIException("Server Error", e.Message + " resp: " + request.GetResponse() + " " + response.ResponseUri + " tra " + response.Server);
}
}
/// <summary>
/// Encode a dictionary of key/value pairs as an HTTP query string.
/// </summary>
/// <param name="dict">The dictionary to encode</param>
/// <param name="questionMark">Whether or not to start it
/// with a question mark (for GET requests)</param>
private string EncodeDictionary(Dictionary<string, string> dict,
bool questionMark)
{
StringBuilder sb = new StringBuilder();
if (questionMark)
{
sb.Append("?");
}
foreach (KeyValuePair<string, string> kvp in dict)
{
sb.Append(HttpUtility.UrlEncode(kvp.Key));
sb.Append("=");
//NOTE: This line causes problems with access_token. The url encoding messes up the access_token, so for now I'm just adding it directly
//if the key == "access_token"
//sb.Append(HttpUtility.UrlEncode(kvp.Value));
if (kvp.Key.ToLower() == "access_token")
{
sb.Append(kvp.Value);
//sb.Append(HttpUtility.UrlEncode(HttpUtility.UrlDecode(kvp.Value)));
}
else
{
sb.Append(HttpUtility.UrlEncode(kvp.Value));
}
sb.Append("&");
}
sb.Remove(sb.Length - 1, 1); // Remove trailing &
return sb.ToString();
}
}
}

Resources