I am using the HttpClient in C# to try and integrate SSO (Single Sign On) into some of our custom Applications.
I have done this successfully in our JavaScript Apps, but I'm having some difficulty integrating it into some of our Umbraco sites.
My code so far:
using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;
using Newtonsoft.Json;
using System.Collections.Generic;
using Umbraco.Web;
using Umbraco.Web.WebApi;
namespace Umbraco.WebApi
{
public class TestController : UmbracoApiController
{
public HttpClient client = new HttpClient();
[HttpPost]
public async Task<Object> GetRefreshToken(Token t)
{
try {
string refToken = t.refresh_token;
var values = new Dictionary<string, string>
{
{ "grant_type", "refresh_token" },
{ "client_id", "CLIENTID" },
{ "client_secret", "CLIENTSECRET" },
{ "refresh_token", refToken }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("https://URL.org/Token", content);
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
} catch(HttpRequestException e) {
return e;
}
}
public class Token
{
public string refresh_token { get; set; }
}
public class AuthData
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_int { get; set; }
public string refresh_token { get; set; }
public string userName { get; set; }
public string client_id { get; set; }
public DateTime issued { get; set; }
public DateTime expires { get; set; }
}
}
}
Which does successfully return the data I'm after but there are issues with the returned data (Removed sensitive data):
<z:anyType xmlns:d1p1="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" i:type="d1p1:string">
{"access_token":"XXXXXXXX","token_type":"bearer","expires_in":1199,"refresh_token":"XXXXXX","userName":"XXXXXX","as:client_id":"XXXXX",".issued":"Fri, 20 Sep 2019 13:23:48 GMT",".expires":"Fri, 20 Sep 2019 13:43:48 GMT"}
</z:anyType>
It also seems to be getting returned as XML instead of JSON?
C# is not my strongest of languages, so I may be completely wrong.
Any help appreciated.
In your code, after you get the JSON string responseString, instead of returning it, try the following code.
...
string responseString = await response.Content.ReadAsStringAsync();
response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(responseString, Encoding.UTF8, "application/json");
return response;
You need to change your method return value from Task<Object> to Task<HttpResponseMessage>
Edit:
To access the properties, install the Newtonsoft.Json package and try below code.
var jsonString = ...
JObject jo = JObject.Parse(jsonString);
Console.WriteLine(jo["access_token"]);
One of the things you can do to resolve this is to specifically request a json format on your response by adding the corrent request headers
Accept: application/json
try it like so
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Now if the server pays attentions to the request headers it will return a json back to you.
It might be defaulting to xml because there is no such header in your request OR the server only supports returning xml responses.
EDIT: If you can not get the server to return json, you can convert your xml string response to json string. Take a look at this example. After the convertsion you can return your json string normally from your controller.
Edit:
Ok, try out this sample below:
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", ""),
new KeyValuePair<string, string>("scope", ""),
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("redirect_uri", ""),
new KeyValuePair<string, string>("code", ""),
new KeyValuePair<string, string>("client_secret","")
});
AADTokenResponse TokenResponse = null;
string _baseAddress = string.Format("https://yourTargetDomain.com/");
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMessage = await client.PostAsync("targetApiSegment", content);
if (responseMessage.IsSuccessStatusCode)
{
var responseString = await responseMessage.Content.ReadAsStringAsync();
TokenResponse = JsonConvert.DeserializeObject<AADTokenResponse>(responseString);
}
}
I think in this case, FormUrlEncodedContent is the wrong choice, and you'd want to use StringContent instead, similar to this:
var content = new StringContent(HttpUtility.UrlEncode(values), Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://URL.com/Token", content);
The reason being, I dont think FormUrlEncodeContent has an overload to accept adding content type.
Another alternative would be to switch to using SendAsync rather than PostAsync, as SendAsync has some additional flexibility.
Related
I'm a Unity developer tool and i want to post a GraphQL request with using System.Net.Http; (i don't want to use the GraphQL dll because there are compatibility problems).
But i have this error (got on the Debug.Log) :
POST body missing, invalid Content-Type, or JSON object has no keys.
My code :
static async Task<string> getEntityID(string path)
{
var values = new Dictionary<string, string>
{
{ "query", "query {topLevelEntityTypes {id}}" },
{ "variables", "{}" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://localhost:4000/graphql", content);
var responseString = await response.Content.ReadAsStringAsync();
Debug.Log(responseString);
return responseString;
}
Thank you !
Solution by ProgrammingLlama :
static async Task<string> getEntityID(string path)
{
var myObject = new QueryJson();
myObject.query = "query {topLevelEntityTypes {id}}";
myObject.variables = "{}";
var response = await client.PostAsync("http://localhost:4000/graphql", new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(myObject), Encoding.UTF8, "application/json"));
var responseString = await response.Content.ReadAsStringAsync();
Debug.Log(responseString);
return responseString;
}
Class json
internal class QueryJson
{
public string query { get; set; }
public string variables { get; set; }
}
I have the following code:
static async Task checkIMEI( double IMEI)
{
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://kelpom-imei-checker1.p.rapidapi.com/api?service=model&imei=" + IMEI.ToString() ),
Headers =
{
{ "X-RapidAPI-Host", "kelpom-imei-checker1.p.rapidapi.com" },
{ "X-RapidAPI-Key", "key" },
}
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
object result = await response.Content.ReadAsStringAsync();
MessageBox.Show("\n" + result);
}
}
Running this code I get the following
response
I would like to further break up this response and the individual data and assign it to a variable such as
string ModelNum= model_nb >> should show "SM-G891A"
String Brand = brand >> should show "Samsung Korea"
Your help would be appriciated.
first your Client is bad practice use this link HttpClientFactory Microsoft docs to refactor your client.
Then Create Class for your needed model for ex:
public class Mobile
{
public string ModelNum { get; set; }
public string Brand { get; set; }
}
then you should deserialize your result to your model:
var result = await response.Content.ReadAsStringAsync();
var model = JsonSerializer.Deserialize<Mobile>(result);
Web API:
I tried using [FormBody] and [FromForm] before the string stringcontent as well.
// POST api/<MAUserController>
[HttpPost("AuthenticateUser")]
public async Task<ActionResult<MAUser>> PostAsync(string stringcontent)
{
//stringcontent is null
}
Client Code:
List<KeyValuePair<string, string>> postParameters = new List<KeyValuePair<string, string>>();
postParameters.Add(new KeyValuePair<string, string>("Email", Email));
postParameters.Add(new KeyValuePair<string, string>("Password", Password));
var jsonString = JsonConvert.SerializeObject(postParameters);
var stringContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenString);
//httpClient.DefaultRequestHeaders.Add("Email", Email);
//httpClient.DefaultRequestHeaders.Add("Password", Password);
using (var response = await httpClient.PostAsync(API_URL, stringContent))
{
if (response.IsSuccessStatusCode)
{
//Success code
}
else
{
//Handle unsuccessful here
}
}
You're posting structured data in JSON format from the client. So why is the API just trying to accept a plain string? That doesn't make a lot of sense.
You should make it accept a model object with the correct structure, instead. ASP.NET will take care of binding the JSON properties to the model properties. However, you should also simplify the postParameters in the client as well - you're over-complicating the structure.
e.g.
Client:
var postParameters = new { Email = "abc#example.com", Password = "xyz" };
Server:
public async Task<ActionResult<MAUser>> PostAsync([FromBody] UserCredentials credentials)
{
}
where UserCredentials is a DTO class like this:
public class UserCredentials
{
public string Email { get; set; }
public string Password { get; set; }
}
ok so when using content type you are basically telling the receiving API how to parse the request payload and so by telling it application/json the framework is trying to parse the payload from JSON to an object and assign it to your parameter ( not really since you need to add an attribute [FromBody] ) but it can not do it since you are expecting a string, so either change your content type or change your parameter type ( i would suggest your parameter type ) .
So I am trying to use GET requests with c# but nothing is really working out.
I wanted to use the site https://covid19.mathdro.id/api as a test to see if I can get the info out of there and use it in a windows form. But I can't seem to figure out how. The only guides I found weren't really that helpful and it just confused me more. Would anyone be able to help me out?
I have tried to use the HttpClient with JSON.net but I get confused in it.
Been trying for the past 2 hours since I never dealt with HTTP GET Requests in c# other than with Python.
Install the 'Newtonsoft.Json' nuget package.
async Task<JToken> GetREST(string uri)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("");
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
return JToken.Parse(jsonData);
}
}
return null;
}
async private void button1_Click(object sender, EventArgs e)
{
var jObj = await GetREST("https://covid19.mathdro.id/api");
var confirmed = jObj["confirmed"];
Console.WriteLine("Confirmed:" + confirmed["value"]);
var confirmedJSON = await GetREST(confirmed["detail"].ToString());
Console.WriteLine(confirmedJSON);
}
In addition to the accepted answer, you can always work with the data as objects by deserializing - I prefer this method over using JToken etc as it tends to be very easy to work with the objects (there's often less boilerplate to pull bits of data from the response).
public async Task<CovidData> GetCovidData(string uri)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("");
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<CovidData>(jsonData);
}
}
return null;
}
The objects you'd deserialize to would look like this:
public class CovidData
{
public ValueDetailPair Confirmed { get; set; }
public ValueDetailPair Recovered { get; set; }
public ValueDetailPair Deaths { get; set; }
}
public class ValueDetailPair
{
public int Value { get; set; }
// If you need the link to the detail it would be deserialized to this string member
public string Detail { get; set; }
}
It really does depend on preference and your use case though.
example:
var data = await GetCovidData("https://covid19.mathdro.id/api");
Console.WriteLine(data.Confirmed.Value);
Console.WriteLine(data.Confirmed.Detail);
Console.WriteLine(data.Recovered.Value);
I'm trying to post a long string (containing XML) to my Web API controller, but I'm failing miserablely.
The following works if TextAsXml is short, but when TextAsXml is long it fails "Invalid URI: The Uri string is too long.", which is understandable.
// Client code
using (var client = new HttpClient())
{
var requestUri = "http://localhost:49528/api/some";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Author", "John Doe"),
new KeyValuePair<string, string>("TextAsXml", "<?xml version=\"1.0\" encoding=\"UTF-8\"?><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>")
});
var response = client.PostAsync(requestUri, content).Result;
response.EnsureSuccessStatusCode();
}
// Controller code
public HttpResponseMessage Post(SomeModel someModel)
{
// ...
return Request.CreateResponse(HttpStatusCode.OK);
}
public class SomeModel
{
public string Author { get; set; }
public string TextAsXml { get; set; }
}
How do I get the above code to work when TextAsXml is long? I tried playing around with StringContent and MultipartContent, but couldn't get it to work.
// This results in 500 Internal server error.
using (var client = new HttpClient())
{
var requestUri = "http://localhost:49528/api/some";
var textAsXml = File.ReadAllText("Note.xml");
var content = new MultipartFormDataContent();
content.Add(new StringContent("John Doe"), "Author");
content.Add(new StringContent(textAsXml), "TextAsXml");
var response = client.PostAsync(requestUri, content).Result;
response.EnsureSuccessStatusCode();
}
Send the XML as content without Uri encoding.