I've been struggling with this for a couple of days.
C#, universal windows
I'm trying to access a device in my house that is web enabled. In looking at the API I should be able to make the following call:
{ "id" : "req-1", "url" : "/stable/av/", "method" : "browse" }
In fact, if I go into fiddler and send that to the device manually I get a response.
I'm using the following code:
try
{
using (var httpClient = new HttpClient { BaseAddress = baseAddress })
{
var _req = new SendRequest();
_req.id = "rec-100";
_req.url = "/stable/av/";
_req.method = "browse";
var _json = JsonConvert.SerializeObject(_req);
using (var content = new StringContent(_json.ToString(),Encoding.UTF8, "application/json"))
{
using (var response = await httpClient.PostAsync(baseAddress, content).ConfigureAwait(false))
{
responseData = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
var _Response = JsonConvert.DeserializeObject<Response>(responseData);
var item = new ZoneInfo();
item.szIPAddress = szIPAddress;
item.szZoneName = _Response.result.item.title;
lstZones.Add(item);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
The app hangs on the httpClient.PostAsync(...
In fiddler when it hits the line for the httpClient.PostAsync(... I see the properly formed request getting sent: (this is copied and pasted from fiddler) {"id":"rec-100","url":"/stable/av/","method":"browse"}
But then I've got messages like this which scroll like crazy (10 / second) until I stop the application.
{"resultId":"492bfa78-b778-4696-ad7c-c0f6a7733c6f","customData":"{\"type\":\"SamplePoints\",\"begin\":{\"h\":0,\"l\":0},\"end\":{\"h\":5,\"l\":-1002889776},\"samples\":436}","dwSessionId":"c533eca5-9ea7-4348-81bd-1920f8ee03da","dwRequestId":"e428bd3c-9d41-479c-993f-325fbfbb4adb"}
This is not the data I get when I post manually...
I'm not as concerned at the moment with the "hang", I'm more concerned about getting the correct data back.
Related
I'm quite new to .Net Core Web API and have spent a few days looking for an answer but couldn't find exactly what I am looking for. What I want to know is how to retrieve the custom object that is pass from an API action back to the client via an ActionResult (BadRequest(), NotFound()...etc.)
So I created a new Web API project in VS2019 and updated the default Get method of the WeatherForecastController like this:
[HttpGet]
public ActionResult<IEnumerable<WeatherForecast>> Get()
{
return NotFound(new { Message = "Could not find data", Suggestion = "Refine your search" });
}
When testing in Postman, I can get the expected output of Status = 404 and body is
{
"message": "Could not find data",
"suggestion": "Refine your search"
}
But in the client project, I just don't know how I can retrieve that custom error object.
My client code is like this:
public async Task OnGet()
{
try
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://localhost:44377/");
WeatherForcasts = await httpClient.GetFromJsonAsync<WeatherForcast[]>("weatherforecast");
}
catch (HttpRequestException ex)
{
hasError = true;
}
}
I understand that if the API action does not return a success status code (such as 200) then this will raise an HttpRequestException. But I can't find a away to get that custom error object out from the HttpRequestException.
Any help will be very much appreciated!
Change your code to this:
public async Task OnGet()
{
using var client = new HttpClient();
var baseAddress ="https://localhost:44377");
client.BaseAddress = new Uri(baseAddress);
var response= await client.GetAsync(baseAddress);
var statusCode = response.StatusCode.ToString(); // shoud be "NotFound"
var stringData = await response.Content.ReadAsStringAsync();
var data= JsonConvert.DeserializeObject<object>(stringData);// should be
// "{{"message":"Could not find data","suggestion": "Refine your search"}}"
....
}
I am trying to pass login creds from a WebView into an HttpWebRequest but not having any luck getting an authenticated response. I am able to successfully make the request, but the response is acting like I haven't logged in. My app has 5 WebViews contained within Fragment s and I'm logged in on all of them. I've tried using the CookieSyncManager but it's deprecated and .Sync() didn't work. I've tried a lot of different ways of passing the cookies into the HttpRequest with no success and many hours spent.
One would think this is a simple request; user has logged in within the app; they should be authenticated for all requests. Here's the closest that I've gotten, but the response string is still not the same as through my authenticated WebView :
This attempt parses each Cookie into a string and adds it
public string _cookieString { get; set; }
private class ExtWebViewClient : WebViewClient
{
TheFragment5 _fm5 = new TheFragment5();
public override void OnPageFinished(WebView view, string url)
{
var cookieHeader = Android.Webkit.CookieManager.Instance.GetCookie(url);
var cookiePairs = cookieHeader.Split('&');
_fm5._cookieString = "";
foreach (var cookiePair in cookiePairs)
{
var cookiePieces = cookiePair.Split('=');
if (cookiePieces[0].Contains(":"))
cookiePieces[0] = cookiePieces[0].Substring(0, cookiePieces[0].IndexOf(":"));
cookies.Add(new Cookie
{
Name = cookiePieces[0],
Value = cookiePieces[1]
});
}
foreach (Cookie c in cookies)
{
if (_fm5._cookieString == "")
{
_fm5._cookieString = c.ToString();
}
else
{
_fm5._cookieString += c.ToString();
}
}
}
}
I've also tried just doing:
_fm5._cookieString = cookieHeader.ToString();
but neither of those attempts is working when I add the cookie string into my HttpRequest :
public async void GetNotificationText(string url)
{
//var _cmhc = _cookieMan.HasCookies;
await Task.Run(() =>
{
_notificationHttpRequestInProgress = true;
try
{
var _ctxxx = Android.App.Application.Context;
//URL _url2 = new URL("https://bitchute.com/notifications/");
//HttpURLConnection conn = (HttpURLConnection)_url2.OpenConnection();
//conn.ReadTimeout = 10000 /* milliseconds */;
//conn.ConnectTimeout = 15000 /* milliseconds */;
////conn.SetRequestProperty("Cookie", cookies);
//conn.Connect();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
Uri uri = new Uri(url);
var _req = request;
var _uriii = uri;
var _cookiesss = _fm5._cookieString;
_cookieCon.SetCookies(uri, _cookiesss);
request.CookieContainer = _cookieCon;
//request.CookieContainer.SetCookies(uri, _cookiesss);
request.AutomaticDecompression = DecompressionMethods.GZip;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
_notificationRawText = reader.ReadToEnd();
Console.WriteLine(_notificationRawText);
_rawNoteText = _notificationRawText;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
_notificationHttpRequestInProgress = false;
});
}
This returns, but not the authenticated webtext request; I get the same response any user would get on a browser having never logged in. If I were to browse out to this same url on any WebView in my app, I'd get a completely different response.
You will also notice some commented out code that was another failed attempt at adding the cookies into a connection. I had also tried using HttpURLConnection.SetRequestProperty("Cookie", cookies);
where cookies was a CookieCollection and that didn't work either. The code is mostly commented out and layered because I've been trying this for days.
Does anyone know how I can pass WebView cookies into an HttpRequest using Xamarin.Android?
I am putting this code below in Fragment5 of my app; you can see and compile the full context here:
https://github.com/hexag0d/BitChute_Mobile_Android_BottomNav/blob/NotificationAdder/Fragments/TheFragment5.cs
I'm not sure exactly why the above example didn't work; maybe if you're better at .NET than I am, you could figure it out. However, I was able to successfully pass WebView creds into an HttpClient by following these steps, which are returning an authenticated response. This may not be the most elegant way of doing it, but you can always refine my answer, or post a better one.
What I had to do was set the HttpClient.DefaultRequestHeaders using the .Add() method like this: _client.DefaultRequestHeaders.Add("Cookie", TheFragment5._cookieHeader);
I got the CookieHeader (which is just a string btw) like this:
//instantiate a string that will house our cookie header
public static string _cookieHeader;
//you might want to make it private to prevent abuse
//but this example is just for demonstration
//the thing is we need a string to house our headers in scope of both the WebView and the HttpClient
//extend the WebViewClient
private class ExtWebViewClient : WebViewClient
{
public override void OnPageFinished(WebView view, string url)
{
//I get the cookies when the page finishes loading because
//then we know the cookie has our login cred header
//also, most of the previous examples got the cookies OnPageFinished
TheFragment5._cookieHeader = Android.Webkit.CookieManager.Instance.GetCookie(url);
}
}
Then we need another method for the HttpClient and HttpClientHandler ... mine scans a webpage for notification text.
public async void GetNotificationText(string url)
{
await Task.Run(() =>
{
/* this line is pretty important,
we need to instantiate an HttpClientHandler
then set it's UseCookies property to false
so that it doesn't override our cookies
*/
HttpClientHandler handler = new HttpClientHandler() { UseCookies = false };
try
{
Uri _notificationURI = new Uri("https://bitchute.com/notifications/");
//instantiate HttpClient using the handler
using (HttpClient _client = new HttpClient(handler))
{
//this line is where the magic happens;
//we set the DefaultRequestHeaders with the cookieheader we got from WebViewClient.OnPageFinished
_client.DefaultRequestHeaders.Add("Cookie", TheFragment5._cookieHeader);
//do a GetAsync request with our cookied up client
var getRequest = _client.GetAsync("https://bitchute.com/notifications/").Result;
//resultContent is the authenticated html string response from the server, ready to be parsed =]
var resultContent = getRequest.Content.ReadAsStringAsync().Result;
/*
I was writing to console to check the
response.. for me, I am now getting
the authenticated notification html
page
*/
Console.WriteLine(resultContent);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Hope this helps you, posting for future reference, especially for people using Xamarin.Android.
I have an issue in an app developed for Windows Phone 8.1, where its working just fine, but in Windows Mobile 10 it gets stuck on GetAsync. It doesn't throw an exception or anything, just gets stuck there waiting endlessly. The content-length of the response is 22014030 bytes.
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
response.EnsureSuccessStatusCode();
using (HttpContent content = response.Content)
{
return await content.ReadAsStringAsync();
}
}
}
I have also tried reading it as a stream, but as soon as i try to read the content body nothing happens anymore.
The code that calls this function is declared as:
public async Task<List<APIJsonObject>> DownloadJsonObjectAsync()
{
string jsonObjectString = await DownloadStringAsync(Constants.URL);
if (jsonObjectString != null && jsonObjectString.Length >= 50)
{
List<APIJsonObject> result = await Task.Run<List<APIJsonObject>>(() => JsonConvert.DeserializeObject<List<APIJsonObject>>(jsonObjectString));
return result;
}
return null;
}
The reason it is blocking is because you are getting a huge response (20.9MB) which the HttpClient will try to download before giving you a result.
However, you can tell the HttpClient to return a result as soon as the response headers are read from the server, which means you get a HttpResponseMessage faster. To do this, you will need to pass HttpCompletionOption.ResponseHeadersRead as a parameter to the SendRequestAsync method of the HttpClient
Here is how:
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("GET"), url))
{
using (var response = await client.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead)
{
//Read the response here using a stream for example if you are downloading a file
//get the stream to download the file using: response.Content.ReadAsInputStreamAsync()
}
}
}
I have created a web API in visual studio 2015 using a MySQL database. The API is working perfect.
So I decided to make a console client application in which I can consume my web-service (web API). The client code is based on HttpClient, and in the API I have used HttpResponse. Now when I run my console application code, I get nothing. Below is my code:
Class
class meters_info_dev
{
public int id { get; set; }
public string meter_msn { get; set; }
public string meter_kwh { get; set; }
}
This class is same as in my web API model class:
Model in web API
namespace WebServiceMySQL.Models
{
using System;
using System.Collections.Generic;
public partial class meters_info_dev
{
public int id { get; set; }
public string meter_msn { get; set; }
public string meter_kwh { get; set; }
}
Console application code
static HttpClient client = new HttpClient();
static void ShowAllProducts(meters_info_dev mi)
{
Console.WriteLine($"Meter Serial Number:{mi.meter_msn}\t Meter_kwh: {mi.meter_kwh}", "\n");
}
static async Task<List<meters_info_dev>> GetAllRecordsAsync(string path)
{
List<meters_info_dev> mID = new List<meters_info_dev>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
mID = await response.Content.ReadAsAsync<List<meters_info_dev>>();
}
else
{
Console.WriteLine("No Record Found");
}
return mID;
}
static void Main()
{
RunAsync().Wait();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("http://localhost:2813/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var m = await GetAllRecordsAsync("api/metersinfo/");
foreach(var b in m)
{
ShowAllProducts(b);
}
}
In my API I have 3 GET methods under a single controller, so I have created different routes for them. Also the URL for them is different.
http://localhost:2813/api/metersinfo/ will return all records
While debugging the code, I found that List<meters_info_dev> mID = new List<meters_info_dev>(); is empty:
While the response is 302 Found, the URL is also correct:
Update 1
After a suggestion I have done the following:
using (var client = new HttpClient())
{
List<meters_info_dev> mID = new List<meters_info_dev>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
mID = await response.Content.ReadAsAsync<List<meters_info_dev>>();
}
else
{
Console.WriteLine("No Record Found");
}
return mID;
}
When I run the application, I get the exception "An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set."
Update 2
I have added a new piece of code:
using (var cl = new HttpClient())
{
var res = await cl.GetAsync("http://localhost:2813/api/metersinfo");
var resp = await res.Content.ReadAsStringAsync();
}
And in the response I am getting all the records:
I don't know why it's not working with the other logic and what the problem is. I have also read the questions Httpclient consume web api via console app C# and Consuming Api in Console Application.
Any help would be highly appreciated.
The code needs quite a bit of work.
The line you highlighted will always be empty because that's where you initialise the variable. What you want is run thorugh the code until you get the result back form the call.
First, make sure your api actually works, you can call the GET method you want in the browser and you see results.
using (var client = new HttpClient())
{
var result = await client.GetAsync("bla");
return await result.Content.ReadAsStringAsync();
}
that's an example of course, so replace that with your particular data and methods.
now, when you check the results just because your response.IsSuccessStatusCode is false that doesn't mean there are no records. What it means is that the call failed completely. Success result with an empty list is not the same thing as complete failure.
If you want to see what you get back you can alter your code a little bit:
if(response.IsSuccessStatusCode)
{
var responseData = await response.Content.ReadAsStringAsync();
//more stuff
}
put a breakpoint on this line and see what you actually get back, then you worry about casting the result to your list of objects. Just make sure you get back the same thing you get when you test the call in the browser.
<------------------------------->
More details after edit.
Why don't you simplify your code a little bit.
for example just set the URL of the request in one go :
using (var client = new HttpClient())
{
var result = await client.GetAsync("http://localhost:2813/api/metersinfo");
var response = await result.Content.ReadAsStringAsync();
//set debug point here and check to see if you get the correct data in the response object
}
Your first order of the day is to see if you can hit the url and get the data.
You can worry about the base address once you get a correct response. Start simple and work your way up from there, once you have a working sample.
<----------------- new edit ---------------->
Ok, now that you are getting a response back, you can serialise the string back to the list of objects using something like Newtonsoft.Json. This is a NuGet package, you might either have it already installed, if not just add it.
Add a using statement at the top of the file.
using Newtonsoft.Json;
then your code becomes something like :
using (var client = new HttpClient())
{
var result = await client.GetAsync("bla");
var response = await result.Content.ReadAsStringAsync();
var mID = JsonConvert.DeserializeObject<List<meters_info_dev>>(response);
}
At this point you should have your list of objects and you can do whatever else you need.
I have recently started working with web api's.
I need to download a file in C# project from a web api, which works fine when I hit the web api using postman's send and download option. Refer to the image, also please check the response in header's tab. This way, I am able to directly download the file to my computer.
I want to do the same from my C# project, I found following two links which shows how to download a file from web api.
https://code.msdn.microsoft.com/HttpClient-Downloading-to-4cc138fd
http://blogs.msdn.com/b/henrikn/archive/2012/02/16/downloading-a-google-map-to-local-file.aspx
I am using the following code in C# project to get the response:
private static async Task FileDownloadAsync()
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Accept", "text/html");
try
{
// _address is exactly same which I use from postman
HttpResponseMessage response = await client.GetAsync(_address);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
catch (Exception)
{
throw;
}
}
}
However I am not getting the response at all (before I can start to convert the response to a file), please check the error message coming:
What am I missing here, any help would be appreciated.
As the (500s) error says - it's the Server that rejects the request. The only thing I see that could cause an issues is the charset encoding. Yours is the default UTF-8. You could try with other encodings.
Below method uses:
SSL certificate (comment out code for cert, if you don't use it)
Custom api header for additional layer of security (comment out Custom_Header_If_You_Need code, if you don't need that)
EnsureSuccessStatusCode will throw an error, when response is not 200. This error will be caught in and converted to a human readable string format to show on your screen (if you need to). Again, comment it out if you don't need that.
private byte[] DownloadMediaMethod(string mediaId)
{
var cert = new X509Certificate2("Keystore/p12_keystore.p12", "p12_keystore_password");
var handler = new WebRequestHandler { ClientCertificates = { cert } };
using (var client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Custom_Header_If_You_Need", "Value_For_Custom_Header");
var httpResponse = client.GetAsync(new Uri($"https://my_api.my_company.com/api/v1/my_media_controller/{mediaId}")).Result;
//Below one line is most relevant to this question, and will address your problem. Other code in this example if just to show the solution as a whole.
var result = httpResponse.Content.ReadAsByteArrayAsync().Result;
try
{
httpResponse.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
if (result == null || result.Length == 0) throw;
using (var ms = new MemoryStream(result))
{
var sr = new StreamReader(ms);
throw new Exception(sr.ReadToEnd(), ex);
}
}
return result;
}
}
Once you have your http response 200, you can use the received bytes[] as under to save them in a file:
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
fs.Write(content, 0, content.Length);
}
Your request header says that you accept HTML responses only. That could be a problem.