Body not sent when using Bearer Authentication - c#

I have a winform client that consumes data from a Python Flask API using Zalando's Connexion and OpenAPI 3.
The client uses Net Framework 4.8. When I send a POST request with Authorization header, the body doesn't get sent, so I get an error 400 from the server. I've inspected received data on API side, also I created a blank project with Flask only that just outputs what it receives as requests, and the body is not there. Inspecting the content on Visual Studio shows the body, but it never reaches the API server.
If I don't put the Authorization header it works ok. It also works ok for GET, WITH the header.
This is how I set the token on client side:
public void SetToken(string token) {
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
This is my default constructor for the client:
public class RestClient{
private readonly HttpClient Client;
public RestClient {
Client = new HttpClient();
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
I've searched a lot before asking here for someone with my same problem but couldn't find any post.
I also see that almost all examples use form/urlencoded for POST instead of application/json but I guess that's simple a choice for the format, doesn't seem as a requirement when using authentication.
I'm using:
Visual Studio 2019
Net Framework 4.8 (tried with 4.7 too)
Python 3.7.2
Flask 1.1.1
Connexion 2.3.0
Also tried from an API test suite with Bearer Authorization created on Python using requests library and it works ok from there...
Edit:
As requested adding my post code:
public HttpResponseMessage Post(string path, HttpContent content, int maxRetries = 0)
{
if (maxRetries < 0)
{
throw new ArgumentException("maxRetries cannot be less than 0");
}
int attemptsMade = 0;
int maxAttempts = maxRetries + 1;
bool workCompletedSuccessfully = false;
bool attemptsRemain = true;
HttpResponseMessage response = null;
while (!workCompletedSuccessfully && attemptsRemain)
{
attemptsMade++;
attemptsRemain = attemptsMade < maxAttempts;
try
{
response = Client.PostAsync(path, content).GetAwaiter().GetResult();
if (response.IsSuccessStatusCode)
{
workCompletedSuccessfully = true;
}
}
catch (Exception e)
{
if (!attemptsRemain)
{
throw e;
}
}
}
return response;
}
And this is how I call it from the service:
private const string PATH = "person";
public PersonService(RestClient rest)
{
_rest = rest;
}
public HttpResponseMessage AddNew(Person person)
{
var personJson = JsonConvert.SerializeObject(person);
using (var content = new StringContent(personJson, Encoding.UTF8, "application/json"))
{
var result = _rest.Post($"api/{PATH}", content);
return result;
}
}

Does your bearer token (the string you pass to SetToken method) contain newlines? This can cause that problem.

Related

Xamarin.Forms HttpClient.PostAsync never returns on Android

I am working on a Xamarin.Forms multi-platform app, which shall utilize a custom Asp.Net Core Web Api. I've written the code to make the calls to the API, but it fails on Android.
I am suspecting some kind of SSL issue... but we'll get to that in a second.
I am using HttpClient and HttpClient.PostAsync for the calls and on Android - and only on Android - it comes to an deadlock on the PostAsync.
I therefore created a minimum example and started from the beginning:
HttpClient client = new HttpClient();
return client.GetAsync("https://www.google.com").Result.Content.ReadAsStringAsync().Result;
Is working fine, then I tried it with my domain, which contains a simple Asp.Net page, which also worked:
HttpClient client = new HttpClient();
return client.GetAsync("https://aircu.de").Result.Content.ReadAsStringAsync().Result;
Next step was to try and address my API:
HttpClient client = new HttpClient();
var response = client.GetAsync("https://api.aircu.de").Result;
return response.Content.ReadAsStringAsync().Result;
This now fails on Android, the line var response = client... never returns. However it is working on iOS and (in a testing .net core console application) on windows, too. If i add a timeout to the client, the application of course will run into that timeout and throw an exception.
I've tried using async Task<string> and var response = await client.GetAsync("https://api.aircu.de").ConfigureAwait(false);, didn't work either...
I cannot find the problem; I've tried adding a custom handler for the server certificates, which did not help. I added Accept for the client and MediaType for a string content I added; didn't work either.
I've changed the Android HttpClientImplementation to Standard, Legacy and Android; didn't change a thing.
What do I need to do, to make this request working on Android?
EDIT://
To make it crystal clear: There is no deadlock issue! When I use any other url, like my base-url https://aircu.de it's working fine. The function only does not return, when using the subdomain https://api.aircu.de
I also moved my demo code to a VM...
private HttpClient client = null;
public MainPageViewModel()
{
client = DependencyService.Get<IHttpsClientFactory>().CreateClient();
client.Timeout = TimeSpan.FromSeconds(5);
}
[RelayCommand]
public void Click()
{
Task.Factory.StartNew(requestTaskFn);
}
private async void requestTaskFn()
{
var result = await makeRequest();
Device.BeginInvokeOnMainThread(async () => await Application.Current.MainPage.DisplayAlert("Result", result, "OK"));
}
private async Task<string> makeRequest()
{
string responseJson = "";
try
{
var response = await client.GetAsync("https://api.aircu.de").ConfigureAwait(false);
responseJson = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
throw;
}
return responseJson;
}
I also added an AndroidClientHandler in the IHttpsClientFactory:
var handler = new AndroidClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
return new HttpClient(handler);

How can I execute an url call from C# code on a ASP website on a ASCX page?

I need to call a Custom protocol (something like: "custom:signDocs?param1=value?param2=value") that is registered on a client.
I have a working one that is executed via JavaScript on a button click.
But I need to call the url to execute the program I have on the clients pc.
The program is for signing documents and sending them back to the server, and, in the code I have a 15min timer that waits for the status of the documents to change to signed then it shows the documents to the user.
I also tried using webrequest:
//Method that uses the webrequest
{
System.Net.WebRequest.RegisterPrefix("customProtocolName", new PrototipoIDPTRequestCreator());
System.Net.WebRequest req = System.Net.WebRequest.Create(protocolUrlWithParams);
var aux = req.GetResponse();
}
internal class CustomRequestCreator : System.Net.IWebRequestCreate
{
public WebRequest Create(Uri uri)
{
return new CustomWebRequest(uri);
}
}
class CustomWebRequest: WebRequest
{
public override Uri RequestUri { get; }
public CustomWebRequest(Uri uri)
{
RequestUri = uri;
}
}
But this does nothing, I do not know it its even the right path...
Does anyone know of a way to accomplish this?
You can use HttpClient from System.Net.Http like the following example.
Simple get call from a test api endpoint.
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("YOUR_BASE_URL"); //https://localhost:8000
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/test"); //api uri
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
}
Note: For more details refer, HttpClient Doc

Google Drive API bad request from C# but fine from Postman

I'm using this request code from C# and getting a Bad Request result:
public class GoogleDriveManager
{
private static readonly HttpClient Client = new HttpClient();
private const string Access = "ACCESS";
public GoogleDriveManager()
{
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Access);
}
public async Task<HttpResponseMessage> GetFilesAsync(string driveId)
{
var url =
$"https://www.googleapis.com/drive/v3/files?driveId={driveId}&includeItemsFromAllDrives=true&corpora=true&supportsAllDrives=true";
var result = await Client.GetAsync(url);
return result;
}
}
But when I make the same sort of request from Postman, it works great:
GET https://www.googleapis.com/drive/v3/files?driveId=DRIVEID&includeItemsFromAllDrives=true&corpora=drive&supportsAllDrives=true
(Authorization Bearer Token, with token value the same as the one used above)
What am I doing wrong on the C# side?
According to API documentation supported values for corpora are:
user
domain
drive
allDrives
Your Postman example has corpora=drive but your C# example shows corpora=true.

ASP .NET Core 2.1-preview2 HttpClient deadlock

I have ASP.NET Core 2.1 application hosted on Azure web app. I am sending photos base64 string over WebSockets and then by HttpClient to Azure Face API.
After some 150-250 requests HttpClient stops responding and I can't use HttpClient class in any part of my application.
In my localhost it works properly and I never get this problem.
public class FaceApiHttpClient
{
private HttpClient _client;
public FaceApiHttpClient(HttpClient client)
{
_client = client;
}
public async Task<string> GetStringAsync(byte[] byteData,string uri)
{
using (ByteArrayContent content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
HttpResponseMessage response = await _client.PostAsync(uri, content).ConfigureAwait(false);
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
DI:
services.AddHttpClient<FaceApiHttpClient>(
client => {
client.BaseAddress = new Uri("xxx");
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "xxx");
});
The method from FaceApiClient is invoke in a Scoped Service:
public interface IFaceAPIService
{
Task<DataServiceResult<List<Face>>> GetFacesDataFromImage(byte[] byteArray);
}
public class FaceAPIService: ServiceBase, IFaceAPIService
{
private readonly IServerLogger _serverLogger;
private FaceApiHttpClient _httpClient;
//Consts
public const string _APIKey = "xxx";
public const string _BaseURL = "xxx";
public FaceAPIService(IServerLogger serverLogger, FaceApiHttpClient client)
{
_serverLogger = serverLogger;
_httpClient = client;
}
public async Task<DataServiceResult<List<Face>>> GetFacesDataFromImage(byte[] byteData)
{
try
{
// Request parameters. A third optional parameter is "details".
string requestParameters = "returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise";
// Assemble the URI for the REST API Call.
string uri = _BaseURL + "/detect" + "?" + requestParameters;
var result = await _httpClient.GetStringAsync(byteData, uri).ConfigureAwait(false);
List<Face> faces = JsonConvert.DeserializeObject<List<Face>>(result);
return Success(faces);
}
catch (Exception ex)
{
_serverLogger.LogExceptionFromService(ex);
return DataServiceResult.ErrorResult<List<Face>>(ex.Message);
}
}
}
a) on localhost enviroment it works. I run 11 simulators with many request per seconds and it never broke (10 hours of simulators, over 20k requests).
b) HttpClient stops working in any part of application not only in one class.
How to fix this?
Consider changing up the design a bit.
Using a typed client the assumption is that its configuration is something that will not change frequently and that it should be added once when registering the typed client.
services.AddHttpClient<FaceApiHttpClient>(_ => {
_.BaseAddress = new Uri(Configuration["OcpApimBaseURL"]);
var apiKey = Configuration["OcpApimSubscriptionKey"];
_.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
_.Timeout = new TimeSpan(0, 0, 10);
});
Which would allow the typed client to no have to add the key for every call
public class FaceApiHttpClient {
private readonly HttpClient client;
public FaceApiHttpClient(HttpClient client) {
this.client = client;
}
public async Task<string> GetStringAsync(byte[] byteData, string uri) {
using (var content = new ByteArrayContent(byteData)) {
// This example uses content type "application/octet-stream".
// The other content types you can use are "application/json" and "multipart/form-data".
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// Execute the REST API call.
HttpResponseMessage response; response = await _client.PostAsync(uri, content).ConfigureAwait(false);
// Get the JSON response.
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
It should be noted from ASP.NET Core 2.1-preview1: Introducing HTTPClient factory
A typed client is, effectively, a transient service, meaning that a new instance is created each time one is needed and it will receive a new HttpClient instance each time it is constructed. This means that the configuration func, in this case retrieving the URI from configuration, will run every time something needs a FaceApiHttpClient.
Based on previous documentations, having that many clients created can pose problems, but the assumption here is that the developers of this new feature took that into consideration when designing it.
I say this because the issues you described are similar to previous problem with the same cause.
After release version of ASP .NET CORE 2.1 RC1 the problem is fixed. I updated project to new version and now there is no problem with deadlock.
The problem with deadlock was only in ASP .NET CORE 2.1 Preview 2 version.

Can't get Microsoft.ProjectOxford.Vision or REST method working in PCL

This should be pretty straightforward, but I can't get it to work. I'm using these instructions. I've tried using the Nuget package in my PCL targeting 259 as as well as in another PCL targeting 7. Both PCLs never return after these lines. I've tried both.
//text = await client.RecognizeTextAsync(imageURL);
text = await client.RecognizeTextAsync(imageURL, languageCode: "en", detectOrientation: true);
Here is the REST code I've also tried. This works with my HTTP Helper.
public static async Task<OcrResults> PostReceiptAsync(string imageURL)
{
try
{
var apiKey = "KEY 1";
var content = await HttpHelper.Request(null, String.Format("https://eastus2.api.cognitive.microsoft.com/vision/v1.0?language=en&detectOrientation=true&subscription-key={0}&url={1}", apiKey, imageURL), null, HttpRequestType.POST);
return Mapper<OcrResults>.MapFromJson(await content.ReadAsStringAsync());
}
catch (Exception e)
{
throw;
}
}
In both methods, I'm trying to use a Blob image URL. Here is my calling method.
takePhoto.Clicked += async (sender, args) =>
{
}
It's almost seems like a deadlock issue.
Any help is much appreciated. Thanks!
UPDATE: I finally got the REST method to work with Android and UWP using this bit of code, but it fails in my iOS app. The main problem I had is that the URL that you copy out of Azure does not include the ocr? parameter. The Json Mapper is something that my SDK uses. OcrResults comes from the Project Oxford Nuget package.
public static async Task<OcrResults> ProcessReceiptAsync(string imageUrl)
{
// Instantiate a HTTP Client
var client = new HttpClient();
var apiKey = "KEY 1";
// Request parameters and URI
string requestParameters = "language=en&detectOrientation =true";
string uri = "https://eastus2.api.cognitive.microsoft.com/vision/v1.0/ocr?" + requestParameters;
// Pass subscription key thru the HTTP Request Header
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
// Format Request body
byte[] byteData = Encoding.UTF8.GetBytes($"{{\"url\": \"{imageUrl}\"}}");
using (var content = new ByteArrayContent(byteData))
{
// Specify Request body Content-Type
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
// Send Post Request
HttpResponseMessage response = await client.PostAsync(uri, content);
// Read Response body into the model
//return await response.Content.ReadAsStringAsync(OcrResults);
var result = Mapper<OcrResults>.MapFromJson(await response.Content.ReadAsStringAsync());
return result;
}
}
UPDATE 2: This method also works on Android and UWP, but fails with a BadRequest in my iOS app. I wanted to change up how the image URL gets encoded.
public static async Task<OcrResults> ProcessReceiptAsync2(string imageUrl)
{
// Instantiate a HTTP Client
var client = new HttpClient();
var apiKey = "KEY 1";
// Request parameters and URI
string requestParameters = "language=en&detectOrientation =true";
string uri = "https://eastus2.api.cognitive.microsoft.com/vision/v1.0/ocr?" + requestParameters;
// Pass subscription key thru the HTTP Request Header
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
// Format Request body
var dict = new Dictionary<string, string>();
dict.Add("url", imageUrl);
//var param = JsonConvert.SerializeObject($"{{\"url\": \"{imageUrl}\"}}");
var param = JsonConvert.SerializeObject(dict);
HttpContent contentPost = new StringContent(param, Encoding.UTF8, "application/json");
// Specify Request body Content-Type
//contentPost.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Send Post Request
HttpResponseMessage response = await client.PostAsync(uri, contentPost);
// Read Response body into the model
//return await response.Content.ReadAsStringAsync(OcrResults);
var result = Mapper<OcrResults>.MapFromJson(await response.Content.ReadAsStringAsync());
return result;
}
UPDATE 3: I tried this code, but it failed with a Bad Request on iOS. I'm using the ExtractTextFromImageUrlAsync. I did get a "file too large" error using the ExtractTextFromImageStreamAsync, so that method appears to have worked.
https://github.com/HoussemDellai/Microsoft-Cognitive-Services-API/blob/master/ComputerVisionApplication/ComputerVisionApplication/Services/ComputerVisionService.cs
UPDATE 4: I removed Microsoft.Net.Http 2.9 from all the Projects and commented out System.Net.Http from all the app.config files. Both methods now work in the Android and UWP app. I still can't get the iOS app to work. The Project Oxford Nuget fails at this line requestObject.url = imageUrl; (see GitHub). The Rest method fails with a Bad Request, but I managed to catch a "InvalidImageUrl" message, so something is happening when the imageUrl is encoded. At this stage, I believe this is a Xamarin issue that's specific to iOS 10.4.0.

Categories

Resources