CS1503: Argument 2: cannot convert from ‘System.Net.Http.HttpContent’ to ‘System.Net.Http.HttpCompletionOption’ - asp.net-core

I'm trying to make a call to another microservice's get method from a service. But when I try to do so, I'm getting this error:
CS1503: Argument 2: cannot convert from ‘System.Net.Http.HttpContent’ to ‘System.Net.Http.HttpCompletionOption’.
Below is the code in the source microservice to get a list of data from the destination service.
this._httpClient.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "relativeAddress");
request.Content = new StringContent(JsonConvert.SerializeObject(invoiceFilterRequest),
Encoding.UTF8,
"application/json");//CONTENT-TYPE header
var response = await this._httpClient.GetAsync(InvEndpoints.GET_Cus_List, request.Content).ConfigureAwait(false);
Actual error is shown in the 2nd parameter request.Content in the GetAsync() call.
Here is the destination service method:
[HttpGet("cus/filter")]
public async Task<ActionResult<PagedList<Inv>>> GetCusByFilterAsync(InvFilterRequest request)
{
try
{
//..
}
}
Is there any simple solution to send object as a parameter using HTTP GET to another microservice other than sending them as a query string using the above code?

Please check the GetAsync(String, HttpCompletionOption) method, when using the GetAsync method, the second parameter should be the HttpCompletionOption, instead of HttpContent.
From your description, it seems that you want to send a request with parameters, if that is the case, you should use the HttpClient.PostAsync method, instead of the HttpClient.GetAsync method, refer the following sample:
var companyForCreation = new CompanyForCreationDto
{
Name = "Eagle IT Ltd.",
Country = "USA",
Address = "Eagle IT Street 289"
};
var company = JsonSerializer.Serialize(companyForCreation);
var requestContent = new StringContent(company, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("companies", requestContent);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var createdCompany = JsonSerializer.Deserialize<CompanyDto>(content, _options);
Reference: Make HTTP requests using IHttpClientFactory in ASP.NET Cor

Related

How to add JWT Bearer Token to ALL requests to an API

I'm in the process of trying to put together a small project which uses Asp.Net Core Identity, Identity Server 4 and a Web API project.
I've got my MVC project authenticating correctly with IdS4 from which I get a JWT which I can then add to the header of a request to my Web API project, this all works as expected.
The issue I have is how I'm actually adding the token to the HttpClient, basically I'm setting it up for every request which is obviously wrong otherwise I'd have seen other examples online, but I haven't been able to determine a good way to refactor this. I've read many articles and I have found very little information about this part of the flow, so I'm guessing it could be so simple that it's never detailed in guides, but I still don't know!
Here is an example MVC action that calls my API:
[HttpGet]
[Authorize]
public async Task<IActionResult> GetFromApi()
{
var client = await GetHttpClient();
string testUri = "https://localhost:44308/api/TestItems";
var response = await client.GetAsync(testUri, HttpCompletionOption.ResponseHeadersRead);
var data = await response.Content.ReadAsStringAsync();
GetFromApiViewModel vm = new GetFromApiViewModel()
{
Output = data
};
return View(vm);
}
And here is the GetHttpClient() method which I call (currently residing in the same controller):
private async Task<HttpClient> GetHttpClient()
{
var client = new HttpClient();
var expat = HttpContext.GetTokenAsync("expires_at").Result;
var dataExp = DateTime.Parse(expat, null, DateTimeStyles.RoundtripKind);
if ((dataExp - DateTime.Now).TotalMinutes < 10)
{
//SNIP GETTING A NEW TOKEN IF ITS ABOUT TO EXPIRE
}
var accessToken = await HttpContext.GetTokenAsync("access_token");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return client;
}
My StartUp classes are pretty standard from what I gather, but if they could be useful, then I'll add them in.
I've read many articles and I have found very little information about this part of the flow, so I'm guessing it could be so simple that it's never detailed in guides, but I still don't know!
The problem is that the docs are really spread all over, so it's hard to get a big picture of all the best practices. I'm planning a blog series on "Modern HTTP API Clients" that will collect all these best practices.
First, I recommend you use HttpClientFactory rather than new-ing up an HttpClient.
Next, adding an authorization header is IMO best done by hooking into the HttpClient's pipeline of message handlers. A basic bearer-token authentication helper could look like this:
public sealed class BackendApiAuthenticationHttpClientHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _accessor;
public BackendApiAuthenticationHttpClientHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var expat = await _accessor.HttpContext.GetTokenAsync("expires_at");
var dataExp = DateTime.Parse(expat, null, DateTimeStyles.RoundtripKind);
if ((dataExp - DateTime.Now).TotalMinutes < 10)
{
//SNIP GETTING A NEW TOKEN IF ITS ABOUT TO EXPIRE
}
var token = await _accessor.HttpContext.GetTokenAsync("access_token");
// Use the token to make the call.
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
return await base.SendAsync(request, cancellationToken);
}
}
This can be hooked up via DI:
services.AddTransient<BackendApiAuthenticationHttpClientHandler>();
services.AddHttpClient<MyController>()
.ConfigureHttpClient((provider, c) => c.BaseAddress = new Uri("https://localhost:44308/api"))
.AddHttpMessageHandler<BackendApiAuthenticationHttpClientHandler>();
Then you can inject an HttpClient into your MyController, and it will magically use the auth tokens:
// _client is an HttpClient, initialized in the constructor
string testUri = "TestItems";
var response = await _client.GetAsync(testUri, HttpCompletionOption.ResponseHeadersRead);
var data = await response.Content.ReadAsStringAsync();
GetFromApiViewModel vm = new GetFromApiViewModel()
{
Output = data
};
return View(vm);
This pattern seems complex at first, but it separates the "how do I call this API" logic from "what is this action doing" logic. And it's easier to extend with retries / circuit breakers / etc, via Polly.
You can use HttpRequestMessage
// Create this instance once on stratup
// (preferably you want to keep an instance per base url to avoid waiting for socket fin)
HttpClient client = new HttpClient();
Then create an instance of HttpRequestMessage:
HttpRequestMessage request = new HttpRequestMessage(
HttpMethod.Get,
"https://localhost:44308/api/TestItems");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "ey..");
await client.SendAsync(request);

Getting Swagger API response error StatusCode 404, when called from code

While making an API call from swagger-ui, with content type "application/json", it is working fine. But when the same API is being called from below specified code, shows 404 StatusCode . What could be the possible reasons for it? Also, few other APIs from the same swagger-ui, are when being called from the code, they work from this same code.
var serialized = JsonConvert.SerializeObject(data);
String uri = GetEndpointUrl(path);
String responseData = "";
var buffer = System.Text.Encoding.UTF8.GetBytes(serialized);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.Timeout = TimeSpan.FromSeconds(120);
var response = client.PostAsync(uri, byteContent).Result;
responseData = await response.Content.ReadAsStringAsync();

Receive IFileForm in Net Core Controller and forward to another (independent) API

I have an Vue.JS application, where I upload an image to a NetCore Controller.
I'm receiving the IFileForm in the following controller:
[HttpPost("UpdateContactPhoto")]
public async Task<string> UpdateContactPhoto(IFormFile file){ //Forward the original IFileForm to another NetCore API. }
At this point everything is working correctly. IFileForm arrives perfect.
My problem is that I need to forward this IFileForm to another API (independent of this) whose input is an IFileForm with HttpClient PutAsync, but not works.
Can someone help me?
Thanks for help.
You can use this example. Note that the argument name is the same as the item added to the form-data:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:57985");
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
{
data = br.ReadBytes((int) file.OpenReadStream().Length);
}
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent();
multiContent.Add(bytes, "file", file.FileName);
var result = client.PutAsync("api/v1/FileManager", multiContent).Result;
if (result.StatusCode == HttpStatusCode.OK)
{
//do some things
}
}
You can also use this code to get the file from the HttpContext :
IFormFile file = HttpContext.Request.Form.Files[0];
Replace "Target url here" with your destination URL:
HttpClient httpClient = new HttpClient();
var streamcontent = new StreamContent(file.OpenReadStream());
var response = await httpClient.PutAsync("target url here", streamcontent);
Reference:
HttpClient PutAsync
StreamContent class
IFormFile interface

Rest Sharp's AddJsonBody sending parameters in url instead of in body for a POST request

I set my application to send a POST request with parameters to be passed in body using
qs.name = name; qs.id = id
request.AddJsonBody(qs)
But on running the application, i can see the individual parameters in my URL as query string parameters
If i Understand correctly you want to send a json a in the body for a post request, You Should use AddParameter(), Instead of AddJsonBody();
Here is a quick example
public IRestResponse ExamplePost(int id, string name)
{
object tmp = new
{
Id = id,
Name = name
};
string json = JsonConvert.SerializeObject(tmp);
var Client = new RestClient();
Client.BaseUrl = new Uri(YourEndPoint); //Your Url
var request = new RestRequest(Method.POST);
request.Resource = string.Format("/someurl");
request.AddParameter("application/json", json, ParameterType.RequestBody);
IRestResponse response = Client.Execute(request);
Logger.LogInfo($"Sending : {json}");
return response;
}
This will send the following json
{"Id":9939,"Name":"Zander"}

Asp.Net Core - Making API calls from backend

I have an application which is calling API's from a backend cs class, using IHostedService. With basic API calls ("http://httpbin.org/ip") it is working fine and returning the correct value, however I now need to call a Siemens API which requires me to set an Authorization header, and place "grant_type=client_credentials" in the body.
public async Task<string> GetResult()
{
string data = "";
string baseUrl = "https://<space-name>.mindsphere.io/oauth/token";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", {ServiceCredentialID: ServiceCredentialSecret});
using (HttpResponseMessage res = await client.GetAsync(baseUrl))
{
using (HttpContent content = res.Content)
{
data = await content.ReadAsStringAsync();
}
}
}
I think I have the header set up correctly but I won't know for sure until the full request gets formatted. Is it even possible to set the the body of the request to "grant_type=client_credentials"?
As far as I can see from Siemens API documentation they expect Form data, so it should be like:
public async Task<string> GetResult()
{
string data = "";
string baseUrl = "https://<space-name>.mindsphere.io/oauth/token";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", {ServiceCredentialID: ServiceCredentialSecret});
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials")
});
using (HttpResponseMessage res = await client.PostAsync(baseUrl, formContent))
{
using (HttpContent content = res.Content)
{
data = await content.ReadAsStringAsync();
}
}
}
}