I want trace the response body of my httpclient requests in dependencies table Application Insight. My application runs with .NET framework 4.8
I created an Initializer to trace the Dependencies telemetry with the following code:
public class TrackResponseBody : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as DependencyTelemetry;
if (requestTelemetry == null)
return;
if (requestTelemetry.TryGetOperationDetail("HttpResponse", out var responseObj))
{
var response = responseObj as HttpWebResponse;
if (response != null)
{
using (var stream = response.GetResponseStream())
{
var reader = new StreamReader(stream);
string result = reader.ReadToEnd();
requestTelemetry.Properties["ResponseBody"] = result;
}
}
}
}
}
But when i try to call reader.ReadToEnd(), my code generate this exception: System.NotSupportedException: 'The stream does not support concurrent IO read or write operations.'
This code write correctly in application insight dependencies log, if i don't try to get body response.
This is how I implemented my HttpClient:
var client_ = new HttpClient();
client_.BaseAddress = new Uri("https://www.google.com");
using (var request_ = new HttpRequestMessage())
{
request_.Method = new HttpMethod("GET");
var response_ = await client_.SendAsync(request_).ConfigureAwait(false);
var responseData_ = await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
}
Is there a way to get the body response of my HttpClient?
I try the code by myself, in TrackResponseBody, this line of code returns null: var response = responseObj as HttpWebResponse; , the response variable is always null.
And then I make a small change, instead convert responseObj to HttpWebResponse, I just convert responseObj to string, then add it to the requestTelemetry.Properties. The code is as below:
public class TrackResponseBody: ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as DependencyTelemetry;
if (requestTelemetry == null)
return;
if (requestTelemetry.TryGetOperationDetail("HttpResponse", out var responseObj))
{
var response = responseObj as HttpWebResponse;
//convert responseObj to string
string s = responseObj.ToString();
requestTelemetry.Properties["ResponseBody"] = s;
}
}
}
Test result:
Related
I would like to convert several data sets with a service in the background through Nominatim server. Now I've invested a lot of time to get an answer from the nominatim with the Http client class. Unfortunately, I only ever get a 404 error
Here is my sample code:
namespace ConsoleApp7
{
internal class Program
{
static void Main(string[] args)
{
Task t = new Task(DownloadPageAsync);
t.Start();
Console.WriteLine("Downloading page...");
Console.ReadLine();
}
static async void DownloadPageAsync()
{
// ... Target page.
string page = "https://nominatim.openstreetmap.org/reverse?
format=geocodejson&lat=60.2299&lon=11.1663";
// ... Use HttpClient.
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await
client.GetAsync(page))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
// ... Display the result.
if (result != null)
{
Console.WriteLine(result);
}
}
}
}
}
I'm happy about any help!
Many thanks
The problem is that website doesn't allow any default http request without user agent. You can solve this adding a useragent to your request. Code like below:
static void Main(string[] args)
{
Task t = new Task(DownloadPageAsync);
t.Start();
Console.WriteLine("Downloading page...");
Console.ReadLine();
}
static async void DownloadPageAsync()
{
// ... Target page.
string page = "https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663";
// ... Use HttpClient.
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; AcmeInc/2.0)");
using (HttpResponseMessage response = await
client.GetAsync(page))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
// ... Display the result.
if (result != null)
{
Console.WriteLine(result);
}
}
}
}
You simply need to set a User-Agent
var page = "https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=60.2299&lon=11.1663";
// ... Use HttpClient.
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("User-Agent", "WHATEVER VALUE");
using var response = await client.GetAsync(page);
using var content = response.Content;
// ... Read the string.
var result = await content.ReadAsStringAsync();
// ... Display the result.
if (result != null) Console.WriteLine(result);
I have used SO to help with several issues in the past. However, I cannot find a solution to something I have been struggling with for 2 days now.
I am a noob, please be kind :)
I have an app that I created using Xamarin Studio, targeted for Android. It is a basic GET request from a Rest Api. It was working perfectly until I realized I was not helping myself when it came time to create the same app in IOS and Windows. Once I changed my project to utilize a PCL I started getting errors, primarily around my RestClient class (originally got from http://www.codeproject.com/Tips/497123/How-to-make-REST-requests-with-Csharp)
From my droid app class:
var apiUser = GetString(Resource.String.apiUser);
var apiPass = GetString(Resource.String.apiPass);
//Get token from API
string token = authenticate(apiUser,apiPass);
public static string authenticate(string apiUser, string apiPass)
{
Authentication Auth = new Authentication ();
try
{
// set json by passing AuthenticationUrl as endpoint, returns json data
var o = JObject.Parse(EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));
Auth.Token = (string)o["Token"];
return Auth.Token;
}
catch (Exception e)
{
// Couldn't do stuff. Log the exception.
// TODO possible timeout, try again, if fails again then return error message
if (e.Message.Contains("400") || e.Message.Contains("401"))
{
string error = string.Format("Invalid credentials, please try again");
return error;
} else {
string error = string.Format ("An error occurred: \r\n{0}", e.Message);
return error;
}
}
}
getAuthenticationUrl gets the api URL.
Here is getJson (in PCL):
public static string getJson(string endpoint)
{
string apiurl = endpoint;
var client = new _RestClient();
client.EndPoint = apiurl;
client.ContentType = "application/json";
client.Method = HttpVerb.GET;
//client.Method = HttpVerb.POST;
client.PostData = "";
//client.PostData = "{postData: value}";
//client.PostData = "{'someValueToPost': 'The Value being Posted'}";
var json = client._MakeRequestAsync();
// to append parameters, pass them into make request:
//var json = client.MakeRequest("?param=0");
return json.ToString();
}
And for the _RestClient class (in PCL):
public async Task<string> _MakeRequestAsync()
{
try {
var request = _MakeRequestAsync ("");
return await request;
}
catch (Exception e){
return e.Message;
}
}
public async Task<string> _MakeRequestAsync(string parameters)
{
var uri = new Uri(EndPoint + parameters);
var request = WebRequest.Create(uri) as HttpWebRequest;
using (var response = await request.GetResponseAsync () as HttpWebResponse) {
var responseValue = string.Empty;
if (response.StatusCode != HttpStatusCode.OK) {
var message = String.Format ("Request failed. Received HTTP {0}", response.StatusCode);
throw new Exception (message);
}
// grab the response
using (var responseStream = await Task.Factory.FromAsync<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream, null)) {
//using (var responseStream = response.GetResponseStream ()) {
if (responseStream != null)
using (var reader = new StreamReader (responseStream)) {
responseValue = reader.ReadToEnd ();
}
}
return responseValue;
}
}
responseValue is returning null
return await request is saying "Status = Waiting for activation"
I have also had the error: "Unexpected character encountered while parsing value: S. Path '', line 0, position 0."
But this works if the RestClient class is within Droid (Instead of the shared PCL) and contains the following:
public string MakeRequest ()
{
return MakeRequest ("");
}
public string MakeRequest (string parameters)
{
var request = (HttpWebRequest)WebRequest.Create (EndPoint + parameters);
request.Method = Method.ToString ();
request.ContentLength = 0;
request.ContentType = ContentType;
if (!string.IsNullOrEmpty (PostData) && Method == HttpVerb.POST) {
var bytes = Encoding.GetEncoding ("iso-8859-1").GetBytes (PostData);
request.ContentLength = bytes.Length;
using (var writeStream = request.GetRequestStream ()) {
writeStream.Write (bytes, 0, bytes.Length);
}
}
using (var response = (HttpWebResponse)request.GetResponse ()) {
var responseValue = string.Empty;
if (response.StatusCode != HttpStatusCode.OK) {
var message = String.Format ("Request failed. Received HTTP {0}", response.StatusCode);
throw new ApplicationException (message);
}
// grab the response
using (var responseStream = response.GetResponseStream ()) {
if (responseStream != null)
using (var reader = new StreamReader (responseStream)) {
responseValue = reader.ReadToEnd ();
}
}
return responseValue;
}
}
I cannot figure this out, any help/guidance is appreciated. Let me know if I can clarify anything.
***** UPDATE ***** Thanks to #milen-pavlov help thus far, here is where I am currently at:
in Android project:
var apiUser = GetString(Resource.String.apiUser);
var apiPass = GetString(Resource.String.apiPass);
//Get token from API
var token = await authenticate(apiUser,apiPass);
lblOutput.Text = token;
calls (also in Android project):
public static async Task<string> authenticate(string apiUser, string apiPass)
{
Authentication Auth = new Authentication ();
try
{
// set json by passing AuthenticationUrl as endpoint, returns json data
var o = JObject.Parse(await EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));
Auth.Token = (string)o["Token"];
return Auth.Token;
}
catch (Exception e)
{
if (e.Message.Contains("400") || e.Message.Contains("401"))
{
string error = string.Format("Invalid credentials, please try again");
return error;
} else {
string error = string.Format ("An error occurred: \r\n{0}", e.Message);
return error;
}
}
}
Calls json class in PCL project:
public static async Task<string> getJson(string endpoint)
{
string apiurl = endpoint;
var client = new _RestClient();
client.EndPoint = apiurl;
client.ContentType = "application/json";
client.Method = HttpVerb.GET;
client.PostData = "";
var json = await client._MakeRequestAsync();
return json;
}
which then calls restclient class in PCL project:
public async Task<string> _MakeRequestAsync()
{
var request = _MakeRequestAsync ("");
return await request;
}
public async Task<string> _MakeRequestAsync(string parameters)
{
var uri = new Uri(EndPoint + parameters);
using (var client = new HttpClient())
{
var response = await client.GetAsync(uri);
return await response.Content.ReadAsAsync<string>();
};
}
End result/error:
Any guidance is appreciated!
Can you use HttpClient instead?
Sample Get request will look similar to this:
public async Task<string> _MakeRequestAsync(string parameters)
{
var uri = new Uri(EndPoint + parameters);
using (var client = new HttpClient())
{
var response = await client.GetAsync(uri);
return await result.Content.ReadAsStringAsync();
};
}
I am using .net's Httpclient for the first time and finding it very hard. I have managed to call the server and receive response from it but stuck at reading from the response. Here is my code:
if (Method == HttpVerb.POST)
response = client.PostAsync(domain, new StringContent(parameters)).Result;
else
response = client.GetAsync(domain).Result;
if (response != null)
{
var responseValue = string.Empty;
Task task = response.Content.ReadAsStreamAsync().ContinueWith(t =>
{
var stream = t.Result;
using (var reader = new StreamReader(stream))
{
responseValue = reader.ReadToEnd();
}
});
return responseValue;
}
responseValue has {} in it although the service is returning data. How should I fix the issue?
The project is in .Net 4.
You are creating an asynchronous task but not waiting for it to complete before returning. This means your responseValue never gets set.
To fix this, before your return do this:
task.Wait();
So your function now looks like this:
if (Method == HttpVerb.POST)
response = client.PostAsync(domain, new StringContent(parameters)).Result;
else
response = client.GetAsync(domain).Result;
if (response != null)
{
var responseValue = string.Empty;
Task task = response.Content.ReadAsStreamAsync().ContinueWith(t =>
{
var stream = t.Result;
using (var reader = new StreamReader(stream))
{
responseValue = reader.ReadToEnd();
}
});
task.Wait();
return responseValue;
}
If you prefer to use await (which you possibly should), then you need to make the function this code is contained in async. So this:
public string GetStuffFromSomewhere()
{
//Code above goes here
task.Wait();
}
Becomes:
public async string GetStuffFromSomewhere()
{
//Code above goes here
await ...
}
Try this
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(obj.Url);
HttpWebResponse response = null;
try
{
response = request.GetResponse() as HttpWebResponse;
}
catch (Exception ex)
{
}
How I can convert my traditional HttpWebRequest "POST" call with Async / Await pattern, Here with this I am attaching my current code, Any one please help me to convert this code using Async / Await pattern for windows phone 8.
public void GetEnvironmentVariables(Action<Credentials> getResultCallback, Action<Exception> getErrorCallback)
{
CredentialsCallback = getResultCallback;
ErrorCallback = getErrorCallback;
var uri = new Uri(BaseUri);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/json";
var jsonObject = new JObject
{
new JProperty("apiKey",_api),
new JProperty("affiliateId",_affid),
};
var serializedResult = JsonConvert.SerializeObject(jsonObject);
byte[] requestBody = Encoding.UTF8.GetBytes(serializedResult);
request.BeginGetRequestStream(GetRequestStreamCallback, new object[] { request, requestBody });
}
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
var request = (HttpWebRequest)((object[])asynchronousResult.AsyncState)[0];
using (var postStream = request.EndGetRequestStream(asynchronousResult))
{
var byteArray = (byte[])((object[])asynchronousResult.AsyncState)[1];
// Write to the request stream.
postStream.Write(byteArray, 0, byteArray.Length);
}
request.BeginGetResponse(GetResponseCallback, request);
}
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
var request = (HttpWebRequest)asynchronousResult.AsyncState;
try
{
var response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
if (response != null)
{
var reader = new StreamReader(response.GetResponseStream());
string responseString = reader.ReadToEnd();
Credentails = JsonConvert.DeserializeObject<Credentials>(responseString);
if (Credentails != null && string.IsNullOrEmpty(Credentails.Err))
CredentialsCallback(Credentails);
else
{
if (Credentails != null)
ErrorCallback(new Exception(string.Format("Error Code : {0}", StorageCredentails.Err)));
}
}
}
catch (WebException we)
{
var reader = new StreamReader(we.Response.GetResponseStream());
string responseString = reader.ReadToEnd();
Debug.WriteLine(responseString);
ErrorCallback(we);
}
}
Since Windows Phone 8 doesn't seem to offer the TAP methods you need such as GetRequestStreamAsync the first thing to do is write a little wrapper to provide them for yourself:
public static class WebRequestAsyncExtensions
{
public static Task<Stream> GetRequestStreamAsync(this WebRequest request)
{
return Task.Factory.FromAsync<Stream>(
request.BeginGetRequestStream, request.EndGetRequestStream, null);
}
public static Task<WebResponse> GetResponseAsync(this WebRequest request)
{
return Task.Factory.FromAsync<WebResponse>(
request.BeginGetResponse, request.EndGetResponse, null);
}
}
Note the use of Task.Factory.FromAsync - this is the preferred way to get an await-friendly wrapper around an APM-based async API such as those offered by WebRequest. This is far more efficient than using Task.Factory.StartNew as suggested by someone else, because that would spin up a new thread, whereas this won't need to.
With this in place, you can now write your code in the same way you would on platforms where these TAP-style methods are available (e.g. Windows 8 store apps, desktop apps, etc.):
public async Task GetEnvironmentVariablesAsync(Action<Credentials> getResultCallback, Action<Exception> getErrorCallback)
{
CredentialsCallback = getResultCallback;
ErrorCallback = getErrorCallback;
var uri = new Uri(BaseUri);
var request = (HttpWebRequest) WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/json";
var jsonObject = new JObject
{
new JProperty("apiKey",_api),
new JProperty("affiliateId",_affid),
};
var serializedResult = JsonConvert.SerializeObject(jsonObject);
byte[] requestBody = Encoding.UTF8.GetBytes(serializedResult);
// ASYNC: using awaitable wrapper to get request stream
using (var postStream = await request.GetRequestStreamAsync())
{
// Write to the request stream.
// ASYNC: writing to the POST stream can be slow
await postStream.WriteAsync(requestBody, 0, requestBody.Length);
}
try
{
// ASYNC: using awaitable wrapper to get response
var response = (HttpWebResponse) await request.GetResponseAsync();
if (response != null)
{
var reader = new StreamReader(response.GetResponseStream());
// ASYNC: using StreamReader's async method to read to end, in case
// the stream i slarge.
string responseString = await reader.ReadToEndAsync();
Credentails = JsonConvert.DeserializeObject<Credentials>(responseString);
if (Credentails != null && string.IsNullOrEmpty(Credentails.Err))
CredentialsCallback(Credentails);
else
{
if (Credentails != null)
ErrorCallback(new Exception(string.Format("Error Code : {0}", StorageCredentails.Err)));
}
}
}
catch (WebException we)
{
var reader = new StreamReader(we.Response.GetResponseStream());
string responseString = reader.ReadToEnd();
Debug.WriteLine(responseString);
ErrorCallback(we);
}
}
Note the four lines with // ASYNC: comments - these show where I've made changes. I've collapsed your method down to one, because that's a) possible once you're using async and await and b) much easier than trying to pass things from one method to the next using state arguments.
Notice that the second and fourth of these actually makes async some things you were previously doing synchronously: writing data into the request stream, and reading data out of the response stream. For a small request this probably doesn't matter, but if large amounts of data are being transferred, a synchronous call to Write or ReadToEnd may block. Fortunately, although Windows Phone 8 appears to be missing the TAP methods on WebRequest, it does offer them on Stream and StreamReader so this works without needing to write any extension methods.
I'm new to the community, so here goes my first post. In this case, you can return anytype using a generic Task. This has worked well for me in the past.
Server Side
public class MyController : ApiController
{
public Task<string> PostAsync()
{
return Task.Factory.StartNew(() =>
{
return "populate me with any type and data, but change the type in the response signature.";
});
}
}
Client Side
public class HomeController : Controller
{
public Task<ViewResult> Index()
{
return Task.Factory.StartNew(() =>
{
var model = "use a provider, get some data, or something";
return View(model);
});
}
}
This should do the job:
public async void GetEnvironmentVariables(Action<Credentials> getResultCallback, Action<Exception> getErrorCallback) {
CredentialsCallback = getResultCallback;
ErrorCallback = getErrorCallback;
var uri = new Uri(BaseUri);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/json";
var jsonObject = new JObject {
new JProperty("apiKey", _api),
new JProperty("affiliateId", _affid),
};
var serializedResult = JsonConvert.SerializeObject(jsonObject);
var requestBody = Encoding.UTF8.GetBytes(serializedResult);
var requestStream = request.GetRequestStream();
requestStream.Write(requestBody, 0, requestBody.Length);
await GetResponse(request);
}
private async Task GetResponse(WebRequest request) {
Stream resStream = null;
try {
var response = await request.GetResponseAsync();
if (response == null) {
return;
}
resStream = response.GetResponseStream();
if (resStream == null) {
return;
}
var reader = new StreamReader(resStream);
var responseString = await reader.ReadToEndAsync();
Credentails = JsonConvert.DeserializeObject<Credentials>(responseString);
if (Credentails != null && string.IsNullOrEmpty(Credentails.Err)) {
CredentialsCallback(Credentails);
}
else {
if (Credentails != null) {
ErrorCallback(new Exception(string.Format("Error Code : {0}", StorageCredentails.Err)));
}
}
}
catch (WebException we) {
if (resStream != null) {
var reader = new StreamReader(resStream);
var responseString = reader.ReadToEnd();
Debug.WriteLine(responseString);
}
ErrorCallback(we);
}
}
Suppose I have the following web method using C# and .NET:
[WebInvoke(UriTemplage="/users", Method="POST")]
[OperationContract]
public User AddNewUser(User u);
It is expected that when you implement POST web method you will accept a request body as part of the incoming HTTP request message. The parameter u is expected to be deserialized from the incoming HTTP message body.
My question is: how do we set this request body on the client side ? It's got to be set somewhere. It really confuses me.
Besides if I added "ResponseFormat = WebMessageFormat.Json" to WebInvoke, how can I deserialize from the returned json string into the User object ?
Thanks.
Your question doesn't reveal what you have tried. If you are using .net on the client then you can use the DataContractSerializer to get the serialized data.
You can then use an HttpWebRequest with the method set to POST.
Add the serialized data to the web request and use the GetResponse() method.
Alternatively you could use Fiddlr to test your web service using requests that you create. It gives you a concise view of exactly what is going up to the server.
To perform a POST request to your WCF Rest method:
private string UseHttpWebApproach<T>(string serviceUrl, string resourceUrl, string method, T requestBody)
{
string responseMessage = null;
var request = WebRequest.Create(string.Concat(serviceUrl, resourceUrl)) as HttpWebRequest;
if (request != null)
{
request.ContentType = "application/json";
request.Method = method;
}
if(method == "POST" && requestBody != null)
{
byte[] requestBodyBytes = ToByteArrayUsingJsonContractSer(requestBody);
request.ContentLength = requestBodyBytes.Length;
using (Stream postStream = request.GetRequestStream())
postStream.Write(requestBodyBytes, 0, requestBodyBytes.Length);
}
if (request != null)
{
var response = request.GetResponse() as HttpWebResponse;
if(response.StatusCode == HttpStatusCode.OK)
{
Stream responseStream = response.GetResponseStream();
if (responseStream != null)
{
var reader = new StreamReader(responseStream);
responseMessage = reader.ReadToEnd();
}
}
else
{
responseMessage = response.StatusDescription;
}
}
return responseMessage;
}
private static byte[] ToByteArrayUsingJsonContractSer<T> (T requestBody)
{
byte[] bytes = null;
var serializer1 = new DataContractJsonSerializer(typeof(T));
var ms1 = new MemoryStream();
serializer1.WriteObject(ms1, requestBody);
ms1.Position = 0;
var reader = new StreamReader(ms1);
bytes = ms1.ToArray();
return bytes;
}
Now Assuming your User object as shown below:
Public Class User
{
Public int UserId {get;set;}
Public string UserName {get;set;}
Public string Password {get;set;}
}
The to call the above method i do:
User objUser = new objUser();
objUser.Username = "Test";
objUser.Password = "Test";
UseHttpWebApproach<User>(serviceBaseUrl, "users", "POST", objUser);