ASP.Net Asynchronous HTTP File Upload Handler - c#

I'm trying to make a file upload handler in C# that is asynchronous and can provide updates on progress of the file through AJAX asynchronous requests. Basically if the request is a POST it loads some information into the session and then starts the upload, if the request was a GET it returns the current state of the upload (bytes uploaded, total bytes, etc). I'm not entire sure that it needs to be an asynchronous handler but the files could be quite large so I thought that would work best. For the base async handler I used something very similar to the handler in this MSDN article. I've posted below some key sections of my code below. The issue I'm having is that I don't receive any of the GET information back until the POST has completed. I will mention that in this example I am using jQuery for GET requests and BlueImp for posting the file.
The HTML and JavaScript
<input id="somefile" type="file" />
$(function () {
name = 'MyUniqueId130';
var int = null;
$('#somefile').fileupload({
url: '/fileupload.axd?key='+name,
done: function (e, data) { clearInterval(int); }
});
$('#somefile').ajaxStart(function(){
int = setInterval(function(){
$.ajax({
url: '/fileupload.axd?key='+name,
dataType: 'json',
async: true
})
.done(function(e1, data1){
if(!e1.InProgress || e1.Complete || e1.Canceled)
clearInterval(int);
});
}, 10000)});
});
The Asynchronous Process Request Method just calls the correct method whether it's a POST or GET to one of the following then calls CompleteRequest to end the request:
private static void GetFilesStatuses(HttpContext context)
{
string key = context.Request.QueryString["key"];
//A dictionary of <string, UploadStatus> in the session
var Statuses = GetSessionStore(context);
UploadStatus ups;
if (!String.IsNullOrWhiteSpace(key))
{
if (Statuses.TryGetValue(key, out ups))
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.Write(CreateJson(ups));
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
}
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.Write(CreateJson(Statuses.Values));
}
}
private static void UploadFile(HttpContext context)
{
var Statuses = GetSessionStore(context);
string key = context.Request.QueryString["key"];
if (String.IsNullOrWhiteSpace(key))
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return;
}
HttpPostedFile file = context.Request.Files[0];
string extn = file.FileName.LastIndexOf('.') == -1 ? "" :
file.FileName.Substring(file.FileName.LastIndexOf('.'), (file.FileName.Length - file.FileName.LastIndexOf('.')));
string temp = GetTempFileName(path, extn);
UploadStatus status = new UploadStatus()
{
FileName = file.FileName,
TempFileName = temp,
Path = path,
Complete = false,
Canceled = false,
InProgress = false,
Success = true,
BytesLoaded = 0,
TotalBytes = file.ContentLength
};
Statuses.Add(key, status);
byte[] buffer = new byte[bufferSize];
int byteCount = 0;
using (var fStream = System.IO.File.OpenWrite(context.Request.MapPath(path + temp)))
{
uploads.Add(status);
while ((byteCount = file.InputStream.Read(buffer, 0, bufferSize)) > 0 && !status.Canceled)
{
status.InProgress = true;
status.BytesLoaded += byteCount;
fStream.Write(buffer, 0, byteCount);
}
status.Complete = !status.Canceled;
status.InProgress = false;
status.Success = true;
if (status.Canceled)
{
Statuses.Remove(temp);
}
context.Response.StatusCode = (int)HttpStatusCode.OK;
}
}
I've tried many things such as non-async handlers, async handlers, making sure the JavaScript is runnning async, but at this point I think I need some different eyes on the problem so thank you for any assistance anyone can provide.

I assume you're using the default ASP.Net Session manager and I see that you call GetSessionStore to get your session. Unfortunately the default Session manager serializes all requests when a call requires write access to the Session Store. This StackOverflow question and this MSDN arcle on Session State have some very useful information on Session State and it's locking behaviors.
Now, To take care of your problem, you're going to have to do a couple things which depend on whether you're using MVC controllers or if you're writing a custom IHttpHandler.
If you're writing your own IHttpHandler, make sure you do not have the IRequiresSessionState or IReadOnlySessionState interfaces added to your handler. In doing so, the pipeline will skip looking for a session and go straight to processing. context.Session will be null in this situation.
If you're using MVC to process the request, you'll need to decorate your controller class with the SessionState attribute passing in the SessionStateBehavior of SessionStateBehavior.Disabled.
In either case you won't be able to rely on the Session object to store your upload statuses. You can create a static ConcurrentDictionary keyed off of their SessionID (which you'll either need to pass in the upload query string or read the cookie yourself, calling Session.SessionId will just block you again) and store your upload statuses in there (which look like they're Concurrent* as well).
Another option would be to replace the SessionStateProvider with your own custom provider but that might be overkill in this situation.

Related

PayPal Order Request never gets back

Dears all,
I'm starting with payPal,
and I've tried to implement the standard sample provided with SDK (c#, FW 4.6.1)
here below my server-side method
public async static Task<PayPalHttp.HttpResponse> CreateOrder()
{
OrdersCreateRequest oRequest = new OrdersCreateRequest();
oRequest.Prefer("return=representation");
//System.Net.ServicePointManager.Expect100Continue = true;
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
oRequest.RequestBody(BuildRequestBodyWithMinimumFields());
//3. Call PayPal to set up a transaction
PayPalHttp.HttpClient oClient = PayPalClient.client();
oClient.SetConnectTimeout(new TimeSpan(0, 0, 0, 10, 0));
var oResponse = await oClient.Execute(oRequest);
var result = oResponse.Result<Order>();
return oResponse;
}
while here jquery call
paypal.Buttons({
style: {
shape: 'rect',
color: 'blue',
layout: 'vertical',
label: 'pay',
},
createOrder: function () {
return fetch('/shop/paypal_test.aspx/CreateOrder', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function (res) {
return res.json();
}).then(function (data) {
return data.orderID; // Use the same key name for order ID on the client and server
});
}
}).render('#paypal-button-container');
The issue is that the response to oClient.Execute never gets back.
PayPalClient has been built exactly as SDK sample.
Looking at PayPal API Calls Log, the API is called correctly and it is marked with a green flag.
Have you some ideas?
thank you in advance
Dears,
I've finally found a solution.
As said before, after API call, on developer.paypal web site the related call has been marked with a green flag, that means that the call has been made correctly.
Then, I've investigated on server code, and how the tasks are managed.
The asynchrounous call is embedded in a private method and it's called by a sync method that "waits" for the response.
In this way all works correctly
Here the snippet code that allows the routine to work and not to be stopped to "client.Execute(oRequest)".
[WebMethod(EnableSession = true)]
public static PayPalCheckoutSdk.Orders.Order SetupTransaction()
{
data.tessutigenovataddei.com.Order oOrder = null;
//save order in database.
oOrder = CreateOrderWithPaypal();
OrdersCreateRequest oRequest = new OrdersCreateRequest();
oRequest.Prefer("return=representation");
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
oRequest.RequestBody(BuildRequestBodyWithMinimumFields(oOrder));
PayPalHttp.HttpClient oClient = PayPalClient.client();
oClient.SetConnectTimeout(new TimeSpan(0, 0, 0, 10, 0));
var oResponseTask = SetupTransactionAsync(oClient, oRequest);
oResponseTask.Wait();
return oResponseTask.Result.Result<PayPalCheckoutSdk.Orders.Order>();
}
private async static Task<PayPalHttp.HttpResponse> SetupTransactionAsync(PayPalHttp.HttpClient client, OrdersCreateRequest request)
{
SynchronizationContext.SetSynchronizationContext(null);
PayPalHttp.HttpResponse oResponse = await client.Execute(request);
return oResponse;
}
thank you for the support
What do you mean "never gets back" ? You say there is a "green flag", does this mean there was an HTTP 2xx success response? Was there an id in the response body?
If you are forwarding the response body to your front end, the front end should not be looking for the data.orderID key, since there will be no such key unless you provide it. Here is a better sample to use for your front end, which reads data.id : https://developer.paypal.com/demo/checkout/#/pattern/server
Note that that sample also includes a fetch in the onApprove function. You need to implement such a route that will capture your order, otherwise there will be no transaction created.

Notify the View after async Task is completed in MVC

I have a view and I send data through ajax to an action in MVC. I want to be notified in the view only when the task is done. The problem I encounter is whether I'm notified before or if i wait untill the the task is done my view is blocked. Here it is un example of what i do:
function UploadFile(file) {
$.ajax({
url: '/Home/Upload',
type: 'POST',
data: file,
success: function (result) {
alert(result);
}
});
}
[HttpPost]
public async Task<ActionResult> Upload(httpPostedFileBase file)
{
NewFile service = new NewFile((long)Session["UserId"]);
Thread thread = new Thread(() =>service.UploadFile(file));
thread.Start();
return Json("File Uploaded", JsonRequestBehavior.AllowGet);
}
public async Task<string> UploadFile(HttpPostedFileBase file) // returns the file Id in Google Drive
{
Google.Apis.Drive.v3.Data.File uploadedFile = new Google.Apis.Drive.v3.Data.File();
try
{
if (file != null && file.ContentLength > 0)
{
// Check for credentials
Google.Apis.Drive.v3.DriveService service = Service;
// Uploads the file to the server in "~/GoogleDriveFiles" folder.
/*string path = Path.Combine(HttpContext.Current.Server.MapPath("~/GoogleDriveFiles"),*/ // TODO Doesn't work with Unit testing. Must be written manual for testing
//Path.GetFileName(file.FileName));
//file.SaveAs(path);
// Save the metadata of the file
var FileMetaData = new Google.Apis.Drive.v3.Data.File()
{
Name = Path.GetFileName(file.FileName),
MimeType = MimeMapping.GetMimeMapping(file.FileName),
Parents = new List<string>
{
GetReposFolderId()
}
};
// Create a request for upload
Google.Apis.Drive.v3.FilesResource.CreateMediaUpload request;
file.InputStream.Close();
// Create a stream using the file filepath and filling the request. Then upload.
using (var stream = new System.IO.FileStream(file.FileName, System.IO.FileMode.Open))
{
request = service.Files.Create(FileMetaData, stream, FileMetaData.MimeType);
request.Fields = "id";
await request.UploadAsync();//TODO Tuka bavi i ne mozesh da browswash prez papkite dokato ne go kachi na servara
}
uploadedFile = request.ResponseBody;
}
}
catch (Exception ex)
{
ErrorEngine.RuntimeExceptions runtimeExceptions = new ErrorEngine.RuntimeExceptions();
runtimeExceptions.ManageException(ex, this);
return ex.ToString();
}
return uploadedFile.Id;
}
I'm not very experienced with MVC and codding in general as you might thought. Can you help me with that please?
the short answer is you cant ....
if you do not want to wait for the processing... then it will never know...
UNLESS
either, you use a poller and ask every x time or use a two way comms.... which is quite a bit of coding but you could look at SingalR, then you could issue a msg and all listens can receive.
so the easiest.... for you... is to upload the file as one request and make that wait for the response.
Uploading and the action as to what to do can be separated, that's what many sites do.. you can use js, to have a progress bar as to how far it is with uploading the file to the server..but processing o the server unless you wait, there would be no way other than what i said above or variations there of.
options are poller or socket base tech, or simply wait for upload to and processing to complete.
The uploading and the processing can be separate calls with different responce and this is what most sites do...
Try this:
[HttpPost]
public async Task<JsonResult> Upload(httpPostedFileBase file)
{
try {
NewFile service = new NewFile((long)Session["UserId"]);
await Task.Run(() => service.UploadFile(file));
} catch (Exception e) {
Console.WriteLine(e);
}
return Json("File Uploaded", JsonRequestBehavior.AllowGet);
}

Added keys missing from HttpContext.Session (concurrency)

I am using .NET Core 3.1. I am wondering if HttpContext.Session.SetString(...) is thread-safe? For example, client makes two requests to the same controller action (Test) at the same time. Controller adds a string to the session (see example below). Will there be two, one or zero keys in the session at the end (ex. when I refresh the page)?
public IActionResult Test()
{
HttpContext.Session.SetString(Guid.NewGuid().ToString(), "test");
return Ok();
}
I am saving some values to session when using DevExtreme FileUploader. When I upload multiple files at once, component makes multiple requests at the same time and at the end, there are usually some keys missing from the session. I think there is some race condition going on.
ADDED: CLIENT CODE
I noticed that session keys are missing only if I use method: 'POST' (there is only 1 key). If I use method: 'GET', there are 3 keys (correct).
$(document).ready(function () {
var method = 'GET'; // works (3 keys)
//var method = 'POST'; // doesn't work (1 key)
$('#fire').on('click', function () {
$.when(
$.ajax({
url: 'Session/Test',
method: method,
success: function(){
console.log('response', 1);
}
}),
$.ajax({
url: 'Session/Test',
method: method,
success: function(){
console.log('response', 2);
}
}),
$.ajax({
url: 'Session/Test',
method: method,
success: function(){
console.log('response', 3);
}
})
).then(function () {
alert('Done');
});
});
});
Assume you use the default session implementation offered by ASP.Net Core.
In terms of HttpContext.Session:
HttpContext.Session returns an instance of DistributedSession, which internally uses a Dictionary<TKey, TValaue>. Dictionary is not thread safe, so if you access Session from multiple threads (e.g. Task.Run), it can cause unexpected results.
In terms of Session for different requests:
In ASP.Net Core, Session comes from ISessionStore, which has a transient lifetime. Meaning, Session object is not shared by requests. So if you have concurrent requests, each of which will have its own Session object.
In terms of race condition:
The default implementation of session reads/writes session state from/to .AspNetCore.Session cookie. This may cause race conditions.
Because Session is per client, so you might have race conditions when you have concurrent requests from the same client touching same bits and pieces in the same cookie / session state. The race condition however is not because of Session on the server side. It is actually caused by cookie management at client side.
Session state is non-locking. If two requests simultaneously attempt to modify the contents of a session, the last request overrides the first.
Consider this example:
Say you have a controller action which sets Session with provided value, and another controller action retrieves the value from Session and returns it in body:
[HttpGet]
[Route("create")]
public IActionResult CreateSession([FromQuery]string value)
{
HttpContext.Session.SetString("key", value);
return Ok();
}
[HttpGet]
[Route("get")]
public IActionResult ReturnSession([FromQuery] string expected)
{
var actual = HttpContext.Session.GetString("key");
return Ok(new { actual, expected });
}
If you test these actions with an HttpClient:
async Task TestSession(HttpClient client, string str)
{
await client.GetAsync($"https://localhost:5001/session/create?value={str}");
var r = await client.GetAsync($"https://localhost:5001/session/get?expected={str}");
var session = await r.Content.ReadAsStringAsync();
Console.WriteLine(session);
}
using (var client = new HttpClient())
{
await TestSession(client, "abc");
}
The output should look like:
{"actual":"abc","expected":"abc"}
Problem raises when you have concurrent requests from the same client:
using (var client = new HttpClient())
{
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
{
var str = i.ToString();
tasks.Add(Task.Run(() => TestSession(client, str)));
}
await Task.WhenAll(tasks);
}
The output looks like:
{"actual":"2","expected":"1"}
{"actual":"3","expected":"6"}
{"actual":"4","expected":"5"}
{"actual":"4","expected":"3"}
{"actual":"4","expected":"2"}
{"actual":"8","expected":"4"}
{"actual":"7","expected":"8"}
{"actual":"7","expected":"7"}
{"actual":"9","expected":"0"}
{"actual":"9","expected":"9"}
In the above case, session state was changed by request 3, between create and get of request 6, meaning it is likely request 6 cannot see its session state correctly.
To avoid this issue, you could use different HttpClient for each batch.
No, dotnet collections are not thread safe, and neither is session.
If you are sharing the same session over more than one thread you should treat it as readonly

How to custom message on browser when internet is not connected

I want to show custom message when my web application is failed to connect to internet.
Currently when my internet connectivity fails then the browser shows "unable to connect" or "server not found". so I want to show my custom message on my page.
I have written this :
bool bb = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();
if (bb == true)
{
//custom message
}
else
//meesage
But still it is not working.
How can I show that?
I think you are expecting internet connectivity from browser, if so use navigator.onLine;
<script type="text/javascript">
var isOnline = navigator.onLine;
if (!isOnline)
alert("Your custom message for no internet connectivity!!!");
</script>
Periodically call from client to server, and if there is no answer or not expecting answer - show error message.
for web forms:
create handler with implemetation of ProcessRequest like this:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Ok");
}
for mvc:
create action with simple result like this:
public ActionResult CheckInternetConnection()
{
return Json("Ok");
}
of course, this request handlers should not require any authorization or another pretreatment
then create js timer and method for request
var maxTime = <your interval time>;
var timer;
//write own method for check request
function performRequest() {
var xhr = new XMLHttpRequest();
// reserve 1 sec for timeout function execution
// if we didn't - you can get a situation when
// simultaneously performing more than 1 request
xhr.timeout = maxTime - 1000;
xhr.ontimeout = function {
//waiting to long
alert('<your message>');
window.clearInterval(timer);
};
xhr.open('GET', '<url of your check connection handler>', true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4)
return;
if (xhr.responseText !== 'OK') {
//Can't access to handler
alert('<your message>');
window.clearInterval(timer);
}
}
}
//after page load start request timer
document.onload += function () {
timer = window.setInterval(performRequest, maxTime);
}
I haven't debug this code.

Update Twitter Status in C#

I'm trying to update a user's Twitter status from my C# application.
I searched the web and found several possibilities, but I'm a bit confused by the recent (?) change in Twitter's authentication process. I also found what seems to be a relevant StackOverflow post, but it simply does not answer my question because it's ultra-specific regading a code snippet that does not work.
I'm attempting to reach the REST API and not the Search API, which means I should live up to the stricter OAuth authentication.
I looked at two solutions. The Twitterizer Framework worked fine, but it's an external DLL and I would rather use source code. Just as an example, the code using it is very clear and looks like so:
Twitter twitter = new Twitter("username", "password");
twitter.Status.Update("Hello World!");
I also examined Yedda's Twitter library, but this one failed on what I believe to be the authentication process, when trying basically the same code as above (Yedda expects the username and password in the status update itself but everything else is supposed to be the same).
Since I could not find a clear cut answer on the web, I'm bringing it to StackOverflow.
What's the simplest way to get a Twitter status update working in a C# application, without external DLL dependency?
Thanks
If you like the Twitterizer Framework but just don't like not having the source, why not download the source? (Or browse it if you just want to see what it's doing...)
I'm not a fan of re-inventing the wheel, especially when it comes to products that already exist that provide 100% of the sought functionality. I actually have the source code for Twitterizer running side by side my ASP.NET MVC application just so that I could make any necessary changes...
If you really don't want the DLL reference to exist, here is an example on how to code the updates in C#. Check this out from dreamincode.
/*
* A function to post an update to Twitter programmatically
* Author: Danny Battison
* Contact: gabehabe#hotmail.com
*/
/// <summary>
/// Post an update to a Twitter acount
/// </summary>
/// <param name="username">The username of the account</param>
/// <param name="password">The password of the account</param>
/// <param name="tweet">The status to post</param>
public static void PostTweet(string username, string password, string tweet)
{
try {
// encode the username/password
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
// determine what we want to upload as a status
byte[] bytes = System.Text.Encoding.ASCII.GetBytes("status=" + tweet);
// connect with the update page
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
// set the method to POST
request.Method="POST";
request.ServicePoint.Expect100Continue = false; // thanks to argodev for this recent change!
// set the authorisation levels
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType="application/x-www-form-urlencoded";
// set the length of the content
request.ContentLength = bytes.Length;
// set up the stream
Stream reqStream = request.GetRequestStream();
// write to the stream
reqStream.Write(bytes, 0, bytes.Length);
// close the stream
reqStream.Close();
} catch (Exception ex) {/* DO NOTHING */}
}
Another Twitter library I have used sucessfully is TweetSharp, which provides a fluent API.
The source code is available at Google code. Why don't you want to use a dll? That is by far the easiest way to include a library in a project.
The simplest way to post stuff to twitter is to use basic authentication , which isn't very strong.
static void PostTweet(string username, string password, string tweet)
{
// Create a webclient with the twitter account credentials, which will be used to set the HTTP header for basic authentication
WebClient client = new WebClient { Credentials = new NetworkCredential { UserName = username, Password = password } };
// Don't wait to receive a 100 Continue HTTP response from the server before sending out the message body
ServicePointManager.Expect100Continue = false;
// Construct the message body
byte[] messageBody = Encoding.ASCII.GetBytes("status=" + tweet);
// Send the HTTP headers and message body (a.k.a. Post the data)
client.UploadData("http://twitter.com/statuses/update.xml", messageBody);
}
Try LINQ To Twitter. Find LINQ To Twitter update status with media complete code example that works with Twitter REST API V1.1. Solution is also available for download.
LINQ To Twitter Code Sample
var twitterCtx = new TwitterContext(auth);
string status = "Testing TweetWithMedia #Linq2Twitter " +
DateTime.Now.ToString(CultureInfo.InvariantCulture);
const bool PossiblySensitive = false;
const decimal Latitude = StatusExtensions.NoCoordinate;
const decimal Longitude = StatusExtensions.NoCoordinate;
const bool DisplayCoordinates = false;
string ReplaceThisWithYourImageLocation = Server.MapPath("~/test.jpg");
var mediaItems =
new List<media>
{
new Media
{
Data = Utilities.GetFileBytes(ReplaceThisWithYourImageLocation),
FileName = "test.jpg",
ContentType = MediaContentType.Jpeg
}
};
Status tweet = twitterCtx.TweetWithMedia(
status, PossiblySensitive, Latitude, Longitude,
null, DisplayCoordinates, mediaItems, null);
Try TweetSharp . Find TweetSharp update status with media complete code example works with Twitter REST API V1.1. Solution is also available for download.
TweetSharp Code Sample
//if you want status update only uncomment the below line of code instead
//var result = tService.SendTweet(new SendTweetOptions { Status = Guid.NewGuid().ToString() });
Bitmap img = new Bitmap(Server.MapPath("~/test.jpg"));
if (img != null)
{
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Seek(0, SeekOrigin.Begin);
Dictionary<string, Stream> images = new Dictionary<string, Stream>{{"mypicture", ms}};
//Twitter compares status contents and rejects dublicated status messages.
//Therefore in order to create a unique message dynamically, a generic guid has been used
var result = tService.SendTweetWithMedia(new SendTweetWithMediaOptions { Status = Guid.NewGuid().ToString(), Images = images });
if (result != null && result.Id > 0)
{
Response.Redirect("https://twitter.com");
}
else
{
Response.Write("fails to update status");
}
}
Here's another solution with minimal code using the excellent AsyncOAuth Nuget package and Microsoft's HttpClient. This solution also assumes you're posting on your own behalf so you have your access token key/secret already, however even if you don't the flow is pretty easy (see AsyncOauth docs).
using System.Threading.Tasks;
using AsyncOAuth;
using System.Net.Http;
using System.Security.Cryptography;
public class TwitterClient
{
private readonly HttpClient _httpClient;
public TwitterClient()
{
// See AsyncOAuth docs (differs for WinRT)
OAuthUtility.ComputeHash = (key, buffer) =>
{
using (var hmac = new HMACSHA1(key))
{
return hmac.ComputeHash(buffer);
}
};
// Best to store secrets outside app (Azure Portal/etc.)
_httpClient = OAuthUtility.CreateOAuthClient(
AppSettings.TwitterAppId, AppSettings.TwitterAppSecret,
new AccessToken(AppSettings.TwitterAccessTokenKey, AppSettings.TwitterAccessTokenSecret));
}
public async Task UpdateStatus(string status)
{
try
{
var content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
{"status", status}
});
var response = await _httpClient.PostAsync("https://api.twitter.com/1.1/statuses/update.json", content);
if (response.IsSuccessStatusCode)
{
// OK
}
else
{
// Not OK
}
}
catch (Exception ex)
{
// Log ex
}
}
}
This works on all platforms due to HttpClient's nature. I use this method myself on Windows Phone 7/8 for a completely different service.

Categories