HttpClient Post deserialize error on SalesForce RestApi - c#

I'm trying to use HttpClient to POST a new Account via SalesForce RESTApi, but I'm receiving the following error:
message: "Can not deserialize SObject out of VALUE_STRING token at [line:1, column:1]".
errorCode: "JSON_PARSE_ERROR".
I'm using the same json for WebRequest and HttpClient tests. With WebRequest, it works well.
Here is my code with HttpClient:
var uri = "https://na15.salesforce.com/services/data/v27.0/sobjects/Account";
var acc = new Account();
acc.Name = "RestAPIHttpClient";
var ser = new JavaScriptSerializer();
var json = ser.Serialize(acc);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + binding.SessionHeaderValue.sessionId);
var response = await client.PostAsJsonAsync(uri, json);
var stringresponse = await response.Content.ReadAsStringAsync();
Console.WriteLine(stringresponse);
The account class that im serializing is just that simple:
private class Account
{
public string Name { get; set; }
}
UPDATE:
I changed the code to use PostAsync besides PostAsJsonAsync, then i added the JsonFormatter at the Content and now it is working. Would be great to know why PostAsJsonAsync doesn't work.
System.Net.Http.Formatting.MediaTypeFormatter jsonFormatter =
new System.Net.Http.Formatting.JsonMediaTypeFormatter();
System.Net.Http.HttpContent content =
new System.Net.Http.ObjectContent<Account>(acc, jsonFormatter);
var response = await client.PostAsync(uri, content);

Related

HTTP PUT Request Body Found Null In Web API Controller

I am trying to send a request with HTTP Verb [HttpPut] which reached to my controller but param which I have sent is Null. Have seen lot of stack Overflow same thread and tried out but cannot figure out... Weird!
Class I have Serialized
Content requestContent = new Content();
requestContent.Name = "Name";
requestContent.Value = "Value";
Here is my request body
private readonly HttpClient _httpClient;
public GetAzureResponseClient(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
var requestBody = JsonConvert.SerializeObject(requestContent);
var uri = new Uri("http://localhost:64288/api/ConnectUs/TestMethod");
var response = _httpClient.PutAsJsonAsync(uri, new StringContent(requestBody, Encoding.UTF8, "application/json")).Result;
client.DefaultRequestHeaders.Add("Authorization", "Basic" + "YourAuthKey");
var responseFromServer = await response.Content.ReadAsStringAsync();
My Web API Controller
public ActionResult<Content> TestMethod([FromBody]Content param)
You dont need to rewrap the object as JSON when using PutAsJsonAsync:
HttpClient client = new HttpClient();
var uri = new Uri("http://localhost:64288/api/ConnectUs/TestMethod");
var response = await client.PutAsJsonAsync(uri, requestContent); // LOOK HERE
client.DefaultRequestHeaders.Add("Authorization", "Basic" + "YourAuthKey");
var responseFromServer = await response.Content.ReadAsStringAsync();
you don't need extra serialization and you have to call the Async method with 'await'.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Basic" + "YourAuthKey");
var uri = new Uri("http://localhost:64288/api/ConnectUs/TestMethod");
var response = await client.PutAsJsonAsync(uri, requestContent);
var responseFromServer = await response.Content.ReadAsStringAsync();
https://learn.microsoft.com/en-us/previous-versions/aspnet/hh944690(v%3Dvs.118)
PutAsJsonAsync will serialize the given object of type T, try PutAsync instead
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.putasync?view=netframework-4.8

How do you use Basic Authentication with System.Net.Http.HttpClient?

I'm trying to implement a rest client in c# .net core that needs to first do Basic Authentication, then leverage a Bearer token in subsequent requests.
When I try to do Basic Authentication in combination with client.PostAsync with a FormUrlEncodedContent object, I'm getting an exception:
System.InvalidOperationException occurred in System.Net.Http.dll: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
//setup reusable http client
HttpClient client = new HttpClient();
Uri baseUri = new Uri(url);
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;
//Post body content
var values = new List<KeyValuePair<string,string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);
//Basic Authentication
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
content.Headers.Add("Authorization", $"Basic {base64EncodedAuthenticationString}");
//make the request
var task = client.PostAsync("/oauth2/token",content);
var response = task.Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
Exception has occurred: CLR/System.InvalidOperationException
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Net.Http.dll: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
at System.Net.Http.Headers.HttpHeaders.GetHeaderDescriptor(String name)
at System.Net.Http.Headers.HttpHeaders.Add(String name, String value)
It looks like you can't use PostAsync and have access to mess with the Headers for authentication. I had to use an HttpRequestMessage and SendAsync.
//setup reusable http client
HttpClient client = new HttpClient();
Uri baseUri = new Uri(url);
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;
//Post body content
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/oauth2/token");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString);
requestMessage.Content = content;
//make the request
var task = client.SendAsync(requestMessage);
var response = task.Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
It's not a good practice to create HttpClients explicitly from your calling code.
Please use HttpClientFactory that simplifies a lot of things.
However, if you want to use basic authentication, just create an HttpRequestMessage and add the following header:
var request = new HttpRequestMessage(HttpMethod.Post, getPath)
{
Content = new FormUrlEncodedContent(values)
};
request.Headers.Authorization = new BasicAuthenticationHeaderValue("username", "password");
// other settings
If you decide to use a recommended IHttpClientFactory it's even simpler:
serviceCollection.AddHttpClient(c =>
{
c.BaseAddress = new Uri("your base url");
c.SetBasicAuthentication("username", "password");
})
Don't encode the whole authentication string - encode the "Username:Password" expression and append the result to the "Basic " prefix.
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
content.Headers.Add("Authorization", "Basic " + base64EncodedAuthenticationString);
Also, consider using just ASCII encoding - the UTF8 may not be understood by the server unless you add a charset declaration to the header.
Wikipedia seems to cover this quite well.
The specific problem is this line (below)
content.Headers.Add("Authorization", $"Basic {base64EncodedAuthenticationString}");
This fails because HttpContent.Headers (System.Net.Http.Headers.HttpContentHeaders) is only for headers that are content-specific, such as Content-Type, Content-Length, and so on.
You've stated that you can't use DefaultRequestHeaders because you only need it for a single request - but you also can't use it with PostAsync - only SendAsync provided you construct the HttpRequestMessage yourself, as per your own answer and #NeilMoss' answer - but you could use an extension-method in future.
But for the benefit of other readers, another alternative is to add a new extension method based on the existing PostAsync, which is actually really simple (only 3 lines!):
public Task<HttpResponseMessage> PostAsync( this HttpClient httpClient, Uri requestUri, HttpContent content, String basicUserName, String basicPassword, String? challengeCharSet = null, CancellationToken cancellationToken = default )
{
if( basicUserName.IndexOf(':') > -1 ) throw new ArgumentException( message: "RFC 7617 states that usernames cannot contain colons.", paramName: nameof(basicUserName) );
HttpRequestMessage httpRequestMessage = new HttpRequestMessage( HttpMethod.Post, requestUri );
httpRequestMessage.Content = content;
//
Encoding encoding = Encoding.ASCII;
if( challengeCharSet != null )
{
try
{
encoding = Encoding.GetEncoding( challengeCharSet );
}
catch
{
encoding = Encoding.ASCII;
}
}
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(
scheme : "Basic",
parameter: Convert.ToBase64String( encoding.GetBytes( userName + ":" + password ) )
);
return SendAsync( httpRequestMessage, cancellationToken );
}
Usage:
HttpClient httpClient = ...
using( HttpResponseMessage response = await httpClient.PostAsync( uri, content, basicUserName: "AzureDiamond", basicPassword: "hunter2" ).ConfigureAwait(false) )
{
// ...
}
Just something to add that I struggled with, which I only experienced with Basic authentication endpoints. If you add Json as StringContent then it adds a charset=utf-8, this often return a BadRequest 400.
Here is the code I got to fix this: reference:
https://dzone.com/articles/httpclient-how-to-remove-charset-from-content-type
using (var client = new HttpClient())
using (var content = new StringContent(ParseJSON(data), Encoding.Default, "application/json"))
{
//Remove UTF-8 Charset causing BadRequest 400
content.Headers.ContentType.CharSet = "";
var clientId = "client";
var clientSecret = "secret";
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
client.DefaultRequestHeaders.TryAddWithoutValidation(authHeader, authorization);
var response = await client.PostAsync(url, content);
return response;
}
I have resolve this by using below code, that serve my purpose also. Added Code for both Get/Post, this will help you. Moreover I have added one more Header key. So to pass extra data to header. Hope that will resolve your issue.
class Program {
private static readonly string Username = "test";
private static readonly string Password = "test#123";
static void Main(string[] args) {
var response = Login();
}
public static async Task Login()
{
var anotherKey ="test";
HttpClient httpClient = new HttpClient
{
BaseAddress = new Uri("https://google.com/")
};
httpClient.DefaultRequestHeaders.Add($"Authorization", $"Basic {Base64Encode($"{Username}:{Password}")}");
httpClient.DefaultRequestHeaders.Add($"anotherKey", $"{anotherKey}");
HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("user/123").ConfigureAwait(false);
// For Get Method
var response= await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
// For Post Method
User user = new User (1,"ABC");
HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync("/post", user).ConfigureAwait(false);
UserDetail userDetail = await httpResponseMessage.Content.ReadAsAsync<UserDetail>().ConfigureAwait(false);
}
}
Using .NET 6, I use the HttpClient.DefaultRequestHeaders.Authorization property to set the Authorization header.
// This example will send a signing request to the RightSignature API
var api = "https://api.rightsignature.com/public/v2/sending_requests";
// requestJson is the serialized JSON request body
var contentData = new StringContent(requestJson, Encoding.UTF8, "application/json");
// Instantiate client (for testing), use Microsoft's guidelines in production
var client = new HttpClient();
// Use basic auth, the token has already been converted to base64
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", tokenB64);
try
{
var response = await client.PostAsync(api, contentData);
}
...
Good luck!

How to call web api with multiple parameter in c# xamarin forms

I know this is a repeated question. I have tried many examples showed on stackoverflow. But the API is still not calling and it shows 404 not found error. I have tried the below examples,
Example 1:
using (var loginData = new MultipartFormDataContent())
{
loginData.Add(new StringContent(JsonConvert.SerializeObject(login.Email)), "emailId");
loginData.Add(new StringContent(JsonConvert.SerializeObject(this.Password)), "password");
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await httpClient.PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", loginData);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
Application.Current.MainPage = new AppShell();
}
}
Example 2:
JObject loginData = new JObject();
loginData.Add("emailId", login.Email);
loginData.Add("password", this.Password);
var httpClient = new HttpClient();
var httpContent = new StringContent(loginData.ToString(), Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", httpContent);
My API:
[Route("authenticate")]
[HttpPost]
public HttpResponseMessage Login(HttpRequestMessage request, string emailId, string password)
{
}
Please suggest me to pass multiple parameters to web API in c# xamarin forms.
You can generate code by postman. Then check hat you are missing. you can even use same code generated by postman. https://learning.getpostman.com/docs/postman/sending_api_requests/generate_code_snippets/
just install restSharp nuget package.. it will work with postman generated code.
Instead of creating JObject create a class which holds your emailId and password property like below.
public class UserModel
{
public string emailId {get;set;}
public string password {get;set;}
}
now later instantiate UserModel and assign values like below
UserModel user = new UserModel
{
emailId = "emailId",
password = "password"
};
Serialize UserModel like below
string serializedModel = await Task.Run(() => JsonConvert.SerializeObject(user));
var contents = new StringContent(serializedModel);
contents.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
Make a Post Request
var response = await httpClient.PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", contents);
If the above solution doesn't work try changing your Web Api method route like below
Route["api/users/authenticate"]
Hope this will solve your issue... :)
you could try this:
using (var httpclient = new HttpClient())
{
var formcontent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string,string>("emailId",emailId),
new KeyValuePair<string, string>("password","password")
});
var request = await httpclient .PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", formcontent);
request.EnsureSuccessStatusCode();
var response = await request.Content.ReadAsStringAsync();
}

Cant get Authorized with my API that has a JWT authentication Im Working With Xamarin

Well Im new in Xamarin and I'm developing and App, the authentication is JWT based.
Im using a HttpClient and setting the AuthenticationHeaders but It always returns Unauthorized when I try it on Postman it Works but I can't make it work in my app.
Here is how im trying to do it:
var client = new HttpClient(new HttpClientHandler());
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("JWT", accessToken);
client.BaseAddress = new Uri(urlBase);
var url = string.Format("{0}{1}", servicePrefix, controller);
var response = await client.GetAsync(url);
Try something like this
using (var client = new HttpClient())
{
var uri = new Uri(string.Format($"{<yourURLString>}", string.Empty));
var jsonTransport = "";
var jsonPayload = new StringContent(jsonTransport, Encoding.UTF8, "application/json");
//client.DefaultRequestHeaders.Add("Content-type", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "JWT " + accessToken);
var response = await client.PostAsync(uri, jsonPayload);
string responseContent = await response.Content.ReadAsStringAsync();
}
then deserialize the responseContent to your object using JsonConvert.DeserializeObject
Note: Below are code samples, edit to your own objects
SubscriptionResponse profileResponse = JsonConvert.DeserializeObject<SubscriptionResponse>(responseContent);
then if your method returns something, use the return statement. Something like this
return profileResponse.Data.Subscriptions;
If you're using a get, this could be a guide
var uri = new Uri(string.Format($"{<yourURLHere>}", string.Empty));
client.DefaultRequestHeaders.Add("Authorization", "JWT " + accessToken);
var httpResponse = await client.GetAsync(uri);
var responseContent = await httpResponse.Content.ReadAsStringAsync();
then deserialize your string response
Note: this is a sample - edit to your model (You may use PostMan to get the response format in JSON and model it in C#)
var UserDetailResponse = JsonConvert.DeserializeObject<UserDetail>(responseContent);
return UserDetailResponse;

How to call API in asp.net MVC5

I have asp.net mvc5 project that I want to call another API using JSON,
and I want to call that API from my Controller action because I need to do some hashing in there,
It's my first time doing this, and I need to send the request in JSON and also get responses in JSON all of that using the controller action.
If your method is POST :
string uri = "yourdomain/api/controller/method;
var client = new HttpClient();
var values = new Dictionary<string, string>()
{
{"username", SecurityHelper.EncryptQueryString(username)},
{"password", SecurityHelper.EncryptQueryString(password)},
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync(uri, content);
response.EnsureSuccessStatusCode();
If your method is GET :
string url = "domain/api/controller/method?parameter1=param";
using (var client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(url).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var jsonResponse = response.Content.ReadAsStringAsync().Result;
bool data = JsonConvert.DeserializeObject<bool>(jsonResponse);
return data;
}
}
var client = new HttpClient();
var payload = #"{
'CPU': 'Intel',
'PSU': '500W',
'Drives': [
'DVD read/writer',
'500 gigabyte hard drive',
'200 gigabype hard drive'
]
}";
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var url = {APIEndpoint};
var result = await client.PostAsync(url, content);
Response parsing using JSON.NET:
JObject joResponse = JObject.Parse(result);

Categories