I'm fairly new to .NET's HTTPClient class, hence kindly excuse if I sounded noob. I'm tryin to replicate Postman's POST request in C# .Net and written following code. However I'm not getting any response but StatusCode: 404. Could someone assist understanding where I'm going wrong?
Also I'd like to understand, how do set Body in following code.
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://testURL.com"),
Timeout = TimeSpan.FromMinutes(10)
};
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("audio/wav"));
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic ldjfdljfdlfjdsjfdsl");
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("model", "Test"),
});
var result = httpClient.PostAsync("api/v1/recognize", content).Result;
Here is what I'm doing in Postman and it works:
"Params" in Postman refers to query parameters which are appended to the URL. You'll see that the URL in Postman contains the parameters you added in the "Params" tab:
However, it seems those are just dummy values you've entered so perhaps you don't need them? In any case, the way you add query parameters to the request for HttpClient is a little different as it needs to be added to the URL.
After that you also need to add the audio file as content to your request. At the moment you're setting the "Accept" header to "audio/wav" but you probably want to set the "Content-Type" header instead (or are you expecting a WAV file to be returned in the response too?).
As far as I can see this is what you're missing:
using (var httpClient = new HttpClient())
{
httpClient.Timeout = TimeSpan.FromMinutes(10);
// Set request headers
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic ldjfdljfdlfjdsjfdsl");
// Set query parameters
var uriBuilder = new UriBuilder("https://testURL.com/api/v1/recognize");
uriBuilder.Query = "model=Test";
// Build request body
// Read bytes from the file being uploaded
var fileBytes = File.ReadAllBytes(wavFilePath);
// Create request content with metadata/headers to tell the
// backend which type of data (media type) is being uploaded
var byteArrayContent = new ByteArrayContent(fileBytes);
byteArrayContent.Headers.ContentType = MediaTypeHeaderValue.Parse("audio/wav");
// Wrap/encode the content as "multipart/form-data"
// See example of how the output/request looks here:
// https://dotnetfiddle.net/qDMwFh
var requestContent = new MultipartFormDataContent
{
{byteArrayContent, "audio", "filename.wav"}
};
var response = await httpClient.PostAsync(uriBuilder.Uri, requestContent);
}
I haven't tested this of course against your application, but it should be something along the lines of this. It might be that the backend doesn't expect "multipart/form-data" and just needs the "audio/wav". I can't see the output headers in your Postman screenshots, but if so, you can use byteArrayContent directly instead of wrapping it in MultipartFormDataContent.
Note: Don't use httpClient.PostAsync(...).Result. If you want to use the asynchronous method, you should await it. Depending on your code, using Result might give you problems if you're not careful. And remember to dispose the HttpClient after use (easiest solution is to use a using statement). If you plan on reusing the HttpClient for more requests, you can avoid disposing it until you're done.
Related
It's a generic question, but I need help with my specific case.
I have a simple GET endpoint (see image) which I've tested with Postman and it works
It takes two id tokens in the header and thats it.
I've put breakpoints in the code and copied the exact instance of the ids into Postman and the request works, but executing from code, I get a 400 response
using (HttpClient client = new HttpClient())
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri("https://*******.execute-api.ap-southeast-2.amazonaws.com/dev/uploads/image.jpg"),
Method = HttpMethod.Get,
};
var idToken = Application.Current.Properties["id_token"].ToString();
var accessToken = Application.Current.Properties["access_token"].ToString();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Add("Id-Token", idToken);
request.Headers.Add("Access-Token", accessToken);
var response = await client.SendAsync(request);
}
I've tried with and without the content-type header and makes no difference. Also doesn't matter if it's present in Postman
This is a Xamarin project which is where Application.Current.Properties comes from. I'm utilising other endpoints in the application are there are no issues with accessing the tokens like this.
I'm using an API that's in beta and it's essentially a querystring for a Mongodb db as a parameter. Refit is encoding my url, which included plenty of curly braces, and their server doesn't like it and it essentially ignores all of my query attributes.
I've tried passing the entire query as a string, which doesn't work but also doesn't chuck an error. I've found a commit to Refit which directly addresses this issue here.
That says to use the header [QueryUriFormat(UriFormat.Unescaped)], which sounds great, but I'm clearly not using the library the QueryUriFormat attribute is in and I can't find it in a google search.
In the interface:
[QueryUriFormat(UriFormat.Unescaped)]
[Headers("Authorization: Basic")]
[Get("/stuff/etc")]
Task<myModel> GetStuff(string q);
The string:
"filter={name:'THENAME',timestamp:{$gt:1571238110000},TOTAL_MB: {$gt:0},thingyId:{$eq:2500}}";
The call:
var result = client.GetUsageSince(stringB).Result;
I tried the link on the first comment (below)
var HttpClient = new HttpClient(new UriQueryUnescapingHandler()) { BaseAddress = new Uri(url) };
HttpClient.DefaultRequestHeaders.Add("Accept", "application/json");
HttpClient.DefaultRequestHeaders.Add("Content", "application/json");
var byteArray = Encoding.UTF8.GetBytes($"{username}:{password}");
HttpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
Stuff = RestService.For<IStuff>(HttpClient, refitSettings);
After doing this it looks like the absolute Uri is now correct, but the basic auth header is not working on my customer HttpClient.
I am trying to set up if-match header as following and making use of HttpClient available in System.Net.Http:
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var adobeRequest = new HttpRequestMessage(HttpMethod.Put, new Uri(url))
{
Content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json")
};
if (!string.IsNullOrEmpty(Etag))
adobeRequest.Headers.Add("If-Match", Etag);
var response = client.SendAsync(adobeRequest);
Etag I received from adobe in previous(Get) call is :
64E8BBA87ACFD0C2C84AF6E1193A3761.5334C3A18AB5A054FF3DBC33AFBDF6C
So when I try to add the same for Put request, it gives me following error:
The format of value
'64E8BBA87ACFD0C2C84AF6E1193A3761.5334C3A18AB5A054FF3DBC33AFBDF6C' is
invalid.
How to resolve this issue? It clearly says format is not valid, however I believe Adobe's api is being used by million others. So somehow Its something from my end.
Link for Adobe api
Use adobeRequest.Headers.TryAddWithoutValidation instead.
I am working with an API that requires a call with the PATCH verb. I am trying to issue a request using the HttpClient object in C#. The request sends, however the JSON is not present in the body. Instead it is blank. Relevant code below.
var patch = new HttpMethod("PATCH");
var http = new HttpRequestMessage(patch, "https://apiendpoint");
var content = new StringContent(json, Encoding.UTF8, "application/json");
http.Content = content;
var result = client.SendAsync(http).Result;
This is my first time using SendAsync, so perhaps I am missing something else that needs to be set on the HttpRequestMessage?
I am struggling with Rest call. Here is my code and it is working for basic authentication.
public async Task RunAsync(string name, string value)
{
using (var handler = new HttpClientHandler { UseDefaultCredentials = true })
using (var client = new HttpClient(handler))
{
var byteArray = Encoding.ASCII.GetBytes("username:password");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var urlRestGet = HomeController.url;
client.BaseAddress = new Uri(urlRestGet + "?name=" + name + "&value=" + value + "");
client.DefaultRequestHeaders.Accept.Clear();
**1. if(HomeController.contentType.ToLower()=="xml"){
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
}**
else if (HomeController.contentType.ToLower() == "json")
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
HttpResponseMessage response = await client.GetAsync(urlRestGet + "?name=" + name + "&value=" + value + "");
if (response.IsSuccessStatusCode)
{
//Get the response
loginJsonString = await response.Content.ReadAsStringAsync();
//Converting to xml
using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(loginJsonString)))
{
var output = new XmlDictionaryReaderQuotas();
xmlResult = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(stream, output)).ToString();
}
}
}
}
1) If the content type is application/xml am I correct to use line 1 part in the code.
2) How can I make this code more generic. (when the authentication type is different eg: tokenized or cookiebased how can I change this.)
There are a couple of things about your code I do not understand.
What is HomeController.contentType all about? The name "HomeController" suggests you're writing an ASP.NET MVC Controller (serverside). Though you seem to be writing something intended to be used as a HTTP client. But I could be mistaken or mislead here.
You are reading a Json response, then loading it as a Xml document?
I'll try to answer anyway.
1) If the content type is application/xml am I correct to use line 1 part in the code.
The Accept header sent by the client (you) tells the server that you accept the given content type. If you send Accept application/xml you tell the server you prefer if the response is Xml.
Your code seem to assume the response's content type is always Json.
You could include both application/xml and application/json as Accept headers in your request. The server should honor that request and pick the first supported content type for it's response.
When processing the response you should check the actual content type and handle the response content appropriately.
Remember that Accept only tells the server that you prefer those content types. The server may decide not to honor your whishes and can return any content type it desires.
2) How can I make this code more generic. (when the authentication type is different eg: tokenized or cookiebased how can I change this.)
If you mean tokenized as in a query parameter you should probably handle your query parameters as a collection rather than a hardcoded formatted string.
Check out the NameValueCollection class and this SO question on NameValueCollection to query string.
To handle cookies, you basically need to copy/re-use the cookie collection returned in a response in the next request.
See this SO question on how to inject cookies when you create a new HttpClient.
... but it's much easier to use a library
As you already discovered, making a robust REST/HTTP client is not a easy task.
And as #Mafii and #Fildor already pointed out in comments there are already numerous libraries available. RestSharp (https://restsharp.org) being one very popular.