Websockets using OWIN - c#

All the examples using Microsoft WebSockets over a web-api that I've seen so far use IIS, the implementation is on the get method the HTTP connection is upgraded to a websocket and an instance of websocket handler is passed to the HTTPContext
public HttpResponseMessage Get() {
if (HttpContext.Current.IsWebSocketRequest) {
var noteHandler = new NoteSocketHandler();
HttpContext.Current.AcceptWebSocketRequest(noteHandler);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
What am trying to achieve is to do the same on an OWIN pipeline. The problem am facing is the connection is being upgraded to use Websockets but it is not utilizing my websocket handler. Where am I going wrong? Please suggest.
Controller utilizing OwinContext (Followed the example WebSockets in Nancy using OWIN),
public HttpResponseMessage Get() {
IOwinContext owinContext = Request.GetOwinContext();
WebSocketAccept acceptToken = owinContext.Get<WebSocketAccept>("websocket.Accept");
if (acceptToken != null) {
var requestHeaders = GetValue<IDictionary<string, string[]>>(owinContext.Environment, "owin.RequestHeaders");
Dictionary<string, object> acceptOptions = null;
string[] subProtocols;
if (requestHeaders.TryGetValue("Sec-WebSocket-Protocol", out subProtocols) && subProtocols.Length > 0) {
acceptOptions = new Dictionary<string, object>();
// Select the first one from the client
acceptOptions.Add("websocket.SubProtocol", subProtocols[0].Split(',').First().Trim());
}
acceptToken(acceptOptions, async wsEnv => {
var wsSendAsync = (WebSocketSendAsync)wsEnv["websocket.SendAsync"];
var wsRecieveAsync = (WebSocketReceiveAsync)wsEnv["websocket.ReceiveAsync"];
var wsCloseAsync = (WebSocketCloseAsync)wsEnv["websocket.CloseAsync"];
var wsCallCancelled = (CancellationToken)wsEnv["websocket.CallCancelled"];
//should I pass the handler to an event?
var handler = new NoteSocketHAndler();
});
} else {
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
Handler Code:
using System;
using Socket = Microsoft.Web.WebSockets;
using Newtonsoft.Json;
public class NoteSocketHandler : Socket.WebSocketHandler {
private static Socket.WebSocketCollection connections = new Socket.WebSocketCollection();
public NoteSocketHandler() {
}
public override void OnOpen() {
connections.Add(this);
}
public override void OnClose() {
connections.Remove(this);
}
public override void OnMessage(string message) {
ChatMessage chatMessage = JsonConvert.DeserializeObject<ChatMessage>(message);
foreach (var connection in connections) {
connection.Send(message);
}
}
}

I finally figured out how to resolve the issue. You can find the code below, also I've written a basic app which uses websockets on OWIN.
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Owin;
namespace NoteApp.WebService.Controller {
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NoteApp.WebService.Handler;
using WebSocketAccept = System.Action<
System.Collections.Generic.IDictionary<string, object>, // WebSocket Accept parameters
System.Func< // WebSocketFunc callback
System.Collections.Generic.IDictionary<string, object>, // WebSocket environment
System.Threading.Tasks.Task>>;
using WebSocketCloseAsync = System.Func<
int, // closeStatus
string, // closeDescription
System.Threading.CancellationToken, // cancel
System.Threading.Tasks.Task>;
using WebSocketReceiveAsync = System.Func<
System.ArraySegment<byte>, // data
System.Threading.CancellationToken, // cancel
System.Threading.Tasks.Task<
System.Tuple< // WebSocketReceiveTuple
int, // messageType
bool, // endOfMessage
int>>>; // count
// closeStatusDescription
using WebSocketReceiveResult = System.Tuple<int, bool, int>;
using WebSocketSendAsync = System.Func<
System.ArraySegment<byte>, // data
int, // message type
bool, // end of message
System.Threading.CancellationToken, // cancel
System.Threading.Tasks.Task>;
public class NoteController : ApiController {
public HttpResponseMessage Get() {
IOwinContext owinContext = Request.GetOwinContext();
WebSocketAccept acceptToken = owinContext.Get<WebSocketAccept>("websocket.Accept");
if (acceptToken != null) {
var requestHeaders = GetValue<IDictionary<string, string[]>>(owinContext.Environment, "owin.RequestHeaders");
Dictionary<string, object> acceptOptions = null;
string[] subProtocols;
if (requestHeaders.TryGetValue("Sec-WebSocket-Protocol", out subProtocols) && subProtocols.Length > 0) {
acceptOptions = new Dictionary<string, object>();
// Select the first one from the client
acceptOptions.Add("websocket.SubProtocol", subProtocols[0].Split(',').First().Trim());
}
acceptToken(acceptOptions, ProcessSocketConnection);
} else {
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
private async Task ProcessSocketConnection(IDictionary<string, object> wsEnv) {
var wsSendAsync = (WebSocketSendAsync)wsEnv["websocket.SendAsync"];
var wsCloseAsync = (WebSocketCloseAsync)wsEnv["websocket.CloseAsync"];
var wsCallCancelled = (CancellationToken)wsEnv["websocket.CallCancelled"];
var wsRecieveAsync = (WebSocketReceiveAsync)wsEnv["websocket.ReceiveAsync"];
//pass the sendasync tuple and the cancellation token to the handler. The handler uses the sendasync method to send message. Each connected client has access to this
var handler = new NoteSocketHandler(wsSendAsync, CancellationToken.None);
handler.OnOpen();
var buffer = new ArraySegment<byte>(new byte[100]);
try {
object status;
while (!wsEnv.TryGetValue("websocket.ClientCloseStatus", out status) || (int)status == 0) {
WebSocketReceiveResult webSocketResultTuple = await wsRecieveAsync(buffer, CancellationToken.None);
int count = webSocketResultTuple.Item3;
handler.OnMessage(Encoding.UTF8.GetString(buffer.Array, 0, count));
}
} catch (Exception ex) {
Console.WriteLine(ex.Message);
throw ex;
}
handler.OnClose();
await wsCloseAsync((int)WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}
T GetValue<T>(IDictionary<string, object> env, string key) {
object value;
return env.TryGetValue(key, out value) && value is T ? (T)value : default(T);
}
}
}

Related

.NET Core Mediatr Usage not all code paths return a value

In my API, I am trying to use MediatR; so in my controller code, I wrote:
[HttpPost("AdminLogin")
public async Task<ActionResult<Admin>> GetContractById(Admin admin)
{
var response = await _mediator.Send(new UserLoginRequest(admin));
if (response.HasError)
{
return BadRequest(response);
}
return Ok(response);
}
Then in my UserLoginRequest.cs I wrote this code:
using MediatR;
using SinavAlemiPanel.Domain.Models;
using SinavAlemiPanel.Infrastructure.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SinavAlemiPanel.Application.Requests.UserRequests
{
public class UserLoginRequest :IRequest<BaseResponse<Admin>>
{
private Admin admin;
public UserLoginRequest(Admin admin)
{
this.admin = admin;
}
public Admin UserCredentials { get; set; }
}
public sealed class UserLoginRequestHandlers : IRequestHandler<UserLoginRequest, BaseResponse<Admin>>
{
private readonly IUserService _userService;
public UserLoginRequestHandlers(IUserService userService)
{
_userService = userService;
}
public async Task<BaseResponse<Admin>> Handle(UserLoginRequest request, CancellationToken cancellationToken)
{
await Task.Run(() =>
{
return _userService.Login(request.UserCredentials);
});
}
}
}
And in my service I have:
using Dapper;
using SinavAlemiPanel.Domain.Models;
using SinavAlemiPanel.Infrastructure.Interfaces;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
namespace SinavAlemiPanel.Infrastructure.Services
{
public class UserService : IUserService
{
private readonly IBaseService _baseService;
private readonly DBConnection _db;
public UserService(IBaseService baseService)
{
_baseService = baseService;
}
public Admin Login(Admin admin)
{
Admin result = new Admin();
var dynamicParams = new DynamicParameters();
var conn = _baseService.GetConnection(_db.GetConnString());
try
{
dynamicParams.Add("#username", admin.Username, DbType.String, ParameterDirection.Input);
dynamicParams.Add("#pass", admin.Password, DbType.String, ParameterDirection.Input);
result = SqlMapper.Query<Admin>(conn, "dbo.SP_Admin_Login #username, #pass", dynamicParams).SingleOrDefault();
conn.Close();
if (result != null && result.Id > 0)
{
result.Accesses = (result != null && result.Id > 0) ? Accesses(result.Id) : new List<Access>();
}
return result;
}
catch(Exception ex)
{
return result;
}
}
public List<Access> Accesses(int refAdmin)
{
List<Access> result = new List<Access>();
var dynamicParams = new DynamicParameters();
var conn = _baseService.GetConnection(_db.GetConnString());
try
{
dynamicParams.Add("#refAdmin", refAdmin, DbType.Int32, ParameterDirection.Input);
result = SqlMapper.Query<Access>(conn, "dbo.SP_Admin_Accesses #refAdmin", dynamicParams).ToList();
conn.Close();
return result;
}
catch (Exception e)
{
return result;
}
}
}
}
My problem is that the code doesn't accept in request handle, I get this error:
Error CS0161
'UserLoginRequestHandlers.Handle(UserLoginRequest, CancellationToken)': not all code paths return a value
I am new in C# so I couldn't detect the error. How can I solve this issue?
Thanks in advance
You are missing a return statement in the Handle method, but adding it alone won't fix the compilation you need to translate Admin returned by _userService.Login to BaseResponse<Admin>. Also since _userService.Login is a sync method - no need for Task.Run and assync-await - use Task.FromResult instead. Something like this (assiming the existence of corresponding BaseResponse ctor):
public Task<BaseResponse<Admin>> Handle(UserLoginRequest request, CancellationToken cancellationToken)
{
var admin = _userService.Login(request.UserCredentials);
return Task.FromResult(new BaseResponse<Admin>(admin)); // no need for Task.Run and async-await cause Login is synchronous method
}
The another approach is to create your service as async:
public async Task<Admin> Login(Admin admin)
And you need a corresponding constructor in your BaseResponse class:
public async Task<BaseResponse<Admin>> Handle(UserLoginRequest request, CancellationToken cancellationToken)
{
Admin admin = await _userService.Login(request.UserCredentials);
return new BaseResponse<Admin>(admin);
}
BaseResponse example:
public class BaseResponse<T>
{
private T Response;
public BaseResponse(T response)
{
Response = response;
}
}

API GET method doesn't store data in the property in Xamarin Forms

Data is being retrieve from the API succesfully, as I can see it here,
response
and then goes to the jsonstring, but never gets to the CantGet variable
I need it to be store in my property so I can use the value.
This is my API return:
[{"CantPremio":"70"}
Then this is my property:
using System;
using System.Collections.Generic;
using System.Text;
namespace ServLottery
{
public class GetCantPremio
{
public long CantPremio { get; set; }
}
}
This is the Get task
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
namespace ServLottery
{
public class RestClient
{
HttpClient client = new HttpClient();
public async Task<T> Get<T>(string URL)
{
try
{
var response = await client.GetAsync(URL);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var jsonstring = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonstring);
}
}
catch
{
}
return default(T);
}
}
}
Finally this is the call:
private async void GetCantDisponible()
{
try
{
RestClient client = new RestClient();
var CantGet = await client.Get<GetCantPremio>("https://servicentroapi.azurewebsites.net/api/GetNumber");
if (CantGet != null)
{
PremiosCantLocal = CantGet.CantPremio.ToString();
}
}
catch (Exception ex)
{
throw ex;
}
}
The api you are accessing is returning an array. So you must deserialize not a simple object but a list.
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonstring);
Replace the line that deserializes with this one. Should solve the problem
Like kelvin said, set the List<T> for the json array. And then foreach the collection to get the CantPremio.
RestClient:
public class RestClient
{
HttpClient client = new HttpClient();
public async Task<List<T>> Get<T>(string URL)
{
try
{
var response = await client.GetAsync(URL);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var jsonstring = await response.Content.ReadAsStringAsync();
var s = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonstring);
return s;
}
}
catch
{
}
return default(List<T>);
}
}
GetCantDisponible:
private async void GetCantDisponible()
{
try
{
RestClient client = new RestClient();
var CantGet = await client.Get<GetCantPremio>("https://servicentroapi.azurewebsites.net/api/GetNumber");
if (CantGet != null)
{
foreach (var item in CantGet)
{
var PremiosCantLocal = item.CantPremio.ToString();
}
}
}
catch (Exception ex)
{
throw ex;
}
}
Screenshot:
As mentioned, your API is returning an array but you're trying to deserialize it to a single instance. I'd suggest changing the call site of your client to pass a list for the type parameter:
List<GetCantPremio> CantGet = await client.Get<List<GetCantPremio>>("https://servicentroapi.azurewebsites.net/api/GetNumber");
Note that CantGet is now a List. If you are only looking for one object you could just add on a FirstOrDefault():
GetCantPremio CantGet = await client.Get<List<GetCantPremio>>("https://servicentroapi.azurewebsites.net/api/GetNumber")?.FirstOrDefault();

How do I redirect the message to the corresponding LUIS app

I have a bot with a root LuisDialog and 4 more LuisDialogs each one with a different LUIS model. Following the conversation started here I've implemented a similar DialogFactory strategy.
When a user sends a question that matches "None" intent in my root dialog, I evaluate the rest of dialogs until I find a match and then forward the message to the "winner".
The problem I'm facing is that I'm getting the http error: 429 (Too Many Requests) when querying LUIS (BaseDialog class).
Any ideas about how to face this?
The "None" intent in my root dialog:
[LuisIntent("None")]
public async Task None(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result)
{
var activity = await message;
var factory = new DialogFactory();
BaseDialog<object> dialog = await factory.Create(result.Query);
if (dialog != null)
{
await context.Forward(dialog, EndDialog, activity, CancellationToken.None);
}
else
{
await context.PostAsync("No results!");
}
}
public static async Task EndDialog(IDialogContext context, IAwaitable<object> result)
{
//...
}
The DialogFactory class:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Threading.Tasks;
namespace CodeBot.Dialogs
{
public class DialogFactory
{
private static object _lock = new object();
private static List<BaseDialog<object>> Dialogs { get; set; }
public async Task<BaseDialog<object>> Create(string query)
{
query = query.ToLowerInvariant();
EnsureDialogs();
foreach (var dialog in Dialogs)
{
if (await dialog.CanHandle(query))
{
return dialog;
}
}
return null;
}
private void EnsureDialogs()
{
if (Dialogs == null || (Dialogs.Count != 4))
{
lock (_lock)
{
if (Dialogs == null)
{
Dialogs = new List<BaseDialog<object>>();
}
else if (Dialogs.Count != 4)
{
Dialogs.Clear();
}
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog1));
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog2));
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog3));
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog4));
}
}
}
}
}
And finally, the BaseDialog class (where I'm getting the error):
using Microsoft.Bot.Builder.Dialogs;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Luis;
using System;
namespace CodeBot.Dialogs
{
[Serializable]
public class BaseDialog<R> : LuisDialog<R>
{
public LuisModelAttribute Luis_Model { get; private set; }
public BaseDialog(LuisModelAttribute luisModel) : base(new LuisService(luisModel))
{
Luis_Model = luisModel;
}
public async Task<bool> CanHandle(string query)
{
try
{
var tasks = services.Select(s => s.QueryAsync(query, CancellationToken.None)).ToArray();
var results = await Task.WhenAll(tasks); <-- Error!!!
var winners = from result in results.Select((value, index) => new { value, index })
let resultWinner = BestIntentFrom(result.value)
where resultWinner != null
select new LuisServiceResult(result.value, resultWinner, this.services[result.index]);
var winner = this.BestResultFrom(winners);
return winner != null && !winner.BestIntent.Intent.Equals(Constants.NONE, StringComparison.InvariantCultureIgnoreCase);
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine($"CanHandle error: {e.Message}");
return false;
}
}
}
}
The 429 error is caused by your application (key) hitting the LUIS API too heavily.
You need to either throttle your requests to ensure you stay below the threshold of the free tier, or upgrade to the Basic plan which allows 50 requests a second.

UWP Windows.Web.HttpClient fake for unit test

I try to do unit-test REST communication logic for UWP client. With reference to the answer for System.Web.HttpClient, I found that Windows.Net.HttpClient also accepts an arguement called IHttpFilter.
So, I try to make custom response with IHttpFilter but I don't know correct way to make a response.
public class TestFilter : IHttpFilter
{
public IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage request)
{
if (request.Method == HttpMethod.Get)
{
// response fake response for GET...
}
}
public void Dispose()
{
// do nothing
}
}
And the target method for unit-test is as below.
public async Task<string> PostResult(HttpClient httpClient, string username)
{
var json = new JsonObject
{
{"Username",
JsonValue.CreateStringValue(string.IsNullOrEmpty(username) ? CommonKey.UnAuthorizedUserPartitionKey : username)
},
};
var content = new HttpStringContent(json.Stringify());
content.Headers.ContentType = new HttpMediaTypeHeaderValue("application/json");
// I want to make below line testable...
var response = await httpClient.PostAsync(new Uri(Common.ProcessUrl), content);
try
{
response.EnsureSuccessStatusCode();
return null;
}
catch (Exception exception)
{
return exception.Message ?? "EMPTY ERROR MESSAGE";
}
}
Note that It's NOT duplicate question related to System.Web.HttpClient mocking/faking. What I ask is Windows.Web.HttpClient specifically. I failed to implement with it.
Note that, Windows.Web.Http.IHttpClient is internal accessible and HttpClient is sealed. So hard to do Mock or inherit-and-override it.
While I agree with some that there are better ways to test HttpClient calls, I'll answer your question of how to create a "fake" response with an IHttpFilter implementation (System.Runtime.InteropServices.WindowsRuntime is your friend)
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Web.Http;
using Windows.Web.Http.Filters;
namespace Project.UnitTesting
{
public class FakeResponseFilter : IHttpFilter
{
private readonly Dictionary<Uri, HttpResponseMessage> _fakeResponses = new Dictionary<Uri, HttpResponseMessage>();
public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage)
{
_fakeResponses.Add(uri, responseMessage);
}
public void Dispose()
{
// Nothing to dispose
}
public IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage request)
{
if (_fakeResponses.ContainsKey(request.RequestUri))
{
var fakeResponse = _fakeResponses[request.RequestUri];
return DownloadStringAsync(fakeResponse);
}
// Alternatively, you might want to throw here if a request comes
// in that is not in the _fakeResponses dictionary.
return DownloadStringAsync(new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request });
}
private IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> DownloadStringAsync(HttpResponseMessage message)
{
return AsyncInfo.Run(delegate (CancellationToken cancellationToken, IProgress<HttpProgress> progress)
{
progress.Report(new HttpProgress());
try
{
return Task.FromResult(message);
}
finally
{
progress.Report(new HttpProgress());
}
});
}
}
}

UploadStringAsync won't work in Windows Phone 8

I'm new in C# and I'd like to crate library for my RESTlike API in Windows Phone application.
My api structure is sort of
http://mysiteurl/api/method_name.json
So I would like to call api with (method_name, params) and return specified class.
When I launch this code on my Windows Phone 8.1 program freeze and nothing happens. Also I can see anything on serverside (any call)
API.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SampleProg.Model;
namespace SampleProg.Lib
{
public class Api
{
public async Task<VersionClass> versionInfo()
{
Debug.WriteLine("versionInfo()");
var result = await this.Post<VersionClass>("version", "{}", null);
Debug.WriteLine(result);
return result;
}
public async Task<TReturnType> Post<TReturnType>(string methodName, string data, Dictionary<string, string> headers)
{
var webClient = new WebClient();
webClient.Headers["Content-Type"] = "application/json";
webClient.Encoding = Encoding.UTF8;
var uri = new Uri(String.Format("http://mysiteurl/api/{0}.json", methodName));
if (headers != null)
{
foreach (var key in headers.Keys)
{
webClient.Headers[key] = headers[key];
}
}
return await Post<TReturnType>(webClient, uri, data);
}
private async Task<TReturnType> Post<TReturnType>(WebClient webClient, Uri uri, string jsonData)
{
TReturnType returnObject = default(TReturnType);
var taskCompletionSource = new TaskCompletionSource<TReturnType>();
webClient.UploadStringCompleted += (s, e) =>
{
var result = e.Result;
try
{
Debug.WriteLine(result);
returnObject = JsonConvert.DeserializeObject<TReturnType>(result);
taskCompletionSource.SetResult(returnObject);
}
catch (Exception ex)
{
var newEx = new Exception(
string.Format("Failed to deserialize server response: {0}", result), ex);
taskCompletionSource.SetException(newEx);
}
};
webClient.UploadStringAsync(uri, "POST", jsonData);
return await taskCompletionSource.Task;
}
}
}
App.xaml.cs (I call this from public App() {} ):
var api = new Api();
var apiTask = api.versionInfo();
VersionClass version = (VersionClass)apiTask.Result;
Debug.WriteLine(version.num);
VersionClass.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace SampleProg.Model
{
public class VersionClass
{
[JsonProperty("version")]
public int num { get; set; }
}
}
Could you tell me what is wrong? I use this code (at bottom) http://sandor68.rssing.com/chan-11557297/all_p1.html as base
This:
VersionClass version = (VersionClass) apiTask.Result;
Is deadlocking your app.
What's happening is the SynchronizationContext is trying to marshal control back to the UI thread after the await inside your inner method.
You should always await on a Task:
VersionClass version = (VersionClass) await apiTask;
Also, be aware that your wrapping of UploadStringAsync which returns a Task is already made for you OOTB via WebClient.UploadStringTaskAsync
Edit
If you choose to continue your version of Post, you should not await on the returned Task, you should let the user do that, and unregister the handler from the UploadStringCompleted:
private async Task<TReturnType> Post<TReturnType>(WebClient webClient, Uri uri, string jsonData)
{
TReturnType returnObject = default(TReturnType);
var taskCompletionSource = new TaskCompletionSource<TReturnType>();
UploadStringCompletedEventHandler handler = null;
handler = (s, e) =>
{
webClient.UploadStringCompleted -= handler;
var result = e.Result;
try
{
Debug.WriteLine(result);
returnObject = JsonConvert.DeserializeObject<TReturnType>(result);
taskCompletionSource.SetResult(returnObject);
}
catch (Exception ex)
{
var newEx = new Exception(
string.Format("Failed to deserialize server response: {0}", result), ex);
taskCompletionSource.SetException(newEx);
}
};
webClient.UploadStringCompleted += handler;
webClient.UploadStringAsync(uri, "POST", jsonData);
return taskCompletionSource.Task;
}

Categories

Resources