No mediatype formatter found - c#

I'm trying to call this method
[HttpGet]
[Route("api/Trinity/GetDirectoryAndTask/{modelTemplateId}/{taskName}")]
public KeyValuePair<string, string> GetDirectoryAndTask(int modelTemplateId, string taskName)
with the url http://localhost:46789/api/Trinity/GetDirectoryAndTask/9/AG33%2f34 but am getting a "MediaTypeFormatter is available to read an object of type 'KeyValuePair`2' from content with media type 'text/html'" exception.

because of using / or %2f in route values, I suspect that the main problem at server side should be:
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
And to solve it you can change routing to this:
api/Trinity/GetDirectoryAndTask/{modelTemplateId}/{*taskName}
To test if the server side is OK, paste the url in browser and get the result.
But for your client the error is related to the way you read data from that api. I use this code and it reads data after I changed the route:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(" http://localhost:46789/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/Trinity/GetDirectoryAndTask/9/AG33%2f34");
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<KeyValuePair<string, string>>();
//The result is a valid key/value pair
}
}

Related

GET Request return 400 bad response

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.

Http Method Gets Rewritten - Post => Get

This is driving me cray cray, I have the following code:
public WorkItemQueryResult GetListOfStories(string queryString, string tfsInstance, string PAT)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", $"{GetPatAsEncodedString(PAT)}");
var request = new HttpRequestMessage()
{
Content = new StringContent(JsonSerializer.Serialize(queryString)),
Method = HttpMethod.Post,
RequestUri = new Uri($"{tfsInstance}_apis/wit/wiql?api-version=5.1")
};
var task = Task.Run(() => client.SendAsync(request));
task.Wait();
if (!task.Result.IsSuccessStatusCode) return new WorkItemQueryResult();
var response = task.Result.Content.ReadAsAsync<WorkItemQueryResult>();
return response.Result;
}
Which when I debug it with a break point on Task.Run shows the following:
My issue is that after executing the method I get an error from Azure DevOps telling me that the method isn't allowed. Here's the request after task.Wait:
I've verified that the same body and auth headers work through another client (YaRC). Obviously I'm doing something wrong but I can't see it :(
I had the same problem, try using 'ConfigureAwait' and 'GetAwaiter' like bellow:
var task = Task.Run(() => client.SendAsync(request));
//task.Wait();
task.ConfigureAwait(false).GetAwaiter();
Know its been a bit but, the issue ended up being that when a 3xx redirect is returned, the Authorization header is cleared out for security. So the POST is redirecting, causing a GET to be sent without authorization, resulting in 405.
The original request URL was http and as a result were getting redirected to https. Which then caused the above to occur :)

The format of value 'XXX.yyyyy' is invalid

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.

Rest calls in c#

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.

import.io: how to query from c#

I am using import.io to perform some simple query. I need to automate the calls because of the quantity of queries, therefore I am writing a c# client to run all of them.
I can use the API query "magic string" as template to build my queries, if I manually paste them in a browser I see the JSon of the response.
Calling the urls by code instead I always get the 401 unauthorized error.
I tried to set the url to a web browser control, but it reports that site cannot be accessed.
callingApi is a string with the full url, composed as such (instead of § I have the url to be scraped):
https://api.import.io/store/connector/(connector code)/_query?input=webpage/url:§_apikeyadmin=(my key)
(same problem with _apikey instead of _apikeyadmin)
I tried
WebRequest r = WebRequest.Create(callingApi);
r.Method = "GET";
using (var sr = r.GetResponse().GetResponseStream())
{
using (var reader = new StreamReader(sr))
{
var content = reader.ReadToEnd();
}
}
and also
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(callingApi);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync(callingApi).Result;
if (response.IsSuccessStatusCode)
... here I have error 401
Any advice please?

Categories