I'm building a Windows Store app, but I'm stuck at getting a UTF-8 response from an API.
This is the code:
using (HttpClient client = new HttpClient())
{
Uri url = new Uri(BaseUrl + "/me/lists");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Accept", "application/json");
HttpResponseMessage response = await client.SendRequestAsync(request);
response.EnsureSuccessStatusCode();
string responseString = await response.Content.ReadAsStringAsync();
response.Dispose();
}
The reponseString always contains strange characters which should be accents like é, and I tried using a stream, but the API I found in some examples don't exist in Windows RT.
Edit: improved code, still same problem.
Instead of using response.Content.ReadAsStringAsync() directly you could use response.Content.ReadAsBufferAsync() pointed by #Kiewic as follows:
var buffer = await response.Content.ReadAsBufferAsync();
var byteArray = buffer.ToArray();
var responseString = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
This is working in my case and I guess that using UTF8 should solve most of the issues. Now go figure why there is no way to do this using ReadAsStringAsync :)
Solved it like this:
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
var byteArray = await response.Content.ReadAsByteArrayAsync();
var result = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
return result;
}
}
I like El Marchewko's approach of using an extension, but the code did not work for me. This did:
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace WannaSport.Data.Integration
{
public static class HttpContentExtension
{
public static async Task<string> ReadAsStringUTF8Async(this HttpContent content)
{
return await content.ReadAsStringAsync(Encoding.UTF8);
}
public static async Task<string> ReadAsStringAsync(this HttpContent content, Encoding encoding)
{
using (var reader = new StreamReader((await content.ReadAsStreamAsync()), encoding))
{
return reader.ReadToEnd();
}
}
}
}
Perhaps the problem is that the response is zipped. If the content type is gzip, you will need decompress the response in to a string. Some servers do this to save bandwidth which is normally fine. In .NET Core and probably .NET Framework, this will automatically unzip the response. But this does not work in UWP. This seems like a glaring bug in UWP to me.
string responseString = await response.Content.ReadAsStringAsync();
This thread gives a clear example of how to decompress the response:
Compression/Decompression string with C#
The HttpClient doesn't give you a lot of flexibility.
You can use a HttpWebRequest instead and get the raw bytes from the response using HttpWebResponse.GetResponseStream().
Can't comment yet, so I'll have to add my thoughts here.
You could try to use _client.GetStringAsync(url) as #cremor suggested, and set your authentication headers using the _client.DefaultRequestHeaders property.
Alternatively, you could also try to use the ReadAsByteArrayAsync method on the response.Content object and use System.Text.Encoding to decode that byte array to a UTF-8 string.
My approach using an Extension:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Web.Http;
namespace yourfancyNamespace
{
public static class IHttpContentExtension
{
public static async Task<string> ReadAsStringUTF8Async(this IHttpContent content)
{
return await content.ReadAsStringAsync(Encoding.UTF8);
}
public static async Task<string> ReadAsStringAsync(this IHttpContent content, Encoding encoding)
{
using (TextReader reader = new StreamReader((await content.ReadAsInputStreamAsync()).AsStreamForRead(), encoding))
{
return reader.ReadToEnd();
}
}
}
}
Related
I have an existing and functioning API, which now have to be able to get data from another external API. How do i do that best?
I have tried with using HTTPClient, but i can't seem to get it to work. The error i get:
"No MediaTypeFormatter is available to read an object of type 'IList`1' from content with media type 'text/html'." -> I get this error on line 37. Can you spot it and/or tell me how I can do this differently, taking into account that all i want is the data (From the external API) and not to display it using a view, as this is an API?
Code below. I have also created a Pastebin: https://pastebin.com/MuKjEVys
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net;
namespace API.Controllers
{
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class ExternalApiController : Controller
{
private string ExternalApiLink = "https://blablabla.com/api";
private string ExternalApiLinkGet = "/module/1/";
[HttpGet("getdata")]
public ActionResult<ExternalApi> GetDataFromExternal()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ExternalApiLink);
var requestApi = client.GetAsync(ExternalApiLinkGet);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "XXXX");
requestApi.Wait();
var resultFromApi = requestApi.Result;
if (resultFromApi.IsSuccessStatusCode)
{
var readResponse = resultFromApi.Content.ReadAsAsync<IList<ExternalApi>>();
readResponse.Wait();
var data = readResponse.Result;
return Json(data);
}else
{
return NotFound();
}
}
}
}
}
Your response content seems to be json, while the content-type is text/html. If that is the case, the first thing to do would be to call the party that is exposing the service and have them fix it. In the meantime you could just read the content of the response as a string, and deserialize that string:
// Note that I made this method async.
public async Task<IActionResult> GetDataFromExternal()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ExternalApiLink);
// note that I moved this line above the GetAsync method.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "XXXX");
// note that I'm disposing the response
using (var response = await client.GetAsync(ExternalApiLinkGet))
{
if (response.IsSuccessStatusCode)
{
// Since the response content is json, but the content-type
// is text/html, simply read the content as a string.
string content = await response.ReadAsStringAsync();
// You can return the actual received content like below,
// or you may deserialize the content before returning to make
// sure it is correct, using JsonConvert.DeserializeObject<List<ExternalApi>>()
// var data = JsonConvert.DeserializeObject<List<ExternalApi>>(content);
// return Json(data);
return Content(content, "application/json");
}
else
{
return NotFound();
}
}
}
}
Before I upgraded to the newest .NetCore I was able to run the HttpWebRequest, add the headers and content Type and pull the stream of the JSON file from Twitch. Since the upgrade this is not working. I receive a Web Exception each time I go to get the response Stream. Nothing has changed with twitch because it still works with the old Bot. The old code is below:
private const string Url = "https://api.twitch.tv/kraken/streams/channelname";
HttpWebRequest request;
try
{
request = (HttpWebRequest)WebRequest.Create(Url);
}
request.Method = "Get";
request.Timeout = 12000;
request.ContentType = "application/vnd.twitchtv.v5+json";
request.Headers.Add("Client-ID", "ID");
try
{
using (var s = request.GetResponse().GetResponseStream())
{
if (s != null)
using (var sr = new StreamReader(s))
{
}
}
}
I have done some research and found that I may need to start using either an HttpClient or HttpRequestMessage. I have tried going about this but when adding headers content type the program halts and exits. after the first line here: (when using HttpsRequestMessage)
request.Content.Headers.ContentType.MediaType = "application/vnd.twitchtv.v5+json";
request.Content.Headers.Add("Client-ID", "rbp1au0xk85ej6wac9b8s1a1amlsi5");
You are trying to add a ContentType header, but what you really want is to add an Accept header (your request is a GET and ContentType is used only on requests which contain a body, e.g. POST or PUT).
In .NET Core you need to use HttpClient, but remember that to correctly use it you need to leverage the use of async and await.
Here it is an example:
using System.Net.Http;
using System.Net.Http.Headers;
private const string Url = "https://api.twitch.tv/kraken/streams/channelname";
public static async Task<string> GetResponseFromTwitch()
{
using(var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.twitchtv.v5+json"));
client.DefaultRequestHeaders.Add("Client-ID", "MyId");
using(var response = await client.GetAsync(Url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(); // here we return the json response, you may parse it
}
}
}
I am trying to consume RESTful service with as simple as possible C# utility. Response is given as XML and I just want to put it into string and later "pretty print it" and save it on disk. For now I am trying just to get proper response. Service is third party and it works OK, tested it many times in browser and java client. In C# utility authorization went OK and as far as I can tell I see in debugger in response object that
StatusCode OK System.Net.HttpStatusCode
and
StatusDescription "OK" string
but looks like response itself is empty? String Xml which has to hold response as String is empty (not null but empty). What do I do wrong?
Here is complete code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.Net;
using System.Xml;
namespace ConsoleApplication3
{
class Program
{
public static void SetBasicAuthHeader(WebRequest request, String userName, String userPassword)
{
string authInfo = userName + ":" + userPassword;
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
request.Headers["Authorization"] = "Basic " + authInfo;
}
static void Main(string[] args)
{
HttpWebRequest request = null;
HttpWebResponse response = null;
String Xml;
// Create the web request
request = WebRequest.Create("https://some.web.services.com?id='1234'¶m1='1'¶m2='2'") as HttpWebRequest;
SetBasicAuthHeader(request, "userName", "userPassword");
// Get response
response = request.GetResponse() as HttpWebResponse;
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
Xml = reader.ReadToEnd();
// Console xml output
Console.WriteLine(Xml); //see if we get the xml response
}
}
}
I've seen some weird behavior with manually using Authorization header; try this:
using System.Net; // contains HttpRequestHeader enum
public static void SetBasicAuthHeader(WebRequest request, String userName, String userPassword)
{
string authInfo = userName + ":" + userPassword;
authInfo = Convert.ToBase64String(Encoding.ASCII.GetBytes(authInfo));
string authHeader = string.Format("Basic {0}", authInfo);
request.Headers[HttpRequestHeader.Authorization] = authHeader;
}
I would recommend using HttpClient what you are trying to do, your code should be something like this:
static void Main(...)
{
var token = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user}:{pwd}"));
using(var client = new HttpClient())
{
client.DefaultHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
// here you could also use await right after GetAsync but since a Console application I use this instead
var response = client.GetAsync(url).GetAwaiter().GetResult();
// again the await could help here
var xml = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Console.WriteLine(xml);
}
}
you could also have also gotten a Stream instead of a string by calling: response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); and from there use it as you want, if you want to read in chunks or buffering to optimize.
Hope this helps. Also take a look at this it could be useful as it contains several extensions to convert between popular formats used in REST, although I don't know for sure if it could help you here as well with xml.
I am new in xamarin and visual studio,I have followed this tuto from microsoft:
enter link description here
to create a cross platform application,but I get this error:
'HttpWebRequest' does not contain a definition for 'GetResponseAsync' and no extension method 'GetResponseAsync' accepting a first argument of type 'HttpWebRequest' was found (a using directive or an assembly reference is it missing * ?)
and this is my code in which I get this error:DataService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using Newtonsoft.Json;
namespace shared
{
//This code shows one way to process JSON data from a service
public class DataService
{
public static async Task<dynamic> getDataFromService(string queryString)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(queryString);
var response = await request.GetResponseAsync().ConfigureAwait(false);
var stream = response.GetResponseStream();
var streamReader = new StreamReader(stream);
string responseText = streamReader.ReadToEnd();
dynamic data = JsonConvert.DeserializeObject(responseText);
return data;
}
}
}
Please how can I solve it, I checked the HttpWebRequest documentation but I didn't get well the problem
thanks for help
Not sure about HttpWebRequest - but a newer and now recommended way to get data is the following:
public static async Task<dynamic> getDataFromService(string queryString)
{
using (var client = new HttpClient())
{
var responseText = await client.GetStringAsync(queryString);
dynamic data = JsonConvert.DeserializeObject(responseText);
return data;
}
}
Try that and let me know if it works.
I need to implement a webapi call into a legacy ASP.Net Web Forms application.
I know not all of the usings are need for this method but they are for other methods on the page i included on the off chance that one of them is causing the problem.
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
private string GetToken(string Username, string IpAddress)
{
string result = string.Empty;
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(SSOApiUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/yourcustomobjects").Result;
if (response.IsSuccessStatusCode)
{
***var data = await response.Content.ReadAsStringAsync();***
var token = JsonConvert.DeserializeObject<GetSSOTokenResponse>(data);
result = token.Token;
}
return result;
}
When I try to compile my application I get the following error at the emphisized line:
Error 19 The 'await' operator can only be used within an async method.
Consider marking this method with the 'async' modifier and changing
its return type to 'Task< string>'.
I am trying to implement a solution similar to the one found in this question but it is failing. I need to call the WebAPI Method, and return part of the result as a string... not a Task< String>
The error is straightforward. You must have an async method to use the await keyword. Your return value will automatically be wrapped in a Task as a result by the compiler. Note that the .Result can be changed to an await as well. Here's the Microsoft documentation on the async/await keywords
private async Task<string> GetToken(string Username, string IpAddress)
{
string result = string.Empty;
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(SSOApiUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/yourcustomobjects");
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
var token = JsonConvert.DeserializeObject<GetSSOTokenResponse>(data);
result = token.Token;
}
return result;
}