HTTPClient to API, Certificate and SSL errors - c#

I've created a ASP.Net Core 2.1 web application which gathers data from two sources. One is a SQL-database which supplies data via Entity Framework to my application. This one works fine. The other source is a REST API. I'm having troubles connecting to this.
I'm calling this Task which should return a user via his/hers e-mail address:
public async Task<PersonsModel> ReturnPersonByEmail(string email)
{
const string apiKey = "xxx";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://xx.xxx.xxxx.xx:xxx/xxxx/xxx/xx/xx/person/?email={email}");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("x-api-key", "123456");
var url = new Uri(client.BaseAddress.ToString());
string json = "";
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var response = await client.GetAsync(url);
using (var content = response.Content)
{
json = await content.ReadAsStringAsync();
}
}
catch (Exception e)
{
var exception = e;
}
return JsonConvert.DeserializeObject<PersonsModel>(json);
}
}
}
When I try calling it via Postman client.GetAsync(url) always returns an exception:
Message = "The remote certificate is invalid according to the validation procedure."
Message = "The SSL connection could not be established, see inner exception."
I tried adding the following codesegment to launchSettings.json(as per a reply to a similar question posted here: HttpClient client.GetAsync works in full .Net framework but not in .Net core 2.1? ) "DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER": "0" and then I get another error:
Message = "Error 12175 calling WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, 'A security error has occurred'."
If you have any idea on what might cause this problem I would be very grateful for any help. Thanks!

Related

Azure function Error : received an unexpected eof or 0 bytes from the transport stream

I have a requirement where I am calling an API (programmatically PUT Method) from another API.
Both of the APIs are hosted as Azure Function App.
The request has nearly 600 rows.
The below method call is throwing the error: received an unexpected EOF or 0 bytes from the transport stream
If I send a request say 100-150 rows, it processes successfully.
I think that it is nothing to do with the code, it is related to the Azure Function app.
Please let me know if I need to add any configuration to the Azure Function app.
Thanks in Advance.
public async Task<List<CarPricing>> TestMethod(CarPricingModel request, string resourcePath,string token)
{
try
{
using var stream = StreamUtility.GenerateStreamFromString(JsonConvert.SerializeObject(request));
using var data= new StreamContent(stream);
data.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var queryParams = new Dictionary<string, string>()
{
{"id", "XXXXXXXXXXXXXXXXXXXXXX" }
};
var relativeUrl = QueryHelpers.AddQueryString(resourcePath, queryParams);
using var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Put,
Content = content,
RequestUri = new Uri(relativeUrl, UriKind.Relative)
};
var httpResponseMessage = await _httpClient.SendAsync(requestMessage);
httpStatusCode = httpResponseMessage.StatusCode;
var httpResponse = await httpResponseMessage.Content.ReadAsStreamAsync();
using var responseContent = new JsonTextReader(new StreamReader(httpResponse));
var response = new JsonSerializer().Deserialize<List<CarPricing>>(responseContent);
return response;
}
catch (Exception ex)
{
_log.LogError("API error {err_msg}",ex.Message);
throw;
}
}
Check the below steps that might help to fix the issue:
received an unexpected eof or 0 bytes from the transport stream
This error generally occurs during the HTTP Calls of .NET Core Applications.
TLS/SSL binding is supported in the Azure Function App. You can bind it through Azure Portal and using the Code.
If you’re using the HTTPS Protocol, apply this SSL Call before the request made as mentioned in the comments:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
or
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Ssl3| SecurityProtocolType.Tls| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12
but the above error might come for many causes such as:
Client IP may be restricted, which you can add in Access Restrictions of the Function app API.
Typo Mistake or Incorrect URL of the API that is called programmatically from another Azure Function API.
Refer to this MS Doc for using the TLS/SSL Certificate Programmatically and SO Thread that shows how to use the TLS/SSL Certificate in Azure Function App.

RestSharp is throwing a bad request on a GET call, but is missing response body

I am making a GET call to an external REST API. I was putting in a parameter incorrectly and was getting an HttpRequestException. When catching the exception the only details it returns are:
Request failed with status code BadRequest
When monitoring the traffic using Fiddler I can see the server was returning a json message in the body explaining why the call failed.
Is there anyway to access this message from RestSharp?
I am using .net 6 and RestSharp version 108.0.2
you must set ThrowOnAnyError from RestClientOptions to false :
private async Task<T?> SendRequest<T>()
{
string url = "your request url";
var options = new RestClientOptions(url)
{
ThrowOnAnyError = false,
MaxTimeout = 6000
};
var client = new RestClient(options);
var request = new RestRequest().AddHeader("Content-Type", "application/json");
request.Method = Method.Post;
var response = await client.ExecuteAsync<T>(request);
return response.Data;
}

How to create a RabbitMQ user using the RabbitMQ API from a c# console app

I am working on a Hashicorp Vault management .net-core 3.1.3 console application written in C#. I have been tasked with creating a RabbitMQ user on an MQ server from the console app utilizing the RabbitMQ restful API. I have zero experience with this API. I have been reading the documentation but still don't have a clue as to how to begin.
I have limited experience with APIs in general and have never tried to do anything like this from a console app.
Any guidance or example code would be greatly appreciated.
Cheers.
Matt
You'll need the RabbitMQ Management HTTP API, the docs for which are here. Specifically you'll want to PUT a user on the /api/users/name endpoint.
There are many ways to make an HTTP request in c#, the simplest is probably the WebRequest class as documented here. You'll need to set the method to PUT, write your json payload to the request and set your rabbitmq credentials for the request.
Thanks for the clue-bat Adam. Here is where I ended up, and works well.
try
{
// Set MQ server credentials
NetworkCredential networkCredential = new NetworkCredential("mqUserName", "mqPassword");
// Instantiate HttpClientHandler, passing in the NetworkCredential
HttpClientHandler httpClientHandler = new HttpClientHandler { Credentials = networkCredential };
// Instantiate HttpClient passing in the HttpClientHandler
using HttpClient httpClient = new HttpClient(httpClientHandler);
// Get the response from the API endpoint.
HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("http://YourServer:AndPort/api/users/");
// Get the response content.
HttpContent httpContent = httpResponseMessage.Content;
// Get the stream of the content.
using StreamReader streamReader = new StreamReader(await httpContent.ReadAsStreamAsync());
// Get the output string.
string returnedJsonString = await streamReader.ReadToEndAsync();
// Instantiate a list to loop through.
List<string> mqAccountNames = new List<string>();
if (returnedJsonString != "")
{
// Deserialize into object
dynamic dynamicJson = JsonConvert.DeserializeObject(returnedJsonString);
if (dynamicJson != null)
{
foreach (dynamic item in dynamicJson)
{
mqAccountNames.Add(item.name.ToString());
}
}
}
bool accountExists = false;
foreach (string mqAccountName in mqAccountNames)
{
if (mqAccountName == userName)
{
accountExists = true;
}
}
switch (accountExists)
{
case true:
Console.WriteLine("This user already exists on the MQ server.");
break;
case false:
// Create the new user on the MQ Server
Console.WriteLine("This user will be created on the MQ server.");
string uri = $"http://YourServer:AndPort/api/users/{userName}";
MqUser mqUser = new MqUser
{
password = password,
tags = "administrator"
};
string info = JsonConvert.SerializeObject(mqUser);
StringContent content = new StringContent(info, Encoding.UTF8, "application/json");
httpResponseMessage = await httpClient.PutAsync(uri, content);
if (!httpResponseMessage.IsSuccessStatusCode)
{
Console.WriteLine("There was an error creating the MQ user account.");
Thread.Sleep(2500);
return false;
}
uri = $"http://YourServer:AndPort/api/permissions/%2F/{userName}";
MqPermissions mqPermissions = new MqPermissions
{
configure = ".*",
write = ".*",
read = ".*"
};
info = JsonConvert.SerializeObject(mqPermissions);
content = new StringContent(info, Encoding.UTF8, "application/json");
httpResponseMessage = await httpClient.PutAsync(uri, content);
if (!httpResponseMessage.IsSuccessStatusCode)
{
Console.WriteLine("There was an error creating the permissions on the MQ user account.");
Thread.Sleep(2500);
return false;
}
break;
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
I created simple MqUser and MqPermissions classes so I could just JsonConvert.SerializeObject to pass the info.
Another weird thing was that my company chose to name the MQ Virtual Host as "/".
This had not been an issue up to this point as we had never tried to use the API before.
Since the / character is expected in a uri, this was a hangup, but I tried encoding it as %2F and it works just fine.

Connection issue with TLS 1.1 and 1.2 - Azure web app

In my application I am calling an API written in WebAPI and hosted in a PaaS environment in azure from another WebAPI method (ideally an internal service call), say MethodA in WebApp_A is calling MethodB in WebApp_B. But i am getting the mentioned error if the TLS settings of WebApp_B is either 1.1 or 1.2 ( it works with 1.0).
"Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host."
I have similar WebApp (WebApp_C) which doesn't have the error with TLS. Below is the code that we use to call the MethodB in WebApp_B from WebApp_A
public async Task<ServiceResponse> CreateDialog(RequestObject requestObject)
{
ServiceResponse serviceResponse = new ServiceResponse();
try
{
using (var client = new HttpClient())
{
SessionTokenBo jwtData = new SessionTokenBo();
Logger = _logger;
jwtData = GetInternalToken(InternalCallTypes.Utilities.ToString(), int.Parse(ServiceConfiguration.TokenExpiryWindow), int.Parse(ServiceConfiguration.InternalTokenExpiryWindow));
if (null != jwtData)
{
client.BaseAddress = new Uri("URI");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtData.Token); HttpResponseMessage response = await client.PostAsJsonAsync("service/methodB", requestObject);
if (response.IsSuccessStatusCode)
{
var data = response.Content.ReadAsStringAsync().Result;
serviceResponse = JsonConvert.DeserializeObject<ServiceResponse>(data);
}
}
}
}
catch (Exception ex)
{
throw
}
return serviceResponse;
}
If i give Security protocol like this , it will work
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Also i tried from postman with the request and it is not failing. So now am confused because if from postman its working then ideally its not the WebApp setup issue
TLS 1.0 is no longer the default. Add this line before making the request:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Have a Reference to this issue.
Update:
but my doubt is why the same code is not failing for other web apps ?
Found the issue, it was due the target framework was 4.5.2 in webconfig.
<compilation targetFramework="4.7.2"></compilation>
<httpRuntime targetFramework="4.7.2" />

C# IBM Speech to Text Get Token using APIKey

Based on the example at https://gist.github.com/nfriedly/0240e862901474a9447a600e5795d500,
I am trying to use WebSocket to use the IBM Speech to Text API.
But I am having problems with the authentication part.
It looks like now IBM does not provide a username/password anymore.
Only an api key.
So I cannot find a way to added that example to use an api to get the token.
Any know how to use WebSocket with the IBM apikey for authentication?
The IBM doc does not seem to be up to date either as their example are using CURL with username and password https://console.bluemix.net/docs/services/speech-to-text/getting-started.html#getting-started-tutorial
I even saw somewhere that I could replace the username with "api" and the password by my apikey.
But that's not working as I get an Unauthorized error from the server.
Maybe I misread and I should pass a token instead of the password.
But then how do I get a token from my APIkey with websockets?
I can get a token using HttpClient without problems.
But it looks like I cannot use that token with Websocket after that, only further HttpClient calls.
With some help I finally found how to handle the WebSocket with the apiKey.
I post the code here in case someone else needs it
IamTokenData GetIAMToken(string apikey)
{
var wr = (HttpWebRequest)WebRequest.Create("https://iam.bluemix.net/identity/token");
wr.Proxy = null;
wr.Method = "POST";
wr.Accept = "application/json";
wr.ContentType = "application/x-www-form-urlencoded";
using (TextWriter tw = new StreamWriter(wr.GetRequestStream()))
{
tw.Write($"grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={apikey}");
}
var resp = wr.GetResponse();
using (TextReader tr = new StreamReader(resp.GetResponseStream()))
{
var s = tr.ReadToEnd();
return JsonConvert.DeserializeObject<IamTokenData>(s);
}
}
IamTokenData tokenData = GetIAMToken([Your IamApiKey]);
CancellationTokenSource cts = new CancellationTokenSource();
ClientWebSocket clientWebSocket = new ClientWebSocket();
clientWebSocket.Options.Proxy = null;
clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {token.AccessToken}");
// Make the sure the following URL is that one IBM pointed you to
Uri connection = new Uri($"wss://gateway-wdc.watsonplatform.net/speech-to-text/api/v1/recognize");
try
{
//await clientWebSocket.ConnectAsync(connection, cts.Token);
clientWebSocket.ConnectAsync(connection, cts.Token).GetAwaiter().GetResult();
Console.WriteLine("Connected!");
}
catch (Exception e)
{
Console.WriteLine("Failed to connect: " + e.ToString());
return null;
}
// ... Do what you need with the websocket after that ...

Categories

Resources