Async tast not working as it should - asp.net-mvc

I am trying to do webservice async so ui can respond and websevice call is done in background, but since i am little inexpirienced with async stuff i need little help.
Here is my code:
Inside my action result i have call to method which have asyinc stuff in it
public ActionResult SavePackage(string ParcelNumber)
{
/////some other stuff
SaveAsync(ParcelNumber);
}
And
then i have async method :
public async Task SaveAsync(string ParcelNumber)
{
await api.RegisterPackage(ParcelNumber);
}
Which calls api:
public async Task RegisterPackage(string ParcelNumber)
{
var rk = "some api http";
HttpWebRequest request = WebRequest.Create(rk) as HttpWebRequest;
request.Headers.Add("cache-control", "no-cache");
request.Method = "POST";
request.ContentType = "application/json";
string data = "{\n \"ParcelNumber\": \"" + ParcelNumber+ "\"}";
byte[] dataStream = Encoding.UTF8.GetBytes(data);
Stream newStream = request.GetRequestStream();
// Send the data.
newStream.Write(dataStream, 0, dataStream.Length);
newStream.Close();
Task<WebResponse> getResponseTask = Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null);
await getResponseTask.ContinueWith(getResponseAntecedent =>
{
WebResponse webResponse = getResponseAntecedent.Result;
using (var reader = new StreamReader(webResponse.GetResponseStream()))
{
string error = reader.ReadToEnd();
//TODO: use JSON.net to parse this string and look at the error message
var myDeserializedObjList3 = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObjectAtt>(error);
var isValid = myDeserializedObjList3.IsValid;
var ModelErrors = myDeserializedObjList3.ModelErrors;
var ValidationErrors = myDeserializedObjList3.ValidationErrors;
}
});
}
My problem is that the above code is still waiting for response to finish (and that can take about 20 second), and i would like if possible when i call the api i could go back to my ui and let ppl do their stuff while api is proccessed in background.
Anybody have any suggestion?

As I describe on my blog, async does not change the HTTP protocol.
First, the best solution to your problem is to not change your ASP.NET action method at all. Instead, have the actual UI application issue the call asynchronously. If your UI app is a .NET app, then it can use async/await; if it's a web app (i.e., JavaScript), then it can use an AJAX call. Another good option is to introduce SignalR, which allows the server to signal when the work is done.
If you really want to run ASP.NET code outside of a request (which is never recommended), then you can use one of the techniques I describe on my blog for ASP.NET fire-and-forget.

Related

Consuming ASP.NET Web API REST Service in ASP.NET MVC using HttpClient

I have an ASP.NET MVC application from which I would like to consume an ASP.NET Web API REST service.
So I have found a piece of code here.
In my case I would like to call ASP.NET Web API method (DumpIntoFile) from a method in a class in my ASP.NET MVC app (this class is not a controller). I need the execution not to continue until call to DumpIntoFile is completed and finished. How can I do it? I have never used async and await so I do not understand at all how they work.
public void GetData()
{
Warehouse myData = new Warehouse();
myData.id = 1111;
myData.name = blabla;
string path = "c:\temp";
string filename = "myData.dat";
// Stuff here
this.DumpWarehouseDataIntoFile(myData, path, filename);
// Some other stuff here
}
public async void DumpWarehouseDataIntoFile(Warehouse myData, string path, string filename) // See Warehouse class later in this post
{
//Hosted web API REST Service base url
string Baseurl = "http://XXX.XXX.XX.X:YYYY/";
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Serialize parameter to pass to the asp web api rest service
string jsonParam = Newtonsoft.JsonConvert.SerializeObject(myData);
//Sending request to find web api REST service resource using HttpClient
// How can I pass parameters jsonParam, path and filename to below call??????? Concatenating it?
HttpResponseMessage Res = await client.GetAsync("api/Warehouse/DumpIntoFile");
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
// Some other sftuff here
}
}
}
The Warehouse object is as below:
public class Warehouse {
public Warehouse();
public int id { get; set; }
public string name { get; set; }
// some other stuff here
}
And web api rest controller with method:
public class MyWebApiController : ApiController
{
public bool DumpIntoFile(string data, string path, string filename)
{
bool result;
Warehouse myData = JsonConvert.DeserializeObject<Warehouse>(data);
string myPath = path;
string myFilename = filename;
// Here rest of code
return result;
}
}
Also I am trying to do below:
Avoid to hard-code the URL because if the ASP.NET Web API REST service is published in another server in a future, then I need to touch code, change it and publish again my ASP.NET MVC application. Maybe putting a the url in the web.config file and read from there?
How can I pass the parameters jsonParam, path and filename to the web api rest service? concatenating it?
NOTES:
I am using NET 4.5 and Visual Studio 2013.
How can I do it? I have never used async and await so I do not understand at all how they work.
I recommend you read my async intro and follow up with async best practices. You'll learn two key things:
Avoid async void.
It's normal for async/await to "grow" through your codebase.
In your case, DumpWarehouseDataIntoFile should be async Task instead of async void. And that means GetData needs to await the task returned from DumpWarehouseDataIntoFile. Which means GetData should also be async Task. Which means things calling GetData should use await, etc.
Side note: By convention, asynchronous methods should end in Async.
public async Task GetDataAsync()
{
Warehouse myData = new Warehouse();
myData.id = 1111;
myData.name = blabla;
string path = "c:\temp";
string filename = "myData.dat";
// Stuff here
await this.DumpWarehouseDataIntoFileAsync(myData, path, filename);
// Some other stuff here
}
public async Task DumpWarehouseDataIntoFileAsync(Warehouse myData, string path, string filename)

Reading a POST request in Undertow without consuming it

In Undertow I have two handlers, that are chained:
The first handler reads the request and then calls calls the second handler via next.handleRequest(exchange);
The second handler is a proxy handler which send the request to and external server where it is processed.
My problem is the first handler which reads the request. The request headers are no big deal but getting the body data of POST requests is a problem.
Existing solutions as shown in the question How to properly read POST request body in a Handler? consume the request body su that the handler chaining does not work anymore.
How can I read the request body data without consuming it or altering the request in a way that the handler chain does not work afterwards?
I found the problem, in the end it was a missing call to ByteBuffer.flip().
If someone ever needs such an POST data reader one can use the following simplified implementation of an AbstractStreamSourceConduit that is able to read the incoming POST data without consuming it:
exchange.addRequestWrapper(new ConduitWrapper<StreamSourceConduit>() {
#Override
public StreamSourceConduit wrap(ConduitFactory<StreamSourceConduit> factory, HttpServerExchange exchange) {
StreamSourceConduit source = factory.create();
return new AbstractStreamSourceConduit<StreamSourceConduit>(source) {
ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
#Override
public int read(ByteBuffer dst) throws IOException {
int x = super.read(dst);
if (x >= 0) {
ByteBuffer dup = dst.duplicate();
dup.flip();
byte[] data = new byte[x];
dup.get(data);
bout.write(data);
} else {
// end of stream reached
byte[] data = bout.toByteArray();
// ... to something with data
}
return x;
}
};
}
});

Is it possible to use Audit.Net with httpClient to capture external requests

Using Audit.Net is it possible to create an audit scope for httpClient requests, in a similar way to the MVC.Core or WebAPI.Core Middleware?
I've tried something like this but not having much success, generally it'll timeout the app.
AuditScope scope = null;
try {
using(HttpClient client = new HttpClient) {
scope = await AuditScope.CreateAsync("",() => client)
// code to initialise the Httpclient
}
}
finally {
await scope.DisposeAsync();
}
I think the only option to hook into the HttpClient is to use a custom HttpClientHandler so you can intercept the rest calls.
Just as an example:
public class AuditClientHandler : HttpClientHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var options = new AuditScopeOptions()
{
EventType = $"{request.Method.Method} {request.RequestUri.AbsoluteUri}",
CreationPolicy = EventCreationPolicy.InsertOnStartReplaceOnEnd,
ExtraFields = new
{
request = GetRequestAudit(request)
}
};
using (var scope = AuditScope.Create(options))
{
var response = await base.SendAsync(request, cancellationToken);
scope.SetCustomField("response", GetResponseAudit(response));
return response;
}
}
}
I've used the InsertOnStartReplaceOnEnd creation policy, so the request is saved before it's sent to the server, and the response is added to the event and saved afterwards.
The implementation of GetRequestAudit / GetResponseAudit is up to you, just return an object (that can be serialized) with the information you want to log.
So each time you need to audit an HttpClient instance, you need to pass the handler to its constructor:
var cli = new HttpClient(new AuditClientHandler());
var response = await cli.GetAsync("http://google.com");
Anyway I will evaluate providing a new library (Audit.HttpClient?) with a configurable Handler so the implementation could be cleaner.
Update
You can now use the Audit.HttpClient extension for a cleaner implementation. Take a look at the documentation here

ASP.NET WebAPI unable to post

I am unable to post data to a ASP.NET WebAPI server.
I can get data from the WEBAPI server. However, I am unable to post.
The following code fails to post:
response = await client.PostAsync("api/Cars", content);
Error:
StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1
Client:
[TestClass]
public class UnitTest1
{
// http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
[TestMethod]
public async Task TestMethod1()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:48213/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
var response = await client.GetAsync("api/Cars");
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
}
// HTTP POST
using (var content = new StringContent(#"some_value"))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
response = await client.PostAsync("api/Cars", content);
if (response.IsSuccessStatusCode)
{
Debug.WriteLine("Post was successful.");
}
}
}
WebAPI:
namespace FSharpWebAPI.Controllers
open System.Web.Http
open FSharpWebAPI.Models
type CarsController() =
inherit ApiController()
let values = [| { Make = "Ford"; Model = "Mustang" }; { Make = "Nissan"; Model = "Titan" } |]
member x.Get() = values
member x.Post(data) =
ignore
What updates do I need to make just to make a simple post?
As #Fyodor Soikin has pointed out, the Post method is generic ('a -> 'b -> unit), and ASP.NET Web API refuses to wire up such methods.
ASP.NET Web API uses convention over configuration in order to figure out how to route and handle incoming requests. If an HTTP POST arrives, it'll go hunting for a method (partially) named Post, and attempt to call it. While you could argue that if a generic method could handle the input, it'd be possible for Web API to still call it, it doesn't do that.
You'll need to add a type annotation - for example:
member x.Post(data : Car) =
ignore
That's probably still not going to be enough, because the type of that version of Post is Car -> 'a -> unit, so the return value is a generic function. I'd be surprised if Web API knows what to do with that.
The reason is that ignore is a function, and since Post doesn't invoke the function, the return value is the function itself.
If you want to ignore the input and return unit, you can simply do this:
member x.Post(data : Car) = ()
This version has the type Car -> unit, which I expect Web API will find acceptable.

ASP.NET MVC - Using cURL or similar to perform requests in application

I'm building an application in ASP.NET MVC (using C#) and I would like to know how I can perform calls like curl http://www.mywebsite.com/clients_list.xml inside my controller
Basically I would like to build a kind of REST API to perform actions such as show edit and delete, such as Twitter API.
But unfortunately until now I didn't find anything besides that cURL for windows on this website: http://curl.haxx.se/
So I don't know if is there any traditional way to retrieve this kind of call from URL with methods like post delete and put on the requests, etc...
I just would like to know an easy way to perform commands like curl inside my controller on my ASP.NET MVC Application.
UPDATE:
Hi so I managed to make GET Requests but now I'm having a serious problem in retrieve POST Request for example, I'm using the update status API from Twitter that in curl would work like this:
curl -u user:password -d "status=playing with cURL and the Twitter API" http://twitter.com/statuses/update.xml
but on my ASP.NET MVC application I'm doing like this inside my custom function:
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.Credentials = new NetworkCredential("username", "password");
request.Headers.Add("status", "Tweeting from ASP.NET MVC C#");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return responseText;
Now the problem is that this request is returning 403 Forbidden,
I really don't know why if it works perfectly on curl
:\
UPDATE:
I finally manage to get it working, but probably there's a way to make it cleaner and beautiful, as I'm new on C# I'll need more knowledge to do it, the way the POST params are passed makes me very confused because is a lot of code to just pass params.
Well, I've created a Gist - http://gist.github.com/215900 , so everybody feel free to revise it as you will. Thanks for your help çağdaş
also follow the code here:
public string TwitterCurl()
{
//PREVENT RESPONSE 417 - EXPECTATION FAILED
System.Net.ServicePointManager.Expect100Continue = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.Credentials = new NetworkCredential("twitterUsername", "twitterPassword");
//DECLARE POST PARAMS
string headerVars = String.Format("status={0}", "Tweeting from ASP.NET MVC C#");
request.ContentLength = headerVars.Length;
//SEND INFORMATION
using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream(), ASCIIEncoding.ASCII))
{
streamWriter.Write(headerVars);
streamWriter.Close();
}
//RETRIEVE RESPONSE
string responseText = String.Empty;
using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return responseText;
/*
//I'M NOT SURE WHAT THIS IS FOR
request.Timeout = 500000;
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = "Custom Twitter Agent";
#if USE_PROXY
request.Proxy = new WebProxy("http://localhost:3000", false);
#endif
*/
}
Try using Microsoft.Http.HttpClient. This is what your request would look like
var client = new HttpClient();
client.DefaultHeaders.Authorization = Credential.CreateBasic("username","password");
var form = new HttpUrlEncodedForm();
form.Add("status","Test tweet using Microsoft.Http.HttpClient");
var content = form.CreateHttpContent();
var resp = client.Post("http://www.twitter.com/statuses/update.xml", content);
string result = resp.Content.ReadAsString();
You can find this library and its source included in the WCF REST Starter kit Preview 2, however it can be used independently of the rest of the stuff in there.
P.S. I tested this code on my twitter account and it works.
Example code using HttpWebRequest and HttpWebResponse :
public string GetResponseText(string url) {
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
responseText = sr.ReadToEnd();
}
return responseText;
}
To POST data :
public string GetResponseText(string url, string postData) {
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentLength = postData.Length;
using (StreamWriter sw = new StreamWriter(request.GetRequestStream())) {
sw.Write(postData);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
responseText = sr.ReadToEnd();
}
return responseText;
}
This is the single line of code I use for calls to a RESTful API that returns JSON.
return ((dynamic) JsonConvert.DeserializeObject<ExpandoObject>(
new WebClient().DownloadString(
GetUri(surveyId))
)).data;
Notes
The Uri is generated off stage using the surveyId and credentials
The 'data' property is part of the de-serialized JSON object returned
by the SurveyGizmo API
The Complete Service
public static class SurveyGizmoService
{
public static string UserName { get { return WebConfigurationManager.AppSettings["SurveyGizmo.UserName"]; } }
public static string Password { get { return WebConfigurationManager.AppSettings["SurveyGizmo.Password"]; } }
public static string ApiUri { get { return WebConfigurationManager.AppSettings["SurveyGizmo.ApiUri"]; } }
public static string SurveyId { get { return WebConfigurationManager.AppSettings["SurveyGizmo.Survey"]; } }
public static dynamic GetSurvey(string surveyId = null)
{
return ((dynamic) JsonConvert.DeserializeObject<ExpandoObject>(
new WebClient().DownloadString(
GetUri(surveyId))
)).data;
}
private static Uri GetUri(string surveyId = null)
{
if (surveyId == null) surveyId = SurveyId;
return new UriBuilder(ApiUri)
{
Path = "/head/survey/" + surveyId,
Query = String.Format("user:pass={0}:{1}", UserName, Password)
}.Uri;
}
}
Look into the System.Net.WebClient class. It should offer the functionality you require. For finer grained control, you might find WebRequest to be more useful, but WebClient seems the best fit for your needs.

Resources