I am working on a async web request. and need to depends on the response to do a message return.
was thinking to do sth like following
// creating request
string messageToReturn = string.empty;
request.BeginGetResponse(ar =>
{
HttpWebRequest req2 = (HttpWebRequest)ar.AsyncState;
var response = (HttpWebResponse)req2.EndGetResponse(ar);
// is it safe to do this?
messageToReturn = "base on respone, assign different message";
}, request);
// will i get any response message? i will always get empty right?
// since response is handle in another thread
return messageToReturn;
what is the best way to do that?
You are right, that variable will always be empty because you fired off an asyncronous request with the BeginGetResponse method. So really you have a few options here. You can either block the executing thread until the response comes back (probably a really bad idea unless you have a very strong argument for doing this), or you could use an event based asynchronous pattern to alert callers when your response returns...
Consider some of your code wrapped in a method
public void GetMessageAsync()
{
string messageToReturn = string.empty;
request.BeginGetResponse(ar =>
{
HttpWebRequest req2 = (HttpWebRequest)ar.AsyncState;
var response = (HttpWebResponse)req2.EndGetResponse(ar);
// is it safe to do this?
messageToReturn = "base on respone, assign different message";
}, request);
}
To wire up an event based pattern here. We define a custom EventArgs class and a custom event which callers can listen for and which we will fire when the response comes back.
public class StringEventArgs : EventArgs
{
public string Message { get; set; }
}
public event EventHandler<StringEventArgs> MessageReturned;
public void GetMessageAsync()
{
//string messageToReturn = string.empty;
request.BeginGetResponse(ar =>
{
HttpWebRequest req2 = (HttpWebRequest)ar.AsyncState;
var response = (HttpWebResponse)req2.EndGetResponse(ar);
//messageToReturn = "base on respone, assign different message";
this.MessageReturned(this, new StringEventArgs { Message = response.ToString() });
}, request);
}
Related
I'm using AddOpenIdConnect and need to modify the response in case the OnRedirectToIdentityProvider event is raised. Within this event the response status is modified to 401 and I would like to set a custom message. To write this custom message, I've created the SetResponseBody method.
The solution of this post is used to set the response status, but I need to modify the Body as well.
I'm calling the SetResponseBody method (a custom method which I implemented) in order to modify the response body as soon as the OnRedirectToIdentityProvider event is raised from AddOpenIdConnect.'
As mentioned in one of the comments by #Panagiotis Kanavos in the post, the SetResponseBody method doesn't seem to be a correct solution (despite the response actually contains valid json). Could you provide an alternative?
Summarized: I would like to return a custom response besides the status code 401.
OnRedirectToIdentityProvider = async e =>
{
// e is of type RedirectContext
if (e.Request.Path.StartsWithSegments("/api")))
{
if (e.Response.StatusCode == (int)HttpStatusCode.OK)
{
e.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
// TestMessage is a const
// e.Response is readonly (get) so it's not possible to set it directly.
await ResponseRewriter.SetResponseBody(e.Response, TestMessage);
}
e.HandleResponse();
}
await Task.CompletedTask;
}
with ResponseRewriter.SetResponseBody defined as follows
public static async Task SetResponseBody(HttpResponse response, string message)
{
var originalResponseBody = response.Body;
var responseBody = new MemoryStream();
response.Body = responseBody;
response.ContentType = "application/json";
var body = new { Message = message };
string json = JsonSerializer.Serialize(body);
await response.WriteAsync(json);
response.Body.Seek(0, SeekOrigin.Begin);
await responseBody.CopyToAsync(originalResponseBody);
}
The last two lines are implemented as written in this post.
I am calling a soap service asynchronously but stuck at a point where I need to close a soap client connection. Not much help from previous post either: How to close Client Proxy with Async Call WCF
Below is my code so far.
Method below (GetFieldList(....) calls generic method ApiClient.GetResponse(....) with request parameters and what service to invoke
public async Task<ServiceReference.GetFieldListResponse> GetFieldList(string identifier)
{
var request = new GetFieldListRequest
{
Header = new Header {Username = ApiSettings.Instance.ApiToken},
AGroup = "",
IdType = "",
Id = ""
};
var response = await ApiClient.GetResponse(request, (c) => c.GetFieldListAsync(request.Header, request.Id, request.IdType, request.AGroup));
return response;
}
In the method below, I have commented out finally block because the connection was being closed before a response was returned to the calling method.
public class ApiClient
{
public static TResponse GetResponse<TResponse, TRequest>(TRequest request,
Func<SoapClient, TResponse> handler,
string apiMethodName = "")
where TResponse : class
{
Debug.WriteLine("Calling: " + typeof(TRequest).Name);
if (string.IsNullOrEmpty(apiMethodName))
{
apiMethodName = typeof(TRequest).Name.Replace("Request", string.Empty);
}
// creates a soap connection
var client = WebServiceClient.CreateServiceInstance();
TResponse response = null;
try
{
//webservice call is invoked here
response = handler(client);
}
catch (FaultException exception)
{
throw new ApiException(string.Format("Api error on {0}.", apiMethodName), exception);
}
//if this finally block is not commented, connection is closed before a response was returned to the calling method.
//finally
//{
// client.Close();
//}
return response;
}
}
Any idea what am I missing?
Thanks
I would suggest to have a global WebServiceClient or ApiClient, and do GetResponse and CloseClient in two threads. That way, even you are waiting for response, you can force trigger client close in CloseClient thread.
I am trying to send http request of json data to a web service. It successfully get directed to the web service but the data is always null...
Here is my web service:
public bool CheckUserExist ([FromBody] string Email)
{
List<User> all_users = repo.getUsers();
var match = all_users.Find(i => i.Email == Email);
if (match == null)
{
return false;
}
else
{
return true;
}
}
and here is my Http Request:
var webAddr = "http://localhost:59305/api/User/CheckUserExist";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"Email\":\"Email\"}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
return RedirectToAction("Index");
}
as i mentioned...Iam using a debugger at the web service function...the request get directed to the service but variable "Email" is always null
The quick fix is to change what you are posting. If you want your API endpoint to work with [FromBody] string Email, then you should change your "json":
string json = "\"a#b.c\"";
streamWriter.Write(json);
streamWriter.Flush();
However, you may want to consider a few other changes for the long term on your approach:
Let your repo return IQueryable instead of List or some other IEnumerable. As written, you will always return every user from the database. With Linq, you can leverage the lazy initialization and let your query only get the matching user instead of get EVERY user and then find the match. This will not scale well.
Use new HttpClient and async functionality in your action instead of HttpWebRequest
Instead of manually building your JSON, let .NET do the work and create a class that gets serialized
With the above solutions in place (except for first since you didn't post your data context, didn't want to make too many assumptions), here are some examples to get you started:
Define a shared user search class (shared either in same project or shared DLL)
public class UserSearch
{
public string Email { get; set; }
}
Let Web API map post against the search class
public bool CheckUserExist([FromBody] UserSearch userSearch)
{
IQueryable<User> all_users = repo.getUsers();
var isMatch = all_users.Any(i => i.Email == userSearch.Email);
return isMatch;
}
Change to HttpClient and use async to send API request with new search class
public async Task<ActionResult> Check()
{
using (var client = new HttpClient())
{
var search = new UserSearch() { Email = "a#b.c" };
var response = await client.PostAsJsonAsync("http://localhost:59305/api/User/CheckUserExist", search);
if (response.IsSuccessStatusCode)
{
bool exists = await response.Content.ReadAsAsync<bool>();
// Handle what to do with exists
return RedirectToAction("Index");
}
else
{
// Handle unsuccessful call
throw new Exception("Application error");
}
}
}
You can't use string to accept the post data, please define a struct class or use dynamic to receive the Json string.
I have the following code:
ProgressMessageHandler progress = new ProgressMessageHandler();
progress.HttpSendProgress += new EventHandler<HttpProgressEventArgs>(HttpSendProgress);
HttpRequestMessage message = new HttpRequestMessage();
message.Method = HttpMethod.Post;
message.Content = content;
message.RequestUri = new Uri("http://myaddress");
var client = HttpClientFactory.Create(progress);
sending = client.SendAsync(message);
private void HttpSendProgress(object sender, HttpProgressEventArgs e)
{
//....
}
I want to catch a situation, when "myaddress" is not available. Method HttpSendProgress is called when progress is active, so, I can't check in this method.
Any way to check if "myaddress" is available. I have an idea to start one more thread to check when HttpSendProgress is called last time. But maybe there is a standard method?
I dont think you can check if the address is a working address in the progress event. You need to check the status after the response has come back.
if (response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode.ToString());
}
How to determine a 404 response status when using the HttpClient.GetAsync()
I able to upload data from client side, and got the response. My question is how to return a value from LoginRequest method after UploadStringCompleted event was completed.
Or How to return a value from UploadStringCompleted event. Please See my code below.
But When I am doing return "true" line is executed before webClientLogin_UploadStringCompleted method called.
Below link have something similar but I didn't get answer for my question
Click [here] (Return value of UploadStringAsync().?)
Thanks in Advance.
public string LoginRequest(string token)
{
WebClient client = new WebClient();
JavaScriptSerializer serializer = new JavaScriptSerializer();
var result= serializer.Serialize(token);
client .Headers["ContentType"] = "application/json";
client.UploadStringCompleted += new UploadStringCompletedEventHandler(webClientLogin_UploadStringCompleted);
client.UploadStringAsync(URI, HTTP_POST, result);
return "true";
}
private void webClientLogin_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
var validate = JsonConvert.DeserializeObject<string>(e.Result);
}
Shall I do anything with AutoResetEvent or ManualReSetEvent?
You should not return anything from the LoginRequest method. Instead do the things that you want to do from the webClientLogin_UploadStringCompleted event. because it will get called when the upload is completed.
You can use UploadStringTaskAsync which returns a Task<string> allowing you to use async/await. Try this:
public async Task<string> LoginRequest(string token)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
var result = serializer.Serialize(token);
WebClient client = new WebClient();
client.Headers["ContentType"] = "application/json";
var response = await client.UploadStringTaskAsync(URI, HTTP_POST, result);
// do something with the response here, eg. JsonConvert.DeserializeObject();
return "true";
}
Also, why return true as a string? Use a boolean if you can.