Post with HttpClient doesn't bind model - c#

Simple post from azure function to API
using (var response = await httpClient.PostAsJsonAsync(installationServiceUrl, deviceInstallation.ToRequestBody()))
{...}
API receives the request, but cannot bind model from Request
But Request.Content is not null and contains sent JSON object. Content-Type header set to application/json.
Any suggestions?
Update: As I got it, some how API thinks that Model is simple string value (locationId), at least that is how I understand from from ModelState.Keys collection. It contains only locationId.
Update: ToRequestBody method just changes the shape of the object
public static DeviceInstallationRequest ToRequestBody(this DeviceInstallation deviceInstallation)
{
return new DeviceInstallationRequest()
{
InstallationId = deviceInstallation.InstallationId,
Name = deviceInstallation.Name,
StartDateTime = deviceInstallation.StartDateTime,
EndDateTime = deviceInstallation.EndDateTime,
CreatedDateTime = deviceInstallation.CreatedDateTime,
InstallationType = deviceInstallation.InstallationType,
Production = deviceInstallation.Production,
Default = deviceInstallation.Default
}
}
And expected model on API side:
public class BindDeviceInstallationRequest
{
[Required]
public string InstallationId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public DateTime StartDateTime { get; set; }
[Required]
public DateTime EndDateTime { get; set; }
[Required]
public DateTime CreatedDateTime { get; set; }
[Required]
public InstallationType InstallationType { get; set; }
[Required]
public bool Production { get; set; }
[Required]
public bool Default { get; set; }
}

In case it is an encoding issue try building the content yourself and sending it to the server,
DeviceInstallationRequest model = deviceInstallation.ToRequestBody();
string json = JsonConvert.SerializeObject(model);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using (var response = await httpClient.PostAsync(installationServiceUrl, content)) {
//...
}
that way you have complete control of what is being sent to the server.
While debugging, inspect the raw JSON from the client and what is received on the server.

Related

In .net5.0 - Extranal API response time takes too long time - httpclient

I'm trying to get response a external API using httpclint in .netcore5.0.
Initially I got timeout exception. So I add client.Timeout = Timeout.InfiniteTimeSpan; after adding this response is come. but it takes more than 20 mins.
But I browser I can get API result within milliseconds.
How can I get response from API with a short time. Any idea to decrease this responding time?
startup.cs
services.AddHttpClient<IHolidayService, HolidayService>("PublicHolidaysApi", c => c.BaseAddress = new Uri("https://api.xmltime.com"));
service.cs
public class HolidayService : IHolidayService
{
private readonly IHttpClientFactory _clientFactory;
private readonly HttpClient _client;
public HolidayService(HttpClient client)
{
_client = client;
client.Timeout = Timeout.InfiniteTimeSpan;
}
public HolidayService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
_client = clientFactory.CreateClient("PublicHolidaysApi");
}
public async Task<Holiday> GetHolidays(string country,int year)
{
string url = string.Format($"/holidays?accesskey="MyAccessKey"&secretkey="MySecretKey"&version=3&country=ro&year=2021&lang=en");
var result = new Holiday();
using (var cts = new CancellationTokenSource(Timeout.InfiniteTimeSpan))
{
var response = await _client.GetAsync(url, cts.Token).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
result = await JsonSerializer.DeserializeAsync<List<Holiday>>(responseStream);
}
else
{
throw new HttpRequestException(response.ReasonPhrase);
}
}
return result;
}
}
}
model
public class Holiday
{
[JsonPropertyName("urlid")]
public string UrliId { get; set; }
[JsonPropertyName("url")]
public string Url { get; set; }
[JsonPropertyName("country")]
public Country Country { get; set; }
[JsonPropertyName("name")]
public Name Name { get; set; }
[JsonPropertyName("oneliner")]
public OneLiner OneLiner { get; set; }
[JsonPropertyName("date")]
public Date Date { get; set; }
[JsonPropertyName("types")]
public List<string> Types { get; set; }
[JsonPropertyName("uid")]
public string UId { get; set; }
}
public class Country
{
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
public class Name
{
[JsonPropertyName("lang")]
public string Lang { get; set; }
[JsonPropertyName("text")]
public string Text { get; set; }
}
public class OneLiner
{
[JsonPropertyName("lang")]
public string Lang { get; set; }
[JsonPropertyName("text")]
public string Text { get; set; }
}
public class Date
{
[JsonPropertyName("iso")]
public string iso { get; set; }
[JsonPropertyName("datetime")]
public DateTime? Datetime { get; set; }
}
}
There are so many possibilities in this situation and I can only give you a way to solve the problem.
First of all, we need to locate the reason why it is so slow. Is it the server or the client?
We can use packet capture tools such as Fiddler ,and then observe the corresponding network requests.
If client had send but server not response , you should think about the api limit...
And if not, the request are not send at all, may be you should the check the connection pool of the HttpClient, or the WorkThreadPool of dotnet.
there are a few problems with your question.
it doesn't compile.
it is incomplete.
it has sensitive data.
but I can get the data from API in no time. just open this link https://dotnetfiddle.net/ryjakT and run the program.
I changed few things
Return Type, it should be List
var result = new Holiday(); to var result = new List();
I am using Newtonsoft.Json for Deserialization.
you were trying to Deserialize to an incorrect model, it should be Root.

Populating an object with HTTP Post response

I am trying to read the result of this Post request...
public class Stuff
{
string token { get; set; }
string type { get; set; }
long expires_in { get; set; }
}
var request = new RestRequest(Method.POST);
IRestResponse response = client.Execute(request);
Stuff result = JsonConvert.DeserializeObject<Stuff>(response.Content);
I'm getting the correct response but I need to populate the object with the content.
response.Content is three values that match the names I'm using.
But result ends up null for those three values (0 for the long). Shouldn't it match up and populate the object?
The solution here is to make the properties in the class public.
public class Stuff
{
public string token { get; set; }
public string type { get; set; }
public long expires_in { get; set; }
}
If however you need/want a private setter you can use the JsonProperty attribute.
[JsonProperty]
public string token { get; private set; }
More information on this can be found at this question

Model State not checked in MVC WebAPI when called from Client

I have the following class defintion in my WebAPI:
public class LogIt
{
[Required(ErrorMessage = "Log ID required")]
public int LogID { get; set; }
public int StatusID { get; set; }
public string Message { get; set; }
public int UserID { get; set; }
}
When calling it with Restlet directly, it validated correctly and err's when LogID is not included in the JSON payload.
Here is a shell of what is called in the controller:
public IHttpActionResult PostTrace([FromBody]LogIt log)
{
if (ModelState.IsValid) {
LogIt.Insert(log);
}
return Ok();
}
From my client application, I have the following class defined:
public class LogIt
{
public int LogID { get; set; }
public int StatusID { get; set; }
public string Message { get; set; }
public int UserID { get; set; }
}
And the code that calls:
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:8080/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
LogIt log = new LogIt();
log.StatusID = 1;
log.Message = "Test";
log.UserID = 1;
HttpResponseMessage response = await client.PostAsJsonAsync("log/trace/insert", log);
response.EnsureSuccessStatusCode();
However, the LogID never gets checked in the WebAPI as a "Required" field, so modelstate is valid, and when the DB insert/update happens, I get an error (obviously)
Any idea why this is? Should I bring the attribute for required field into the client app as well?
When you call LogIt log = new LogIt(); in your client application, LogID will be set to 0 by the constructor. This is a value, so will be sent in your JSON payload as "logID: 0", for example.

JSON to object C# (mapping complex API response to C# object)

I am able to handle simple JSON serialization and deserialization but this API response seems little complicated, and I am seeking an advice as to what would be ideal approach to tackle this.
I'm trying to call an API for MVC application.
Goal is to map API data to model.
API endpoint is
https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=1min&apikey=MyAPIKey
Troubles here are:
JSON data keys have white space in them.
When I tried doing paste special in Visual studio, It gave me a long
list of classes for each date entry separately, because this API
call returns a separate set of information for date.
To solve problem explained in point 1, I used [JsonProperty("1. Information")] in class. And in my code..
public async Task TSI()
{
HttpClient client = new HttpClient();
//Uri uri = new Uri("http://date.jsontest.com/");
Uri uri = new Uri("https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=5min&apikey=demo");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
dynamic result = await response.Content.ReadAsAsync<object>();
IEnumerable<dynamic> dObj = JsonConvert.DeserializeObject<dynamic>(result.ToString());
IEnumerable<dynamic> t1 = dObj.FirstOrDefault();
IEnumerable<dynamic> t2 = dObj.LastOrDefault();
dynamic MetaData = t1.FirstOrDefault();
Rootobject ro = new Rootobject();
ro.MetaData = MetaData;
}
PS: I'm relatively new to make API calls and handling them.
I was able to make a call to
date.jsontest.com
and map the API data to model (which I had created using paste special)
//API response
{
"time": "12:53:22 PM",
"milliseconds_since_epoch": 1504875202754,
"date": "09-08-2017"
}
//C# code to map to API data
public class sampleObject
{
public string time { get; set; }
public long milliseconds_since_epoch { get; set; }
public string date { get; set; }
}
My RootObject looks like this:
public class Rootobject
{
[JsonProperty("Meta Data")]
public MetaData MetaData { get; set; }
[JsonProperty("Time Series (1min)")]
public TimeSeries1Min TimeSeries1min { get; set; }
}
public class MetaData
{
[JsonProperty("1. Information")]
public string _1Information { get; set; }
[JsonProperty("2. Symbol")]
public string _2Symbol { get; set; }
[JsonProperty("3. Last Refreshed")]
public string _3LastRefreshed { get; set; }
[JsonProperty("4. Interval")]
public string _4Interval { get; set; }
[JsonProperty("5. Output Size")]
public string _5OutputSize { get; set; }
[JsonProperty("6. Time Zone")]
public string _6TimeZone { get; set; }
}
// I have so many of these sub-classes for dates, which again is an issue
public class TimeSeries1Min
{
public _20170907160000 _20170907160000 { get; set; }
public _20170907155900 _20170907155900 { get; set; }
....
....}
public class _20170907160000
{
public string _1open { get; set; }
public string _2high { get; set; }
public string _3low { get; set; }
public string _4close { get; set; }
public string _5volume { get; set; }
}
public class _20170907155900
{
public string _1open { get; set; }
public string _2high { get; set; }
public string _3low { get; set; }
public string _4close { get; set; }
public string _5volume { get; set; }
}
It is hard to create a model from this json, but you can convert those data to dictionary
var jObj = JObject.Parse(json);
var metadata = jObj["Meta Data"].ToObject<Dictionary<string, string>>();
var timeseries = jObj["Time Series (1min)"].ToObject<Dictionary<string, Dictionary<string, string>>>();
The following code should do what you want
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
var obj = JsonConvert.DeserializeObject<Rootobject>(result);
//No idea what you want to do with this line as there is no MetaData property on the root object
obj.MetaData = MetaData;
}

preview JSON on DataGridView

I have an ASP MVC Web Api that returns these data as Json :
"[{\"OpID\":15,\"DeviceID\":1,\"DeviceType\":\"LED1\",\"DeviceState\":true,\"TurnOnTime\":\"2016-07-26T21:10:05.607\",\"TurnOffTime\":null,\"ToggleTime\":\"2016-07-26T21:10:05.61\",\"ToggleHour\":null},{\"OpID\":16,\"DeviceID\":5,\"DeviceType\":\"TV\",\"DeviceState\":true,\"TurnOnTime\":\"2016-07-26T21:10:09.283\",\"TurnOffTime\":null,\"ToggleTime\":\"2016-07-26T21:10:09.283\",\"ToggleHour\":null}]"
I`m trying to deserialize it using this code :
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://engeek.azurewebsites.net/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/operation").Result;
string str = await response.Content.ReadAsStringAsync();
List<operation> myDeserializedObjList = (List<operation>)JsonConvert.DeserializeObject(str , typeof(List<operation>));
dataGridView1.DataSource = myDeserializedObjList;
and here is my model:
class operation
{
[JsonProperty("OpID")]
public int OpID { get; set; }
[JsonProperty("DeviceID")]
public Nullable<int> DeviceID { get; set; }
[JsonProperty("DeviceType")]
public string DeviceType { get; set; }
[JsonProperty("DeviceState")]
public Nullable<bool> DeviceState { get; set; }
[JsonProperty("TurnOnTime")]
public Nullable<System.DateTime> TurnOnTime { get; set; }
[JsonProperty("TurnOffTime")]
public Nullable<System.DateTime> TurnOffTime { get; set; }
[JsonProperty("ToggleTime")]
public Nullable<System.DateTime> ToggleTime { get; set; }
[JsonProperty("ToggleHour")]
public Nullable<int> ToggleHour { get; set; }
}
and the serialization code :
public string Getoperation()
{
var data = new List<operation>();
data = db.operations.ToList();
string str = JsonConvert.SerializeObject(data, Formatting.None);
return str;
}
It gives me :
couldn`t convert or cast from string to List
What should I do ?
Somewhere in the MVC code where you are creating the JSON response, you are serializing the List<operation> as a JSON string, and then serializing that JSON string again.
Show the code where you serialize the List<operation> (that part looks good, thanks!) and the code where you return that JSON string to the client, and I'll probably be able to show you where that's happening.
You have a slight variation on the same problem this other guy had yesterday, except he only double-encoded one branch of his object, while you double-encoded the entire thing.

Categories