I have a MVC 4 app. One of the things it does is send an email by calling another service using the WebClient like so:
var serializer = new JavaScriptSerializer();
string requestData = serializer.Serialize(new
{
EventID = 1,
SubscriberID = studentId,
ToList = loginModel.EmailAddress,
TemplateParamVals = strStudentDetails,
});
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
var result = await client.UploadData(URI, Encoding.UTF8.GetBytes(requestData));
}
I wanted to make use of UploadDataAsync but got the error:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle.
So I thought of creating a wrapper function & making it asynchronous while still using UploadDataAsync.
private async Task<bool> SendEmailAsync(long studentId, LoginModel loginModel)
{
try
{
string URI = ConfigurationManager.AppSettings["CommunicationmanagerURL"] + "PostUserEvent";
var serializer = new JavaScriptSerializer();
string requestData = serializer.Serialize(new
{
EventID = 1,
SubscriberID = studentId,
ToList = loginModel.EmailAddress,
TemplateParamVals = strStudentDetails,
});
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
var result = await client.UploadData(URI, Encoding.UTF8.GetBytes(requestData));
}
}
catch (Exception ex)
{
// Log exceptions ...
}
}
This doesn't build and gives the error:
Cannot await 'byte[]'
Any ideas how to solve this?
you can not await a synchrone function, you have to call the [..]Async one:
var result = await client.UploadDataAsync(URI, Encoding.UTF8.GetBytes(requestData));
The function you call returns a byte[], which is of course not awaitable.
UploadDataAsync returns a Task<byte[]> instead, this one you want to use:
http://msdn.microsoft.com/de-de/library/ms144225(v=vs.110).aspx
edit:
The Method seems to return void, which is kind of awaitable, too.. in terms of fire and forget.
The UploadData method you are using in your sample code is a synchronous operation. As the compiler is saying, you cannot await a synchronous operation. The problem is not the byte[] type but the operation itself.
If you want to use await, you'll need to use an asynchronous operation. WebClient offers two asynchronous versions of the UploadData method :
UploadDataAsync
UploadDataTaskAsync
To properly use the await functionnality, you'll need to use UploadDataTaskAsync.
EDIT : My first answer was about the warning you got from the compiler. What you are trying to do is wrap the call to the synchronous operation in a Task to make it asynchronous.
You can achieve this by creating your own Task and returning it :
private async Task<bool> SendEmailAsync(long studentId, LoginModel loginModel)
{
Task<bool> uploadTask = Task.Factory.StartNew<bool>(
() =>
{
try
{
string URI = ConfigurationManager.AppSettings["CommunicationmanagerURL"] + "PostUserEvent";
var serializer = new JavaScriptSerializer();
string requestData = serializer.Serialize(new
{
EventID = 1,
SubscriberID = studentId,
ToList = loginModel.EmailAddress,
TemplateParamVals = strStudentDetails,
});
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
var result = client.UploadData(URI, Encoding.UTF8.GetBytes(requestData));
return true;
}
}
catch (Exception ex)
{
// Log exceptions ...
return false;
}
});
return uploadTask;
}
The underlying reason is a bit complicated; it has to do with how EAP components (such as WebClient) notify their platform that they are doing work.
The easiest solution is to replace WebClient with HttpClient.
Related
When testing my web API with Postman my API get executes fine!
When it comes to running the code with HttpClient in my client application the code executes without error but without the expected result on the server.
What could be happening?
From my client application:
private string GetResponseFromURI(Uri u)
{
var response = "";
HttpResponseMessage result;
using (var client = new HttpClient())
{
Task task = Task.Run(async () =>
{
result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
});
task.Wait();
}
return response;
}
Here is the API controller:
[Route("api/[controller]")]
public class CartsController : Controller
{
private readonly ICartRepository _cartRepo;
public CartsController(ICartRepository cartRepo)
{
_cartRepo = cartRepo;
}
[HttpGet]
public string GetTodays()
{
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Add")]
public string GetIncrement()
{
var cart = new CountedCarts();
_cartRepo.Add(cart);
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Remove")]
public string GetDecrement()
{
_cartRepo.RemoveLast();
return _cartRepo.GetTodaysCarts();
}
}
Note these API calls work as expected when called from Postman.
You shouldn't use await with client.GetAsync, It's managed by .Net platform, because you can only send one request at the time.
just use it like this
var response = client.GetAsync("URL").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var dataObjects = response.Content.ReadAsAsync<object>().Result;
}
else
{
var result = $"{(int)response.StatusCode} ({response.ReasonPhrase})";
// logger.WriteEntry(result, EventLogEntryType.Error, 40);
}
You are doing fire-and-forget approach. In your case, you need to wait for the result.
For example,
static async Task<string> GetResponseFromURI(Uri u)
{
var response = "";
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
}
return response;
}
static void Main(string[] args)
{
var t = Task.Run(() => GetResponseFromURI(new Uri("http://www.google.com")));
t.Wait();
Console.WriteLine(t.Result);
Console.ReadLine();
}
Simple sample used to get page data.
public string GetPage(string url)
{
HttpResponseMessage response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
string page = response.Content.ReadAsStringAsync().Result;
return "Successfully load page";
}
else
{
return "Invalid Page url requested";
}
}
I've had a problem with chace control when using httpclient.
HttpBaseProtocalFilter^ filter = ref new HttpBaseProtocolFilter();
filter->CacheControl->ReadBehavior = Windows::Web::Http::Filters::HttpCacheReadBehavior::MostRecent;
HttpClient^ httpClient = ref new HttpClient(filter);
I'm not really sure what the expected results are or what results your getting at all so this is really just a guessing game right now.
When I POST something using HttpClient I found adding headers by hand seemed to work more often than using default headers.
auto httpClient = ref new HttpClient();
Windows::Web::Http::Headers::HttpMediaTypeHeaderValue^ type = ref new Windows::Web::http::Headers::HttpMediaTypeHeaderValue("application/json");
content->Headers->ContentType = type;
If I don't do these 2 things I found, for me anyways, that half the time my web requests were either not actually being sent or the headers were all messed up and the other half of the time it worked perfectly.
I just read a comment where you said it would only fire once, that makes me think it is the cachecontrol. I think what happens is something (Windows?) sees 2 requests being sent that are the exact same, so to speed things up it just assumes the same answer and never actually sends the request a 2nd time
Currently working with the outlook api, even tough I usually work with the outlook library acquired via Nuget; I have reached a limitation where I am not able to accept event invitations. So I proceeded in making a a restful call out to the the outlook api. However, when I am making the call I am getting the following message {"error":{"code":"InvalidMethod","message":"An action can only be invoked as a 'POST' request."}} when executing the call.
Bad Code
class Program
{
static void Main(string[] args)
{
var testAccept = ExecuteClientCall.AcceptEvent().Result;
}
public static async Task<bool> AcceptEvent()
{
AuthenticationContext authenticationContext = new AuthenticationContext(CrmPrototype.Helpers.AuthHelper.devTenant);
try
{
var token = await GetTokenHelperAsync(authenticationContext, CrmPrototype.Helpers.AuthHelper.OutlookAuthenticationEndpoint);
string requestUrl = "https://outlook.office.com/api/v2.0/Users/***#nowwhere.com/events('AAQkAGZiNDQxZTVkLWQzZjEtNDdjNy04OTc4LTM4NmNjM2JiOTRjNAAQAFpV0CnWR0FIpWFYRtszPHU=')/accept";
HttpClient hc = new HttpClient();
hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var method = new HttpMethod("POST");
var request = new HttpRequestMessage(method, requestUrl)
{
Content = new StringContent("{SendResponse: true}", Encoding.UTF8, "application/json")
};
HttpResponseMessage hrm = await hc.GetAsync(requestUrl);
if (hrm.IsSuccessStatusCode)
{
string jsonresult = await hrm.Content.ReadAsStringAsync();
var stophere = 0;
}
else
{
return false;
}
return true;
}
catch (Exception ex)
{
throw;
}
}
}
Maybe the reason is that you called
hc.GetAsync(requestUrl);
The doc said that this method:
Sends a GET request to the specified Uri as an asynchronous operation.
Try:
PostAsync(Uri, HttpContent)
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.118).aspx
Hope this help you.
Your variable request contains an HttpRequestMessage object that you have created, but your code presently doesn't do anything with it.
Try replacing the line
HttpResponseMessage hrm = await hc.GetAsync(requestUrl);
(which, as pointed out by the other answer, makes a GET request), with
HttpResponseMessage hrm = await hc.SendAsync(request);
Hi I need to write a proper async web request function in C# to get some data in JSON format and deserialize it in strongly typed object. I came up with this solution:
public async Task<TReturnType> MakeAsyncRequest<TReturnType>(string url) where TReturnType : class
{
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
var response = await request.GetResponseAsync();
var json = await Task.Run(() => ReadStreamFromResponse(response));
var deserializedObject = await Task.Run(() => JsonConvert.DeserializeObject<TReturnType>(json));
return deserializedObject;
}
catch (Exception)
{
// TODO: Handle exception here
throw;
}
}
private string ReadStreamFromResponse(WebResponse response)
{
using (var responseStream = response.GetResponseStream())
using (var sr = new StreamReader(responseStream))
{
return sr.ReadToEnd();
}
}
And usage would be like this:
var myData = await MakeAsyncRequest<IEnumerable<SomeObject>>(http://example.com/api/?getdata=all);
The issue I'm facing is that sometimes my web request fires and never returns, is there a proper way to handle that? Maybe playing with timers is an option but I don't like to go there.
Also I would be thankful if you could improve on my function, maybe there is a better way to do things and I just don't see it.
After considering this interesting answer HttpClient.GetAsync(...) never returns..., I still have a situation where my HttpClient is not returning when I use await (sample code below). In addition. I use this helper routine from both asp.net MVC5 Controllers (UI-driven) and the WebApi. What can I do to:
Use await instead of the (dreaded) .Result and still have this function return?
Reuse this same routine from both MVC Controllers and WebApi?
Apparently, I should replace the .Result with .ConfigureAwait(false) but this seems to contradict with the fact that "Task4" in the post cited above works fine with an await httpClient.GetAsync. Or do I need separate routines for Controller and WebApi cases?
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// Why does this never return when await is used?
HttpResponseMessage response = hclient.GetAsync("api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
UPDATE: My call chain, which starts with a Telerik Kendo Mvc.Grid DataBinding call is as follows:
[HttpPost]
public async Task<ActionResult> TopLicenseGrid_Read([DataSourceRequest]DataSourceRequest request)
{
var res = await GetLicenseInfo();
return Json(res.ToDataSourceResult(request)); // Kendo.Mvc.Extensions.DataSourceRequest
}
Then:
private async Task<IEnumerable<CsoPortalLicenseInfoModel>> GetLicenseInfo()
{
...
// Never returns
var qry = #"app_metadata.tc_app_user.country:""DE""";
return await TcSearchMultiUsersAsync(qry);
}
Then, the routine shown in full above but now WITHOUT the .Result:
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// This now returns fine
HttpResponseMessage response = hclient.GetAsync("api/v2/users?search_engine=v2&q=" + elasticQuery");
if (response.IsSuccessStatusCode)
{
// This returns my results fine too
var content = await response.Content.ReadAsStringAsync();
// The following line never returns results. When debugging, everything flows perfectly until I reach the following line, which never
// returns and the debugger returns me immediately to the top level HttpPost with a result of null.
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
You need to await everything. It's not enough that you await on one of them, you need to await on all of them:
This:
HttpResponseMessage response = hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
Should be:
HttpResponseMessage response = await hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2");
It is enough to have one blocking call to .Result in order to deadlock. You need "async all the way".
I'm working on a new Windows Phone 8 app. I'm connecting to a webservice which returns valid json data. I'm using longlistselector to display the data. This works fine when i'm using the string json in GetAccountList(); but when receiving data from the DataServices class i'm getting the error "Cannot implicitly convert type 'System.Threading.Tasks.Task'to string". Don't know what goes wrong. Any help is welcome. Thanks!
DataServices.cs
public async static Task<string> GetRequest(string url)
{
HttpClient httpClient = new HttpClient();
await Task.Delay(250);
HttpResponseMessage response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Debug.WriteLine(responseBody);
return await Task.Run(() => responseBody);
}
AccountViewModel.cs
public static List<AccountModel> GetAccountList()
{
string json = DataService.GetRequest(url);
//string json = #"{'accounts': [{'id': 1,'created': '2013-10-03T16:17:13+0200','name': 'account1 - test'},{'id': 2,'created': '2013-10-03T16:18:08+0200','name': 'account2'},{'id': 3,'created': '2013-10-04T13:23:23+0200','name': 'account3'}]}";
List<AccountModel> accountList = new List<AccountModel>();
var deserialized = JsonConvert.DeserializeObject<IDictionary<string, JArray>>(json);
JArray recordList = deserialized["accounts"];
foreach (JObject record in recordList)
{
accountList.Add(new AccountModel(record["name"].ToString(), record["id"].ToString()));
}
return accountList;
}
UPDATE: I changed it slightly and works like a charm now. Thanks for your help!
DataServices.cs
//GET REQUEST
public async static Task<string> GetAsync(string url)
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(url);
string content = await response.Content.ReadAsStringAsync();
return content;
}
AccountViewModel.cs
public async void LoadData()
{
this.Json = await DataService.GetAsync(url);
this.Accounts = GetAccounts(Json);
this.AccountList = GetAccountList(Accounts);
this.IsDataLoaded = true;
}
public static IList<AccountModel> GetAccounts(string json)
{
dynamic context = JObject.Parse(json);
JArray deserialized = (JArray)JsonConvert.DeserializeObject(context.results.ToString());
IList<AccountModel> accounts = deserialized.ToObject<IList<AccountModel>>();
return accounts;
}
public static List<AlphaKeyGroup<AccountModel>> GetAccountList(IList<AccountModel> Accounts)
{
List<AlphaKeyGroup<AccountModel>> accountList = AlphaKeyGroup<AccountModel>.CreateGroups(Accounts,
System.Threading.Thread.CurrentThread.CurrentUICulture,
(AccountModel s) => { return s.Name; }, true);
return accountList;
}
That line is your problem:
return await Task.Run(() => responseBody);
Did you try that? :
return responseBody;
Try this too:
public async static List<AccountModel> GetAccountList()
{
string json = await DataService.GetRequest(url);
...
}
A few things here. First the error
Cannot implicitly convert type 'System.Threading.Tasks.Task' to string
This error is coming from the call to DataService.GetRequest(url). This method does return a string. Tt returns a Task where T is a string. There are many ways that you can use the result of this method. the first (and best/newest) is to await the call to the method.
string json = await DataService.GetResult(url);
Making this change requires you to add the async keyboard to your method
public async static List<AccountModel> GetAccountList()
This is the new async/await pattern. Adding these words tells the compiler that the method cal is asynchronous. It allows you to make asynchronous calls but write code as if it is synchronous.
The other ways to call the method are to work the Task object directly.
// First is to use the Result property of the Task
// This is not recommended as it makes the call synchronous, and ties up the UI thread
string json = DataService.GetResult(url).Result;
// Next is to continue work after the Task completes.
DataService.GetResult(url).ContinueWith(t =>
{
string json = t.Result;
// other code here.
};
Now for the GetResult method. Using the async/await pattern requires you to return Task from methods. Even though the return type is Task, your code should return T. So as Krekkon mentioned, you should change the return line to
return responseBody;
Here is a great article about returning Task from an async method.