WEB API 1 - Pass JSON ARRAY from .NET CLIENT - c#

I am trying to create one WEB API controller (Service method) which accept an array of a class object.
And then also a .NET client which makes a call to this API method and pass JSON string (array of class object). Issue is I am not able to receive json array contents on server side. Seems some serialization/de-serialization error but I am not able to spot. Please see sample code as below:
C# class as below:
public class UserData
{
public int ID { get; set; }
public DateTime DATETIME { get; set; }
public int SEQUENCE { get; set; }
}
And then WEB API method (API Controller as below)
[HttpPost()]
public HttpResponseMessage Post([FromBody()]
IEnumerable<#UserData> RequestBody)
{
}
Json array as below
[
{
"ID": 1,
"DATE": "2014-01-01",
"SEQUENCE": 533
},
{
"ID": 2,
"DATE": "2015-01-01",
"SEQUENCE": 3233
},
{
"ID": 3,
"DATE": "2015-01-01",
"SEQUENCE": 233
}
]
And the .NET Client as below:
public void CallService(string jsonString)
{
try {
var client = new RestClient(GetBaseURLService());
var requestRest = new RestRequest("event ", Method.POST);
var RequestBody = TextBoxCreateEventJson.Text;
requestRest.AddBody(jsonString);
requestRest.RequestFormat = DataFormat.Json;
var res = client.Execute(requestRest);
} catch (Exception ex) {
}
}
And then I get null/nothing in the RequestBody.
I know something I need to do before a call to requestRest.AddBody(jsonString);
But not sure what?

In System.Web.Helpers namespace, there is a Json class which you can use in order to encode or decode. For your case,
string jsonString = Json.Encode(your array as an argument)
Use jsonString in the body of your request.

Related

Trouble deserializing JSON to a C# object [duplicate]

This question already has answers here:
System.Text.Json.JsonException: The JSON value could not be converted
(4 answers)
Closed 12 days ago.
I am working on a WPF MVVM application to retrieve some cryptocurrency information from this API. I am able to call the API and get an HTTP response, however, I am having trouble deserializing this response to an object. I understand that the symbol variable is passed but not used, however, I want the deserialization process to work and then I will format the URI accordingly to include the symbol and the API Key. Here is the code:
Crypto Object
public class Crypto
{
public string? Symbol { get; set; }
public string? Name { get; set; }
public double? Price { get; set; }
public double? ChangesPercentage { get; set; }
}
API Call Service Interface
public interface ICryptoService
{
Task<Crypto> GetCrypto(string symbol);
}
API Call Service
public async Task<Crypto> GetCrypto(string symbol)
{
using (HttpClient client = new HttpClient())
{
using var response = await client.GetAsync("https://financialmodelingprep.com/api/v3/quote/BTCUSD?apikey=KEY", HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
if (response.Content is object && response.Content.Headers.ContentType.MediaType == "application/json")
{
var responseStream = await response.Content.ReadAsStreamAsync();
try
{
return await System.Text.Json.JsonSerializer.DeserializeAsync<Crypto>(responseStream, new System.Text.Json.JsonSerializerOptions { IgnoreNullValues = true, PropertyNameCaseInsensitive = true });
}
catch (JsonException)
{
Console.WriteLine("Invalid JSON!");
}
}
else
{
Console.WriteLine("HTTP Response cannot be deserialised");
}
return null;
}
}
}
Main Method
CryptoService cryptoService = new CryptoService();
cryptoService.GetCrypto("BTCUSD").ContinueWith((task) =>
{
var crypto = task.Result;
});
I am attaching the JSON response that the link will provide below:
[
{
"symbol": "BTCUSD",
"name": "Bitcoin USD",
"price": 22887.08,
"changesPercentage": -0.1263,
"change": -28.9473,
"dayLow": 22887.08,
"dayHigh": 23351.51,
"yearHigh": 48086.836,
"yearLow": 15599.047,
"marketCap": 441375461059,
"priceAvg50": 19835.04,
"priceAvg200": 19730.518,
"volume": 27292504064,
"avgVolume": 23965132574,
"exchange": "CRYPTO",
"open": 23267.4,
"previousClose": 23267.4,
"eps": null,
"pe": null,
"earningsAnnouncement": null,
"sharesOutstanding": 19284918,
"timestamp": 1675872360
}
]
This is the exception that I get whenever I run the code:
Exception thrown: 'System.Text.Json.JsonException' in System.Private.CoreLib.dll
Your json is a json array, not a single json object, so you need to deserialize to a collection:
var result = await JsonSerializer.DeserializeAsync<List<Crypto>>(...);
P.S.
In general case it is recommended to just await async functions instead of using ContinueWith.

Absolutely lost with RestSharp

I am pretty new to C# and want to create a REST Get request with Basic auth.
I'm using RestSharp but i can't find a proper working full example.
This is my code:
using RestSharp;
using RestSharp.Authenticators;
namespace HttpClientAPP
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://data.myhost.de");
client.Authenticator = new HttpBasicAuthenticator("user", "xxxx");
var request = new RestRequest("resource",Method.Get);
client.ExecuteGetAsync(request).Wait();
Console.WriteLine("as");
}
}
}
This is thejson answer I expect and i want to parse it to use the data to store it in a MYSQL DB:
{"info": [
{
"method": "GET",
"url": "https://data.myhost.de/zfa-values/{zfaId}",
"description": "Get a ZFA value by id"
},
{
"method": "GET",
"url": "https://data.myhost.de/zfa-values",
"description": "Get a list of available ZFA values"
},
{
"method": "GET",
"url": "https://data.myhost.de/new-zfa-values",
"description": "Get a list of available ZFA values which have not been viewed yet"
},
{
"method": "GET",
"url": "https://data.myhost.de/",
"description": "Get a list of api endpoints"
}
]}
How do I parse the json response?
At https://restsharp.dev/v107/#recommended-usage there is some guidance (but at time of writing this answer I think it might have a typo)
Get docs says:
public class GitHubClient {
readonly RestClient _client;
public GitHubClient() {
_client = new RestClient("https://api.github.com/")
.AddDefaultHeader(KnownHeaders.Accept, "application/vnd.github.v3+json");
}
public Task<GitHubRepo[]> GetRepos()
=> _client.GetAsync<GitHubRepo[]>("users/aspnet/repos");
^^^^^^^^
//note: I think this should be GetJsonAsync
}
Post docs says:
var request = new MyRequest { Data = "foo" };
var response = await _client.PostAsync<MyRequest, MyResponse>(request, cancellationToken);
^^^^^^^^^ ^^^^^^^
//note: i think this should be PostJsonAsync and the first arg should be a string url path
You have classes that represent the json you send/get back, and RestSharp will de/serialize them for you. If you want some help making classes from JSON, take a look at services like http://app.QuickType.io - you paste your json and you get representative classes. The online generators are usually a bit more sophisticated in terms of what they interpret/the attributes they decorate with. Pasting your json into QT gives (you can choose a better name than SomeRoot):
namespace SomeNamespace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class SomeRoot
{
[JsonProperty("info")]
public Info[] Info { get; set; }
}
public partial class Info
{
[JsonProperty("method")]
public string Method { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
}
public partial class SomeRoot
{
public static SomeRoot FromJson(string json) => JsonConvert.DeserializeObject<SomeRoot>(json, SomeNamespace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this SomeRoot self) => JsonConvert.SerializeObject(self, SomeNamespace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}
And use like:
using RestSharp;
using RestSharp.Authenticators;
namespace HttpClientAPP
{
class Program
{
static async Task Main(string[] args)
{
var client = new RestClient("https://data.myhost.de");
client.Authenticator = new HttpBasicAuthenticator("user", "xxxx");
var res = await client.GetJsonAsync<SomeRoot>("");
Console.ReadLine(); //prevent exit
}
}
}
First, you can use newtosoft json nuget package:
Docs: https://www.newtonsoft.com/json
Example:
class ApiEndpoint{
public string Method {get;set;} // or you can use enum
public string Url {get;set;}
public string Description {get;set;}
}
// some logic
public void Fetch()
{
var client = new RestClient("https://data.myhost.de");
client.Authenticator = new HttpBasicAuthenticator("user", "xxxx");
var request = new RestRequest("resource",Method.Get);
var response = client.Get(request);
// read response as json
var json = JsonConvert.DeserializeObject<ICollection<ApiEndpoint>>(response);
// now json will have structure in your example
}
Other way is to add swagger gen for you server, get openapi.json and then generate sharp client using nswag
https://blog.logrocket.com/generate-typescript-csharp-clients-nswag-api/

HTTP client method null exception

I have an API project and I need to develop a web project using the API I wrote some code but not able to find the exception and problem and not getting data from the link.
Here is my Service Code:
public async Task<IEnumerable<AgentReadDto>> GetAgent()
{
IEnumerable<AgentReadDto> agents = new List<AgentReadDto>();
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44331/api/");
var response = client.GetAsync("Agent/GetAllAgent");
response.Wait();
var result = response.Result;
if (result.IsSuccessStatusCode)
{
var readTask =JsonConvert.DeserializeObject<IList<AgentReadDto>>(await result.Content.ReadAsStringAsync());
agents = readTask;
}
}
return agents;
}
And my controller code is look like this:
public IActionResult AgentLists()
{
var agentsList = _agentRespositoryWeb.GetAgent();
if (agentsList != null )
{
ViewBag.Message = "There was a problem retrieving agent from the database or no agents exists";
}
ViewBag.SuccessMessage = TempData["SuccessMessage"];
return View(agentsList);
}
My api return the value following:
{
"agentDetail": [
{
"usersId": 85,
"firstName": "Amit",
"lastName": "One",
"gender": "Male",
"informationTips": [
{
"video": "https://www.w3schools.com/html/movie.mp4"
},
{
"video": "https://www.w3schools.com/html/movie.mp4"
},
]
},
{
"usersId": 86,
"firstName": "Amit",
"lastName": "Two",
"gender": "Male",
"informationTips": [
{
"video": "https://www.w3schools.com/html/movie.mp4"
}
]
}
]
}
For exception I added image there is three image that take screen on the different steps:
Your model is set to IEnumerable<AgentReadDto>, but you've forgotten to await the call to GetAgent inside of the AgentLists action. This means there's a mismatch between what the view expects (IEnumerable<AgentReadDto>) and what it receives (Task<IEnumerable<AgentReadDto>>).
To fix this, convert AgentLists to an async method and then await the call to GetAgent. Here's a fixed version of the AgentLists action:
public async Task<IActionResult> AgentLists()
{
var agentsList = await _agentRespositoryWeb.GetAgent();
if (agentsList != null)
{
ViewBag.Message =
"There was a problem retrieving agent from the database or no agents exists";
}
ViewBag.SuccessMessage = TempData["SuccessMessage"];
return View(agentsList);
}
It looks like you also have a mismatch between the type you expect to be returned and the JSON actually being returned. The JSON represents an object with a list inside of it, but you're attempting to parse it as a simple list. To fix that, create a wrapper class that matches the structure of the response. For example, create the following class:
public class ApiResponse
{
public IEnumerable<AgentReadDto> AgentDetail { get; set; }
}
Update the deserialization logic to use this new type:
var apiResponse = JsonConvert.DeserializeObject<ApiResponse>(...);
var agentsLit = apiResponse.AgentDetail;

Patch API call in C# doesn't work, the same call works in swagger

I want to use an external API which has Swagger. In Swagger I am calling this url:
PATCH /rest/inventory/item/{id}
with parameters: X-Auth-Token, id and patchOperations which looks like this:
[
{
"op": "replace",
"path": "price",
"value": 6.2
}
]
And when I call this method with those parameters, it works. I get success code 200 and afterwards when I call the GET method I see that the price of the item has been updated to 6.2.
Now I want to do this in C#. I am already calling some GET methods from the same API successfully. This is my code for the PATCH method:
var model = new Dictionary<string, object>
{
{"op", "replace"},
{"path", "price"},
{"value", 6}
};
var blabla = await _httpProvider.PatchAsync($"https://{url}/server/rest/inventory/item/{id}", model, null, null, null, connection.Request.HeaderParameters);
public async Task<HttpResponseModel> PatchAsync<T>(string uri, T data, HttpClientHandler httpClientHandler = null, TimeSpan? timeout = null, string contentTypes = null, Dictionary<string, string> headerParameters = null)
{
using (var client = CreateHttpClient(httpClientHandler, timeout, contentTypes, headerParameters))
{
var requestContent = new StringContent(JsonConvert.SerializeObject(data));
var response = await client.PatchAsync(new Uri(uri), requestContent);
var result = new HttpResponseModel
{
Success = response.IsSuccessStatusCode,
ResponseContent = await response.Content.ReadAsStringAsync(),
ResponseTime = sw.Elapsed
};
return result;
}
}
Where is my mistake? I am getting error StatusCode: 500, ReasonPhrase: 'Server Error', Version: 1.1, Content: System.Net.Http.StreamContent
The mistake is that you're not pasting the same content, not quite anyway.
Your PATCH example is an array of a objects that has 3 properties, in your example there is only 1 element in the array, but it is still an array. Your C# is serialized into single object.
It's subtle but your JSON that you are sending is actually:
{
"op": "replace",
"path": "price",
"value": 6
}
So instead you need to send your dictionary or other object inside an array:
var model = new List<object> {
{
new Dictionary<string, object>
{
{ "op", "replace"},
{"path", "price"},
{"value", 6}
}
};
Ideally, in c# you would create a class to represent this DTO (Data Transfer Object), it can work with anonymous types or with dictionaries (a Dictionary<string,object> serializes into a single JSON object) but the code gets harder to manage over time.
public class DTO
{
public string op { get; set; }
public string path { get; set; }
public object value { get; set; }
}
...
var model = new List<DTO>
{
new DTO {
op = "replace",
path = "price",
value = 6
}
};

Creating a JSON object in ASP.Net

I am trying to create a JSON object in a .Net 5 application. The default options I am presented with are Microsoft.AspNet.Mvc.Formatters.Json, Microsoft.Extensions.Configuration.Json, and Newtonsoft.Json when I use the Visual Studio 2015 Qucik Actions on Json. My understanding is that Configuration.Json is for reading form the appsettings.json so it probably is not what I would use to create a JSON object. I can't find any real information on Formatters.Json, how to use it, or what it's intended use it. Newtonsoft.Json is will documented but is it better over the Formatters.Json? Which of the two should I be using?
Taken directly from ASP.NET Core 1 tests
var expected = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { foo = "abcd" }));
Also taken from the tests and slightly modified, call it with HttpClient to see how to send your json string to the server.
var response = await Client.PostAsync(
"http://localhost/api/ActionUsingSpecificFormatters",
new StringContent(yourJsonContent, Encoding.UTF8, "application/json"));
As per Newtonsoft you can simply encode, then do whatever you want after that.
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonConvert.SerializeObject(product);
//{
// "Name": "Apple",
// "ExpiryDate": "2008-12-28T00:00:00",
// "Price": 3.99,
// "Sizes": [
// "Small",
// "Medium",
// "Large"
// ]
//}
Product deserializedProduct = JsonConvert.DeserializeObject(output);
Putting it all together - I just tested this. Keep in mind this is a real generic pass through test from MVC 6 (ASP.NET 5 ie ASP.NET Core 1) :)
[HttpGet]
public async Task<string> Get()
{
var client = new HttpClient();
var customer = new Customer() { Name = "Schmo", Address = "1999 Purple Rain St" };
var customerJson = JsonConvert.SerializeObject(customer);
var response = await client.PostAsync(
"http://localhost:4815/api/Customer",
new StringContent(customerJson, Encoding.UTF8, "application/json"));
//just some template output to test which I'm getting back.
string resultJson = "{ 'Name':'adam'}";
if (response.StatusCode == HttpStatusCode.OK)
{
resultJson = await response.Content.ReadAsStringAsync();
var updatedCustomer = JsonConvert.DeserializeObject(resultJson);
}
return resultJson;
}
public class Customer
{
public string Name { get; set; }
public string Address { get; set; }
}
I would use Json.Net to create the JSON payloads for sure (Afterall, Microsoft does for Web Api).
Nuget Package Source:
Install-Package Newtonsoft.Json
Here is an example. If you want to call a REST api that returns a product when you make a GET call then you might do something like this.
public static class Main
{
string url = "https://TheDomainYouWantToContact.com/products/1";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Method = "GET";
request.ContentType = "application/json";
request.Accept = "application/json";
var httpResponse = (HttpWebResponse)request.GetResponse();
var dataStream = httpResponse.GetResponseStream();
var reader = new StreamReader(dataStream);
var responseFromServer = reader.ReadToEnd();
reader.Close();
dataStream.Close();
// This is the code that turns the JSON string into the Product object.
Product productFromServer = JsonConvert.Deserialize<Product>(responseFromServer);
Console.Writeline(productFromServer.Id);
}
// This is the class that represents the JSON that you want to post to the service.
public class Product
{
public string Id { get; set; }
public decimal Cost { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
The exact same method can be used for POST and PUT as well.
You can use 3rd party assemblies to make this super easy too. We are the authors of DynamicApis.Api
Install-Package DynamicApis.Api
The code to make the same request using this client would be:
public static class Main
{
RestClient client = new RestClient();
string url = "https://YourDomain.com/products/1";
var productFromServer = client.Get<Product>(url);
Console.Writeline(productFromServer.Id);
}
you should use NewtonSoft.Json if you need to serialize objects as json
You shouldn't have to do anything special to send back Json data. The default output formatter is already Json, and if your Startup.cs file is somewhat normal, you should have a line similar to this:
services.AddMvc();
By default, this already contains the Json formatter, and your controller should autonegotiate the return type based on what the browser asked. So a controller like the following should work (taken from this Github issue, which contains some information on why/how this work):
public class ValuesController : ApiController
{
public SomeClass Get()
{
return new SomeClass();
}
public SomeClass Post([FromBody] SomeClass x)
{
return x;
}
}

Categories