HttpClient PostAsync returns 500 when getting token - c#

I am trying to figure out what I can do (logging, things to check) before having to read server logs as I don't want to miss something stupid before requesting that.
Here is my code:
const string URL = "https://SomeURL/api/security/";
string urlParameters = string.Format("grant_type=password&username={0}&password={1}", username, password);
StringContent content = new StringContent(urlParameters, Encoding.UTF8, "application/x-www-form-urlencoded");
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
StringContent content = new StringContent(urlParameters, Encoding.UTF8, "application/x-www-form-urlencoded");
var tokenResponse = client.PostAsync("token", content).Result;
I am a little newer to this so I'm not sure what to check next but have tried the same request using postman and get a response with my token so it looks like I am missing something or maybe formatting something incorrectly?

I was following an online course, and the code for setting the URL parameters were set like this:
public async Task<AuthenticatedUser> Authenticate(string userName, string password)
{
var data = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username ", "userName"),
new KeyValuePair<string, string>("password", "password")
});
using (HttpResponseMessage response = await apiClient.PostAsync("/Token", data))
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<AuthenticatedUser>();
return result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
When testing, found that 500 error was being returned for the PostAsync call. I checked my URL address and parameters and they all looked correct. If I tested in Swagger, then I received a 200 status and token was shown.
Following the link by Thomas Levesque I changed how the data variables were set to :
var data = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["username"] = username,
["password"] = password
});
Now the response status is 200 and the AuthenticatedUser model is populated correctly. However I couldn't understand why Dictionary seem to work and KeyValuePair didn't. So I created the list and then encoded it:
var dataList = new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password)
};
var content = new FormUrlEncodedContent(dataList);
using (HttpResponseMessage response = await apiClient.PostAsync(requestUrl, content))
This also worked. I freely admit that I do not fully understand why.....yet.

I did not URL encode my parameters, here's the fix (probably a better way of doing it).
string urlParameters = string.Format("grant_type=password&username={0}&password={1}", Uri.EscapeDataString(username), Uri.EscapeDataString(password));

Related

How to fix "Bad request" error when sending Rest request to Google Oauth2 SSO in HttpClient

I´m trying to learn how to implement Googles Oauth2 SSO login function. So far I´ve gotten to the point where everything works except when I try to send a request to "https://accounts.google.com/o/oauth2/token" using REST in C# to get the Access_code/Token. I´ve registered my localhost and the corresponding port on google and I´ve managed to get my POST request to work in POSTMAN however when i try sending a request in C# my HttpRequestMessage returns 400 bad request.
First I have an Authorization.aspx page that runs a command that redirects the users to googles authorization page where the user login and then gets redirected to my page http://localhost:64716/GoogleCallBack.aspx which picks up the response. In the page it picks up the authorization code and tries to send a POST request to the base URL. I´ve tried running it in POSTMAN where it works (although only with a new authorization code). So I know it is my C# code that is the problem. I´ve tried using webrequest and I´ve also tried changing the Media type to application/Json and application/x-www-form-urlencoded
protected void Page_Load(object sender, EventArgs e)
{
string Error = Request.QueryString["error"];
string Code = Request.QueryString["code"];
if (Error != null) { }
else if (Code != null)
{
string UserId = Request.QueryString["state"];
int Id = Convert.ToInt32(UserId);
string AccessToken = string.Empty;
string RefreshToken = ExchangeAuthorizationCode(Id, Code, out AccessToken);
string Url = "Authorize.aspx?UserId=" + UserId;
Response.Redirect(Url, true);
}
}
private string ExchangeAuthorizationCode(int userId, string code, out string accessToken)
{
string baseurl = "https://accounts.google.com/o/oauth2/token";
accessToken = string.Empty;
string ClientSecret = ConfigurationManager.AppSettings["ClientSecrete"];
string ClientId = ConfigurationManager.AppSettings["ClientId"];
string RedirectUrl = "http://localhost:64716/GoogleCallBack.aspx";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseurl);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/Json"));
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("Code", code));
nvc.Add(new KeyValuePair<string, string>("Client_id", ClientId));
nvc.Add(new KeyValuePair<string, string>("Client_secret", ClientSecret));
nvc.Add(new KeyValuePair<string, string>("Redirect_uri", RedirectUrl));
nvc.Add(new KeyValuePair<string, string>("Grant_type", "authorization_code"));
nvc.Add(new KeyValuePair<string, string>("access_type", "offline"));
var req = new HttpRequestMessage(HttpMethod.Post, client.BaseAddress) { Content = new FormUrlEncodedContent(nvc) };
var resres = client.SendAsync(req).Result;
return "temp";
}
}
I want my response to be statuscode 200 with an access_token from google (Like it does in Postman)
[resolved] This is what I did (With the help from a few friends)
Changed Headers to JSON
Moved the Base URI to PostAsync()
Once I had change all that I could see that the error was in the redirect_Uri and I then changed it to match the one in postman. And now it works
protected void Page_Load(object sender, EventArgs e)
{
//you will get this, when any error will occur while authorization otherwise null
string Error = Request.QueryString["error"];
//authorization code after successful authorization
string Code = Request.QueryString["code"];
if (Error != null) { }
else if (Code != null)
{
//Remember, we have set userid in State
string UserId = Request.QueryString["state"];
//Get AccessToken
int Id = Convert.ToInt32(UserId);
string AccessToken = string.Empty;
string RefreshToken = ExchangeAuthorizationCode(Id, Code, out AccessToken);
//saving refresh token in database
SaveRefreshToken(Id, RefreshToken);
//Get Email Id of the authorized user
string EmailId = FetchEmailId(AccessToken);
//Saving Email Id
SaveEmailId(UserId, EmailId);
//Redirect the user to Authorize.aspx with user id
string Url = "Authorize.aspx?UserId=" + UserId;
Response.Redirect(Url, true);
}
}
private string ExchangeAuthorizationCode(int userId, string code, out string accessToken)
{
string baseurl = "https://accounts.google.com/o/oauth2/token";
accessToken = string.Empty;
string ClientSecret = ConfigurationManager.AppSettings["ClientSecrete"];
string ClientId = ConfigurationManager.AppSettings["ClientId"];
// //get this value by opening your web app in browser.
string RedirectUrl = "http://localhost:64716/GoogleCallback.aspx"; //I changed this to match the one in Postman
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri("https://accounts.google.com/o/oauth2"); //I replaced the Uri to "var result = client.PostAsync("https://accounts.google.com/o/oauth2/token", nvc).Result;"
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // I made this JSON
// I removed this "client.DefaultRequestHeaders.Add("Content-Type", "application/x-www-form-urlencoded");"
var nvc = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("client_id", ClientId),
new KeyValuePair<string, string>("client_secret", ClientSecret),
new KeyValuePair<string, string>("redirect_uri", RedirectUrl),
new KeyValuePair<string, string>("access_type", "offline")
});
var result = client.PostAsync("https://accounts.google.com/o/oauth2/token", nvc).Result;
var resultContent = result.Content.ReadAsStringAsync();
var tokenResponse = JsonConvert.DeserializeObject<Authorization_request_body>(resultContent.ToString());
return "temp";
}
}

Re-generating Postman request from C#

I'm having an issue creating a POST request from C# to generate a token. There's nothing wrong with the service because I can consume it using Postman.
Can someone help my out.
Following my coding. Every time I get a 403 error.
public async Task<string> LoginAsync(string userName, string password)
{
try
{
var keyValues = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
var request = new HttpRequestMessage(HttpMethod.Post, login_url);
request.Content = new FormUrlEncodedContent(keyValues);
var client = new HttpClient();
var authenticationBytes = System.Text.Encoding.ASCII.GetBytes("----------username-------- : ----------pwd----------");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authenticationBytes));
//request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(authenticationBytes));
var response = await client.SendAsync(request);
var jwtResponse = await response.Content.ReadAsStringAsync(); // contains access token
JObject jwtDynamic = JsonConvert.DeserializeObject<dynamic>(jwtResponse);
accessToken = jwtDynamic.Value<string>("access_token");
Debug.WriteLine(jwtResponse);
}
catch (Exception e)
{
Console.WriteLine(e);
}
return accessToken;
}
Following are the screenshots of Postman.
Thanks a lot.
you can use generating code from postman using RestSharp Library for .Net as you can see by clicking on code button below in the picture .
best regards

HttpClient: The uri string is too long

Given the following attempt to post data to a web service that generates PDF files, PDF rocket (which is awesome by the way).
I get the error Invalid URI: The uri string is too long
Why would anyone impose an arbitrary limit on POSTed data?
using (var client = new HttpClient())
{
// Build the conversion options
var options = new Dictionary<string, string>
{
{ "value", html },
{ "apikey", ConfigurationManager.AppSettings["pdf:key"] },
{ "MarginLeft", "10" },
{ "MarginRight", "10" }
};
// THIS LINE RAISES THE EXCEPTION
var content = new FormUrlEncodedContent(options);
var response = await client.PostAsync("https://api.html2pdfrocket.com/pdf", content);
var result = await response.Content.ReadAsByteArrayAsync();
return result;
}
I receive this rediculous error.
{System.UriFormatException: Invalid URI: The Uri string is too long.
at System.UriHelper.EscapeString
at System.Uri.EscapeDataString
at System.Net.Http.FormUrlEncodedContent.Encode
at System.Net.Http.FormUrlEncodedContent.GetContentByteArray
This reminds me of 640k ought to be enough... I mean really?
If, like me, you're faced with some wonky 3rd party web service that will only accept form content, you can work around the problem like this:
// Let's assume you've got your key-value pairs organised into a nice Dictionary<string, string> called formData
var encodedItems = formData.Select(i => WebUtility.UrlEncode(i.Key) + "=" + WebUtility.UrlEncode(i.Value));
var encodedContent = new StringContent(String.Join("&", encodedItems), null, "application/x-www-form-urlencoded");
// Post away!
var response = await client.PostAsync(url, encodedContent);
With a post can include the content in the http message instead of the URI. A uri has a max length of 2083 characters. You could send it as JSON in the http message instead of the URI which is the recommended way to send larger chunks of data in an HttpPost/HttpPut. I altered your code to make use of it. This assumes that your service you are contacting can work with JSON (.net Web Api out of the box should have no problem with this).
using (var client = new HttpClient())
{
// Build the conversion options
var options = new
{
value = html,
apikey = ConfigurationManager.AppSettings["pdf:key"],
MarginLeft = "10",
MarginRight = "10"
};
// Serialize our concrete class into a JSON String
var stringPayload = JsonConvert.SerializeObject(options);
var content = new StringContent(stringPayload, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.html2pdfrocket.com/pdf", content);
var result = await response.Content.ReadAsByteArrayAsync();
return result;
}
Make sure to install newtonsoft json.
I just solved a similar problem. For me I was integrating with a backend I didn't control and had to POST file along with form data (eg customerID) as form variables. So switching to JSON or Multipart would break the backend I didn't control. The problem was that large files would cause the FormUrlEncodedContent to throw an error saying "The uri string is too long".
This is the code that solved it for me after two days of effort (note still needs to be tweaked to be ASYNC).
private string UploadFile(string filename, int CustomerID, byte[] ImageData) {
string Base64String = "data:image/jpeg;base64," + Convert.ToBase64String(ImageData, 0, ImageData.Length);
var baseAddress = new Uri("[PUT URL HERE]");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { AllowAutoRedirect = true, UseCookies = true, CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
try {
//ENCODE THE FORM VARIABLES DIRECTLY INTO A STRING rather than using a FormUrlEncodedContent type which has a limit on its size.
string FormStuff = string.Format("name={0}&file={1}&id={2}", filename, HttpUtility.UrlEncode(Base64String), CustomerID.ToString());
//THEN USE THIS STRING TO CREATE A NEW STRINGCONTENT WHICH TAKES A PARAMETER WHICH WILL FormURLEncode IT AND DOES NOT SEEM TO THROW THE SIZE ERROR
StringContent content = new StringContent(FormStuff, Encoding.UTF8, "application/x-www-form-urlencoded");
//UPLOAD
string url = string.Format("/ajax/customer_image_upload.php");
response = client.PostAsync(url, content).Result;
return response.Content.ToString();
}
catch (Exception ex) {
return ex.ToString();
}
}
}
#Mick Byrne :
Thanks - your solution worked like a charme!
Here is my complete code:
public async Task DateienSendenAsync (string PfadUndDatei, string Dateiname, String VRPinGUID, String ProjektGUID, String VRPinX, String VRPinY, String VRPinZ)
{
var client = new HttpClient();
// Create the HttpContent for the form to be posted.
var requestContent = new[] {
new KeyValuePair<string, string>("dateiname", Dateiname),
new KeyValuePair<string, string>("bild", Convert.ToBase64String(File.ReadAllBytes(PfadUndDatei))),
new KeyValuePair<string, string>("VRPinGUID", VRPinGUID),
new KeyValuePair<string, string>("ProjektGUID", ProjektGUID),
new KeyValuePair<string, string>("ebene", "ebene"),
new KeyValuePair<string, string>("raumnummer", "raumnummer"),
new KeyValuePair<string, string>("ansichtsname", "ansichtsname"),
new KeyValuePair<string, string>("VRPinX", VRPinX),
new KeyValuePair<string, string>("VRPinY", VRPinY),
new KeyValuePair<string, string>("VRPinZ", VRPinZ),
};
String url = "http://yourhomepage/path/upload.php";
var encodedItems = requestContent.Select(i => WebUtility.UrlEncode(i.Key) + "=" + WebUtility.UrlEncode(i.Value));
var encodedContent = new StringContent(String.Join("&", encodedItems), null, "application/x-www-form-urlencoded");
// Post away!
var response = await client.PostAsync(url, encodedContent);
}

How to authenticate the Pardot API using HttpClient

I struggled for about a day trying to authenticate against the Pardot API. It didn't like how I was trying to post the message body. So I wanted to post the solution that worked for me. If you have any tips or alternatives I'd like to hear them.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
var url = "https://pi.pardot.com/api/login/version/3";
//(Edit) Shorter way to pass the parameters below
//var postData = new List<KeyValuePair<string, string>>
//{
// new KeyValuePair<string, string>("email", "<value>"),
// new KeyValuePair<string, string>("password", "<value>"),
// new KeyValuePair<string, string>("user_key", "<value>")
//};
var postData = new Dictionary<string, string>
{
{"email", "<value>"},
{"password", "<value>"},
{"user_key", "<value>"}
};
var httpContent = new FormUrlEncodedContent(postData);
using (var client = new HttpClient())
{
HttpResponseMessage response = client.PostAsync(url, httpContent).Result;
if (response.IsSuccessStatusCode)
{
string resultValue = response.Content.ReadAsStringAsync().Result;
}
}
Thanks!

Response body blank when using HttpClient POST WP8.1

I am having some issues with HttpClient POST on my windows phone app. Using the following code only returns the headers, but not the json response body that i need.
FormUrlEncodedContent formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password),
new KeyValuePair<string, string>("key", apiKey)
});
HttpClient client = new HttpClient();
var response = await client.PostAsync(loginUrl, formContent);
var response = await client.PostAsync(loginUrl, formContent);
will only return you a HttpResponseMessage, you will need to read the content from it like so to get the JSON/XML data.
string response_data = await response.Content.ReadAsStringAsync();
Now response_data will equal what you are looking for.

Categories

Resources