I've created a custom DNN module that uses HTTPClient to send information to an external API. My HttpClient method is as follows:
public static async Task<string> CreatePayerResponse()
{
var credentials = GetCredentials();
var objEventLog = new EventLogController();
var gatewaySettings = new GatewaySetting_ProPay();
SignupResult_ProPay result = new SignupResult_ProPay();
using (HttpClient client = new HttpClient(new LoggingHandler(new HttpClientHandler())))
{
HttpRequestMessage request = new HttpRequestMessage();
HttpResponseMessage response = new HttpResponseMessage();
response.EnsureSuccessStatusCode();
client.BaseAddress = new Uri("https://xmltestapi.propay.com/ProPayAPI/signup");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string responseBody;
GatewaySetting_ProPay gatewaySetting = new GatewaySetting_ProPay();
SignupRequest payerRequest = new SignupRequest();
HttpContent content = new StringContent(payerRequest.ToString());
try
{
request.Headers.Add("Authorization", credentials);
request.Headers.Add("accept", "application/json");
response = await client.PutAsync("https://xmltestapi.propay.com/ProPayAPI/signup", content);
responseBody = await response.Content.ReadAsStringAsync();
objEventLog.AddLog("Merchant Onboarding Request Sent", portalSettings, userId,
response.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
Console.WriteLine(responseBody);
}
catch (HttpRequestException ex)
{
result.Succeeded = false;
result.Status = ex.Message;
objEventLog.AddLog("Merchant Onboarding Error!", portalSettings, userId, response.ToString(),
EventLogController.EventLogType.ADMIN_ALERT);
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ", ex.Message);
}
return response.Content.ToString();
}
}
public static string GetCredentials()
{
GatewaySetting_ProPay gatewaySettings = new GatewaySetting_ProPay();
var billerAccountId = "mycreds";
var authToken = "mycreds";
var encodedCredentials =
Convert.ToBase64String(Encoding.Default.GetBytes(billerAccountId + ":" + authToken));
var credentials = string.Format("Basic {0}", encodedCredentials);
return credentials;
}
When I wire this method up to a click event, an HTTP 302 response is received and nothing is sent to the API. What modifications are needed to ensure proper transmission?
Update
I still receive the following response:
Error code 302 was received from server response.
This is despite implementing the AllowAutoRedirect property and setting it to true. Here's the LoggingHandler class I've written:
public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken, HttpClientHandler handler)
{
Console.WriteLine("Request:");
Console.WriteLine(request.ToString());
if (request.Content != null)
{
Console.WriteLine(await request.Content.ReadAsStringAsync());
}
Console.WriteLine();
handler.AllowAutoRedirect = true;
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine("Response:");
Console.WriteLine(response.ToString());
if (response.Content != null)
{
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
Console.WriteLine();
return response;
}
Is this the proper way to implement HttpClientHandler and associated properties?
Make sure that instance of HttpClientHandler has AllowAutoRedirect property set true and is used by your HttpClient.
After implementing trace logs for System.Net.Http and related namespaces, the logs stated that the connection was forcibly closed. After further research, it turns out the .NET Framework 4.5 is not compatible with more modern Transport Layer Security (TLS) versions. As such, the approach of calling the API from our DNN application had to be jettisoned because the source code we're extending targets .NET Framework version 4.5.
Related
ASP.Net Core Web API Call Thirds party API fails intermittently.
The following exception raises intermittently when load test with postman.
"Call failed with status code 500 (Internal Server Error): POST https://sample.com/apiendpoint."
I tried the Named/Typed with HttpClient/IHttpClientFactory approach and the problem continues.
How to make sure it uses connection pooling and not create new on one.
what is the right value for SetHandlerLifetime to keep the connection in the pool for future call to use.
services.AddHttpClient<IRestService, RestServiceOne>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes
.AddPolicyHandler(GetRetryPolicy());
The following code is in RestServiceOne.cs
public class RestServiceOne : IRestService
{
private readonly IHttpClientFactory _httpClientFactory;
public RestServiceOne(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<HttpResponseMessage> GetDataAsync(string destinationUrl, string user,
string password, string requestXml, string orderNumber, CancellationToken cancellationToken)
{
var endpoint = $"{destinationUrl}";
var authToken = Encoding.ASCII.GetBytes($"{user}:{password}");
var data = new StringContent(requestXml, Encoding.UTF8, "text/xml");
var httpRequestMessage = new HttpRequestMessage(
HttpMethod.Post,
endpoint)
{
Headers =
{
{ "Accept", "application/vnd.github.v3+json" },
{ "User-Agent", "HttpRequestsConsoleSample" }
}
};
httpRequestMessage.Content = data;
var httpClient = _httpClientFactory.CreateClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(authToken));
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
return httpResponseMessage;
}
}
I also tried HttpClient injection given in Microsoft type example.
public class RestService
{
private readonly HttpClient _httpClient;
public RestService(HttpClient httpClient)
{
_httpClient = httpClient;
try
{
_httpClient.BaseAddress = new Uri("https://testxx.com/test");
// GitHub API versioning
_httpClient.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
_httpClient.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
}
catch(Exception ex)
{
}
}
public async Task<HttpResponseMessage> GetDataAsync(string destinationUrl, string user,
string password, string requestXml, string orderNumber, CancellationToken cancellationToken)
{
var endpoint = $"{destinationUrl}";
var authToken = Encoding.ASCII.GetBytes($"{user}:{password}");
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(authToken));
var data = new StringContent(requestXml, Encoding.UTF8, "text/xml");
try
{
var response = await _httpClient.PostAsync(endpoint, data);
return response;
}
catch(Exception ex)
{
throw;
}
}
}
I tried with Flurl directly in service layer
var endpoint = $"{destinationUrl}";
var authToken = Encoding.ASCII.GetBytes($"{user}:{password}");
var data = new StringContent(requestXml, Encoding.UTF8, "text/xml");
try
{
var response = await endpoint
.WithHeader("Content-Type", "text/xml")
.WithHeader("app-bu-id", "SANIDERMMEDICAL")
.WithHeader("Authorization", new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)))
.PostAsync(data);
The above all 3 approach failed.
what is right value for .SetHandlerLifetime() to keep the connection in the pool and avoid creating new.
I saw some other example using the following approach but how to use this with IHttpClientFactory / Flurl.
var socketsHandler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(10),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),
MaxConnectionsPerServer = 10
};
var client = new HttpClient(socketsHandler);
How can I ensure it use connection pooling and avoid the 500 error when calling 3rd party API from Azure.
I found solution when I use httpclient as given below.
private static readonly HttpClient httpClient = new HttpClient(new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromSeconds(60),
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(20),
MaxConnectionsPerServer = 10
});
This article helped me
Tried couple of value for SocketsHttpHandler but finally choose this since no error.
i am trying to call the Zoom Api for Access token. It work Perfectly fine when i am trying to post with postman .
but from code it does not respond
ZoomApiLink_StepNo2
below are the following details
public static async Task<String> PostTogetToken<T>(string requestUrl, string client_Secretkey) {
ZoomToken hello = new ZoomToken();
var EncodedURl = ApiService.Base64Encode(client_Secretkey);
using (var _httpClient = new HttpClient()) {
if (!string.IsNullOrEmpty(requestUrl))
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Basic " + EncodedURl);
var httpContent = new StringContent("", System.Text.Encoding.UTF8, "application/x-www-form-urlencoded");
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(requestUrl)) {
Version = HttpVersion.Version11,
Content = httpContent
};
using (var response = await _httpClient.SendAsync(httpRequestMessage)) {
if (response.IsSuccessStatusCode) {
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
} else {
return await response.Content.ReadAsStringAsync();
}
}
}
}
You're seeing a common deadlock problem because code further up the stack is using Result, blocking on asynchronous code.
To fix, change the Result to await and use async all the way.
I would like to ask help how can I fix the issue in the header of my httpclient request.
This is ebay restful api in creating a fulfillment shipment. I am able to create in Postman but when I tried it in VS, it won't work with error bad request. Screenshot below using postman.
Codes below in ASP.NET
private HttpClient CreateHttpClient()
{
var client = new HttpClient();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
string baseAddress = WebApiBaseAddress;
client.Timeout = new TimeSpan(0, 5, 59);
client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", _cred.eBayToken));
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
return client;
}
public HttpResponseMessage PostHttpResponse(string requestUri, object data)
{
var stringPayload = JsonConvert.SerializeObject(data);
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
httpContent.Headers.Add("Content-Language", "en-US");
using (var client = CreateHttpClient())
{
try
{
HttpResponseMessage response = client.PostAsJsonAsync(requestUri, httpContent).Result;
if (response.IsSuccessStatusCode)
{
return response;
}
else
{
GetErrorsResponse(response);
throw new HttpRequestException(string.Format("There was an exception trying to post a request. response: {0}", response.ReasonPhrase));
}
}
catch (HttpRequestException ex)
{
throw ex;
//return null;
}
}
}
I was able to fix the issue by not converting the request to json but send as object. Though the error provided is very generic and could not identify the main issue. Upon asking to someone has experienced in ebay integration, the main issue is to provide all the needed in the headers.
public HttpResponseMessage PostHttpResponse(string requestUri, object data)
{
using (var client = CreateHttpClient())
{
try
{
HttpResponseMessage response = client.PostAsJsonAsync(requestUri, data).Result;
if (response.IsSuccessStatusCode)
{
return response;
}
else
{
GetErrorsResponse(response);
throw new HttpRequestException(string.Format("There was an exception trying to post a request. response: {0}", response.ReasonPhrase));
}
}
catch (HttpRequestException ex)
{
throw ex;
//return null;
}
}
}
And in the httpclient needs to add the header.
private HttpClient CreateHttpClient()
{
var client = new HttpClient();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
string baseAddress = WebApiBaseAddress;
if (string.IsNullOrEmpty(baseAddress))
{
throw new HttpRequestException("There is no base address specified in the configuration file.");
}
client.Timeout = new TimeSpan(0, 5, 59);
client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", _cred.eBayToken));
client.DefaultRequestHeaders.Add("Accept-Language", "en-US");
client.DefaultRequestHeaders.Add("Accept-Charset", "utf-8");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("LegacyUse", "true");
return client;
}
I've written a python function to move a router's IO port via a HTTP post request with Basic Authentivation. This works fine. But now I'd like to implement the sam with C#.
Here is my python function:
def io_on(ip='192.168.2.1', username='adm', password='123456'):
if not isinstance(ip, str):
print('not string')
try:
payload ='_ajax=1&_web_cmd=%21%0Aio%20output%201%20on%0A'
r = requests.post('http://{}/apply.cgi'.format(ip), auth=HTTPBasicAuth(username, password), data=payload, timeout=3)
if r.status_code == 200:
print('{} : IO ON'.format(ip))
elif r.status_code == 401:
print('{} : Auth error'.format(ip))
else:
print(r.status_code)
except Exception as e:
print(e)
I've experimented with NetWorkCredentials with no success.
Something like this :
try
{
string username = "adm", password = "123456";
string payload = "http://192.168.2.1/apply.cgi/?_ajax=1&_web_cmd=%21%0Aio%20output%201%20on%0A";
HttpClient client = new HttpClient();
var byteArray = Encoding.ASCII.GetBytes($"{username}:{password}");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
HttpResponseMessage response = await client.GetAsync(payload);
HttpContent content = response.Content;
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Success");
}
else if (response.StatusCode == HttpStatusCode.Unauthorized)
{
Console.WriteLine("Auth error");
}
else
{
Console.WriteLine(response.StatusCode);
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
Here's my way to make POST with basic authentication.
var authValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{login}:{password}")));
using (var client = new HttpClient() { DefaultRequestHeaders = { Authorization = authValue } })
{
HttpResponseMessage response = client.PostAsync("https://localhost:44396/Documentation/All?pageNumber=0&pageSize=10", httpContent).Result;
if (response.IsSuccessStatusCode)
{
response = await response.Content.ReadAsStringAsync();
}
}
I'm trying to access a rest endpoint, https://api.planet.com/auth/v1/experimental/public/users/authenticate. It is expecting json in the request body.
I can get the request to work in Postman but not using c#. Using postman I get the expected invalid email or password message but with my code I get "Bad Request" no matter I try.
Here is the code that makes the request
private void Login()
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://api.planet.com/");
client.DefaultRequestHeaders.Accept.Clear();
//ClientDefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
Data.User user = new Data.User
{
email = "myemail#company.com",
password = "sdosadf"
};
var requestMessage = JsonConvert.SerializeObject(user);
var content = new StringContent(requestMessage, Encoding.UTF8, "application/json");
var response = client.PostAsync("auth/v1/experimental/public/users/authenticate", content).Result;
Console.WriteLine(response.ToString());
}
catch (WebException wex )
{
MessageBox.Show(wex.Message) ;
}
}
class User
{
public string email;
public string password;
}
Here are screen grabs form Postman that are working
The way to get this to work was to alter the content header "content-type". By default HTTPClient was creating content-type: application/json;characterset= UTF8. I dropped and recreated the content header without the characterset section and it worked.
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "application/json");
The issue is you are trying to call an async method without waiting for the response using await method or var task = method; task.Wait() Therefore, when you end up doing response.ToString() it returns the text you are seeing.
One way to handle this within a non-async method would be to do the following:
var task = client.PostAsync("auth/v1/experimental/public/users/authenticate", content);
task.Wait();
var responseTask = task.Content.ReadAsStringAsync();
responseTask.Wait();
Console.WriteLine(responseTask.Result);
Another way is to make the current method async by doing private async void Login() and then do:
var postResp = await client.PostAsync("auth/v1/experimental/public/users/authenticate", content);
var response = await postResp.Content.ReadAsStringAsync();
Console.WriteLine(response);
Create a Method Like this...
static async Task<string> PostURI(Uri u, HttpContent c)
{
var response = string.Empty;
var msg = "";
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.PostAsync(u, c);
msg = await result.Content.ReadAsStringAsync();
if (result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString();
}
}
return response;
}
call In your Method
public void Login()
{
string postData ="{\"email\":\"your_email\",\"password\":\"your_password\"}";
Uri u = new Uri("yoururl");
var payload = postData;
HttpContent c = new StringContent(payload, Encoding.UTF8,"application/json");
var t = Task.Run(() => PostURI(u, c));
t.Wait();
Response.Write(t.Result);
}