currently im facing a problem when calling the ReadAsStringAsync() Method on HttpResponseMessage. When i call the GetAsync() Method on the HttpClient class i read the Contents from the Content property with ReadAsStringAsync(). Normally i would get a JSON string returned, but i always get System.ObjectDisposedException thrown.
Setting the parameter disposeHandler on the HttpClient constructor to false still does not resolve the problem. The Code in particular:
internal async Task<T> HandleMessage<T>(HttpResponseMessage message)
{
string jsonContent = await message.Content.ReadAsStringAsync(); // Error on this line
return JsonConvert.DeserializeObject<T>(jsonContent);
}
I would pass the type and the the response message of the GetAsync()Method.
internal async Task<T> GetRequest<T>(string route)
{
var response = await _httpClient.GetAsync(new Uri(_baseUrl,route));
return await HandleMessage<T>(response);
}
This is the Method i use to call the HandleMessage Method
EDIT:
I forgot some things to mention. Im currently calling the Nextcloud API. When i get a 401 Unauthorized the Exception gets thrown. This itself is not the Problem. I have the NetworkException() Method which takes the Exception and throws a custom Exception.
On a 200 Response Code everything works.
Additonal Code:
Here is my main method. From here i would call my Method GetAllUsers()
Uri url = new Uri("https://192.168.178.30");
SiliconAuthentication siliconAuthentication = new SiliconAuthentication("cloudadmin", "token");
SiliconCloud.SiliconCloud silicon = new SiliconCloud.SiliconCloud(url,siliconAuthentication);
try
{
silicon.SiliconUserInstruction.GetAllUsers().Wait();
}
catch (SiliconNetworkException sne)
{
Console.WriteLine(sne.Message + Environment.NewLine + "Meta Message: " + sne.MetaMessage + Environment.NewLine+"Meta Statuscode: " + sne.StatusCode);
}
Declaring the route and calling the GetRequest Method:
public async Task<SiliconOCSResponse<SiliconResponseAvailableUser>> GetAllUsers()
{
string apiRoute = "/ocs/v2.php/cloud/users";
return await _siliconNetwork.GetRequest<SiliconOCSResponse<SiliconResponseAvailableUser>>(apiRoute);
}
Calling the GetRequest with the api route
internal async Task<T> GetRequest<T>(string route)
{
var response = await _httpClient.GetAsync(new Uri(_baseUrl, route));
try
{
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException eax) // A HTTP error occurs
{
await NetworkException(route, response, eax);
}
return await HandleMessage<T>(response);
}
Network Exception Method throwing a custom exception
internal async Task NetworkException(string route, HttpResponseMessage message, HttpRequestException innerEx)
{
var ms = await message.Content.ReadAsStringAsync(); // Error here
throw new SiliconNetworkException($"Unknown Network Error occured trying to connect to {route}. See Inner Exception for more information",
innerEx,
JsonConvert.DeserializeObject<SiliconOCSResponse<List<string>>>(ms)
);
}
As requested here is the constructor where the HttpClient is initialized
public SiliconNetwork(Uri url, SiliconAuthentication siliconAuth)
{
// https://github.com/nextcloud/server/issues/7753
_httpClient = new HttpClient(new HttpClientHandler() { UseCookies = false });
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ContentType.TypeJSON));
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{siliconAuth.UserName}:{siliconAuth.Password}")));
this._httpClient.DefaultRequestHeaders.Add("OCS-APIRequest", "true");
this._baseUrl= url;
this._siliconAuthentication = siliconAuth;
}
Additionally the stack trace:
at System.Net.Http.HttpContent.CheckDisposed()
at System.Net.Http.HttpContent.ReadAsStringAsync()
at SiliconCloud.Network.SiliconNetwork.<NetworkException>d__22.MoveNext() in C:\Users\Weasel\source\repos\Silicon\SiliconCloud\Network\SiliconNetwork.cs:line 233
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at SiliconCloud.Network.SiliconNetwork.<GetRequest>d__16`1.MoveNext() in C:\Users\Weasel\source\repos\Silicon\SiliconCloud\Network\SiliconNetwork.cs:line 174
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at SiliconCloud.UserManagement.UserProvisioning.SiliconUserInstruction.<GetAllUsers>d__17.MoveNext() in C:\Users\Weasel\source\repos\Silicon\SiliconCloud\UserManagement\UserProvisioning\SiliconUserInstruction.cs:line 278
As mentioned by Jimi in the comments the problem seems to be with EnsureSucessStatusCode() Method which disposes everything after the Exception gets thrown.
internal async Task<T> GetRequest<T>(string route)
{
var response = await _httpClient.GetAsync(new Uri(_baseUrl, route));
if(!response.IsSuccessStatusCode)
{
await NetworkException(route, response);
}
return await HandleMessage<T>(response);
}
Related
My Azure Function code is like below
public static class MyHttpTriggerFunction
{
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
// some business logic
if (valid)
{
return req.CreateResponse(HttpStatusCode.OK, true);
}
else
{
return req.CreateResponse(HttpStatusCode.BadRequest, "some error message");
}
}
}
In my test project I am reading the result like below:
var result = await MyHttpTriggerFunction.Run(req, log).ConfigureAwait(false);
After executing the function, when it try to return the response in result variable, the test method fails with exception.
**
System.InvalidOperationException: The request does not have an
associated configuration object or the provided configuration was
null.
**
I have made sure that test project has the same System.Net.Http.HttpRequestMessageExtension dll.
If I change the function code not to use CreateResponse extension method (this extension method is from the VS 2017 template's code )
and return the response like below, I get the response in test method and the test case runs fine.
var res = new HttpResponseMessage();
if (valid)
{
res.StatusCode = HttpStatusCode.OK;
res.Content = new ObjectContent<bool>(true, new JsonMediaTypeFormatter());
return res;
}
else
{
res.StatusCode = HttpStatusCode.BadRequest;
res.Content = new ObjectContent<string>("some error message", new JsonMediaTypeFormatter());
return res;
}
Below is the stacktrace of error
Result StackTrace: at
System.Net.Http.HttpRequestMessageExtensions.CreateResponse[T](HttpRequestMessage
request, HttpStatusCode statusCode, T value, HttpConfiguration
configuration) at
System.Net.Http.HttpRequestMessageExtensions.CreateResponse[T](HttpRequestMessage
request, HttpStatusCode statusCode, T value) at
MyFunctionApp.MyHttpTriggerFunction.d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at
MyFunctionAppUnitTest.MyHttpTriggerFunctionTest.d__2.MoveNext()
in
C:\Users\rsingh\Desktop\Git_Workspace\ActivationAPI\MyFunctionAppUnitTest\MyHttpTriggerFunctionTest.cs:line
53
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadOperations.ExecuteWithAbortSafety(Action
action) Result Message: Test method
MyFunctionAppUnitTest.MyHttpTriggerFunctionTest.MyHttpTriggerFunction_SuccessResult
threw exception: System.InvalidOperationException: The request does
not have an associated configuration object or the provided
configuration was null.
Am I missing something trivial
The Error message is telling you the problem.
The request does not have an associated configuration object or the provided configuration was null.
When testing the request out side of a httpserver you need to give the request a HttpConfiguration.
// Arrange.
var configuration = new HttpConfiguration();
var request = new System.Net.Http.HttpRequestMessage();
request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = configuration;
//...other code
This is not specific to Azure Functions, but in order to execute this test outside of the context of an actual HTTP request you need to make sure you create an HttpConfiguartion instance, configure it as required (e.g. add any formatters you may need) and call SetConfiguration on the HttpRequestMessage instance with that object.
Example:
var configuration = new HttpConfiguration();
var request = new HttpRequestMessage();
request.SetConfiguration(configuration);
I am trying to make a POST request in an UWP C# app, based on this example - Method A.
The code for my example is:
string scriptname = "myscript.php";
var content = new FormUrlEncodedContent(values);
//Exception Line (103):
var response = await client.PostAsync("https://myserver.ddns.net/" + scriptname, content);
var responseString = await response.Content.ReadAsStringAsync();
string SJson = responseString.ToString();
messagedialog.Content = SJson;
Exception log:
System.Net.Http.HttpRequestException
HResult=0x80072F0D
Message=An error occurred while sending the request.
Source=System.Net.Http
StackTrace:
at System.Net.Http.HttpClientHandler.d__86.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.Http.HttpClient.d__58.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at Aplikacija_iFE.MainPage.d__10.MoveNext() in D:\Onedrive\myproject\myproject\App\App\MainPage.xaml.cs:line 103
Inner Exception 1:
COMException: The text associated with this error code could not be found.
Overitelj digitalnih potrdil ni veljaven ali pa je napačen
The bold string is in my native language and tells me that the CA is invalid or wrong (Basically it is ,because I signed it myself). Can this error be fixed temporarily with some C# code or must I replace the certificate?
My HTTPS (Apache) server is on a Debian 9 machine.
Edit (10:20 PM): Working code
The following code works for now, but it is ugly, highly insecure, and just a shane for me as a student who's new to programming :|
string scriptname = "MyRestAPI.php";
HttpFormUrlEncodedContent content = new HttpFormUrlEncodedContent(values);
HttpResponseMessage response = new HttpResponseMessage();
try
{
client = new HttpClient();
response = await client.PostAsync(new Uri("https://myserver.ddns.net/" + scriptname), content);
}
catch (Exception e)
{
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
ChainValidationResult[] results = new ChainValidationResult []
{
ChainValidationResult.Untrusted, ChainValidationResult.WrongUsage,
ChainValidationResult.BasicConstraintsError, ChainValidationResult.Expired,
ChainValidationResult.IncompleteChain, ChainValidationResult.InvalidCertificateAuthorityPolicy,
ChainValidationResult.InvalidName, ChainValidationResult.OtherErrors,
ChainValidationResult.RevocationFailure, ChainValidationResult.RevocationInformationMissing,
ChainValidationResult.Revoked, ChainValidationResult.UnknownCriticalExtension
};
for(int i=0;i<results.Length;i++)
{
try
{
filter.IgnorableServerCertificateErrors.Add(results[i]);
client = new HttpClient(filter);
response = await client.PostAsync(new Uri("https://myserver.ddns.net/" + scriptname), content);
}
catch
{
continue;
}
}
client = new HttpClient(filter);
response = await client.PostAsync(new Uri("https://myserver.ddns.net/" + scriptname), content);
}
finally
{
client.Dispose();
}
messagedialog.Content = response.Content.ToString();
You can wither use a config to ignore this error in development environment or make your client to trust the certificate, i.e just add the certificate to your trusted root on your client.
My Azure Function code is like below
public static class MyHttpTriggerFunction
{
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
// some business logic
if (valid)
{
return req.CreateResponse(HttpStatusCode.OK, true);
}
else
{
return req.CreateResponse(HttpStatusCode.BadRequest, "some error message");
}
}
}
In my test project I am reading the result like below:
var result = await MyHttpTriggerFunction.Run(req, log).ConfigureAwait(false);
After executing the function, when it try to return the response in result variable, the test method fails with exception.
**
System.InvalidOperationException: The request does not have an
associated configuration object or the provided configuration was
null.
**
I have made sure that test project has the same System.Net.Http.HttpRequestMessageExtension dll.
If I change the function code not to use CreateResponse extension method (this extension method is from the VS 2017 template's code )
and return the response like below, I get the response in test method and the test case runs fine.
var res = new HttpResponseMessage();
if (valid)
{
res.StatusCode = HttpStatusCode.OK;
res.Content = new ObjectContent<bool>(true, new JsonMediaTypeFormatter());
return res;
}
else
{
res.StatusCode = HttpStatusCode.BadRequest;
res.Content = new ObjectContent<string>("some error message", new JsonMediaTypeFormatter());
return res;
}
Below is the stacktrace of error
Result StackTrace: at
System.Net.Http.HttpRequestMessageExtensions.CreateResponse[T](HttpRequestMessage
request, HttpStatusCode statusCode, T value, HttpConfiguration
configuration) at
System.Net.Http.HttpRequestMessageExtensions.CreateResponse[T](HttpRequestMessage
request, HttpStatusCode statusCode, T value) at
MyFunctionApp.MyHttpTriggerFunction.d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at
MyFunctionAppUnitTest.MyHttpTriggerFunctionTest.d__2.MoveNext()
in
C:\Users\rsingh\Desktop\Git_Workspace\ActivationAPI\MyFunctionAppUnitTest\MyHttpTriggerFunctionTest.cs:line
53
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadOperations.ExecuteWithAbortSafety(Action
action) Result Message: Test method
MyFunctionAppUnitTest.MyHttpTriggerFunctionTest.MyHttpTriggerFunction_SuccessResult
threw exception: System.InvalidOperationException: The request does
not have an associated configuration object or the provided
configuration was null.
Am I missing something trivial
The Error message is telling you the problem.
The request does not have an associated configuration object or the provided configuration was null.
When testing the request out side of a httpserver you need to give the request a HttpConfiguration.
// Arrange.
var configuration = new HttpConfiguration();
var request = new System.Net.Http.HttpRequestMessage();
request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = configuration;
//...other code
This is not specific to Azure Functions, but in order to execute this test outside of the context of an actual HTTP request you need to make sure you create an HttpConfiguartion instance, configure it as required (e.g. add any formatters you may need) and call SetConfiguration on the HttpRequestMessage instance with that object.
Example:
var configuration = new HttpConfiguration();
var request = new HttpRequestMessage();
request.SetConfiguration(configuration);
I get somehow a Deadlock/hanging after I purposely catch a WebServiceException to let my application continue. However, even though the application contiues. Doing a webservice hangs and it probably seems like it is still trying to do something from the previous call.
I tried using CancellationTokenSource but that did not seem to solve my problem.
RetryHandler:
public class RetryHandler : DelegatingHandler
{
private const int MaxRetries = 2;
public RetryHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
Exception lastException = null;
for (var i = 0; i < MaxRetries; i++)
{
try
{
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
lastException = ex;
}
if (IsSuccessful(response))
{
return response;
}
}
throw GetException(response, lastException);
}
Calling Post twice makes my program hang:
public async Task<T> Post<T>(
string path,
HttpContent content,
string username,
string token,
HttpMessageHandler handler)
{
var client = new HttpClient(new RetryHandler(handler));
var authString = GetAuthenticationString(username, token);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authString);
AddUsernameAndTokenToClientRequestHeader(client, username, token);
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", content.Headers.ContentType.MediaType);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
var result = await client.PostAsync(new Uri(path), content, _cancelHttpRequests.Token).ConfigureAwait(false);
var resultContent = result.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<T>(resultContent);
}
What is happening here is, although you are catching the exception, and supposedly let your application continue, the service itself continues it's work, async, so that even when you are trying to force the service to continue, it will still go on attempting to complete all of the desired action.
In your case: causing deadlock. Cancellation token won't help here, as your service running async and you already stopped it by catching the exception, so, you are basically doing nothing with this token.
Two ways to solve this:
Either, disconnect the service when you are getting the exception, this way forcing the service to shut.
Or try to work with your service in a sync way so that you can stop the service when ever needed, this way insuring it won't do any additional work when you stop it.
I have code like this:
public async Task<string> getToken()
{
string uri = "http://localhost/api/getToken";
HttpClient client = new HttpClient();
//client.BaseAddress = new Uri(uri);
string token = "asfd";
string baseId = "asfasdf";
string appVersion = "afsadf";
string content = "";
HttpResponseMessage response = null;
try
{
string url = string.Format("{0}?token='{1}'&baseId='{2}'&appVersion='{3}'", uri, token, baseId, appVersion);
//client.BaseAddress = new Uri(url);
response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
content = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
}
return content;
}
And when I get to the line when is GetAsync called VS turn off debug mode and does not throw any exception.
On the server I have breakpoint in the action in controller (mvc web api) and it is not reached.
But when I copy url and past it to the browser action in controller is invoked.
And when I change my url for some other incorrect url, GetAsync throw exception which is captured in catch.
My application is in .net framework 4.5, console application.
Maybe I must add any dll ?
You probably aren't waiting for the asynchronous operation to complete. That's why it gets to that aysnc call, the method returns the task that represents the operation and then the application ends.
You need to await the task getToken returns by using await getToken(). If you call getToken from the main method, which can't be async you need to use Task.Wait to wait synchronously:
static void Main()
{
getToken().Wait();
}