I followed a tutorial (https://www.youtube.com/watch?v=aWePkE2ReGw) to try and call APIs using C#, and while it worked, my attempts to modify it for my own projects failed miserably. Despite my best efforts and trying various different links on the same website, they all return failure.
I was able to access it with python no problem, so the problem seems confined to C#. I have the microsoft.aspnet.webapi.client installed, as well as json. It compiles and runs, just returns failure.
https://dbd.tricky.lol/apidocs/
Here's the code:
namespace API_Test
{
// class used to initialize the httpclient
public class ApiHelper
{
public static HttpClient? ApiClient { get; set; }
public static void InitializeClient()
{
ApiClient = new HttpClient();
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
}
// used to hold the result
class ShrineModel
{
public double ID { get; set; }
}
// used for processing results
class ShrineResultModel
{
public ShrineModel? Results { get; set; }
}
// used to access the api
class ShrineProcessor
{
public static async Task<ShrineModel> LoadShrineInformation()
{
string url = "https://dbd.tricky.lol/api/shrine";
using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
ShrineResultModel result = await response.Content.ReadAsAsync<ShrineResultModel>();
return result.Results;
}
else
{
// ALWAYS ENDS UP HERE
Console.WriteLine("Failure");
Console.ReadKey();
throw new Exception(response.ReasonPhrase);
}
}
}
}
// main
public class API_Test
{
static async Task Main()
{
ApiHelper.InitializeClient();
var ShrineInfo = await ShrineProcessor.LoadShrineInformation();
Console.WriteLine(ShrineInfo.ID);
}
}
}
Best case scenario I get a json output with all the information found at the link. What I'm getting is a false response from "IsSuccessStatusCode"
I've tried various other endpoints available from the same website. Other APIs have worked. But I can't get this one to.
I ran your code against the API and there are a couple of issues to fix.
1. 403 Forbidden
You got response.IsSuccessStatusCode false because the API endpoint requires a User-Agent header. This is to block any web crawler bots.
To fix that you just need to add this code,
ApiClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0");
2. Wrong JSON data model
You must model the JSON response structure so the deserialization can work properly.
The JSON response:
{
"id": 666,
"perks": [
{
"id": "Aftercare",
"bloodpoints": 100000,
"shards": 2000
},
{
"id": "BeastOfPrey",
"bloodpoints": 100000,
"shards": 2000
},
{
"id": "DeadHard",
"bloodpoints": 100000,
"shards": 2000
},
{
"id": "Nemesis",
"bloodpoints": 100000,
"shards": 2000
}
],
"start": 1672185600,
"end": 1672790399
}
So your ShrineResultModel should be like this:
public class Perk
{
public string id { get; set; }
public int bloodpoints { get; set; }
public int shards { get; set; }
}
public class ShrineResultModel
{
public int id { get; set; }
public List<Perk> perks { get; set; }
public int start { get; set; }
public int end { get; set; }
}
Update your code to deserialize the JSON response:
if (response.IsSuccessStatusCode)
{
ShrineResultModel result = await response.Content.ReadFromJsonAsync<ShrineResultModel>();
return result;
}
The actual result:
Related
In my ASP.NET Core-6 Web API, I am given a third party API to consume and then return the account details. I am using WebClient.
api:
https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber=112123412
Headers:
X-GivenID:Given2211
X-GivenName:Givenyou
X-GivenPassword:Given#llcool
Then JSON Result is shown below:
{
"AccountName": "string",
"CurrentBalance": 0,
"AvailableBalance": 0,
"Currency": "string"
}
So far, I have done this:
BalanceEnquiryResponse:
public class BalanceEnquiryResponse
{
public string Response
{
get;
set;
}
public bool IsSuccessful
{
get;
set;
}
public List<BalanceList> AccountBalances
{
get;
set;
}
}
BalanceList:
public class BalanceList
{
public string AccountNumber
{
get;
set;
}
public decimal CurrentBalance
{
get;
set;
}
public decimal AvailableBalance
{
get;
set;
}
public string Currency
{
get;
set;
}
}
Then the service is shown below.
IDataService:
public interface IDataService
{
BalanceEnquiryResponse GetAccountBalance(string accountNo);
}
public class DataService : IDataService
{
private readonly ILogger<DataService> _logger;
private readonly HttpClient _myClient;
public DataService(ILogger<DataService> logger, HttpClient myClient)
{
_logger = logger;
_myClient = myClient;
PrepareAPIHeaders(); // Actually apply the headers!
}
private void PrepareAPIHeaders()
{
_myClient.DefaultRequestHeaders.Add("X-GivenID", "Given2211");
_myClient.DefaultRequestHeaders.Add("X-GivenName", "Givenyou");
_myClient.DefaultRequestHeaders.Add("X-GivenPassword", "Given#llcool");
_myClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
_myClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/json; charset=utf-8");
}
// If you want to use async API, you need to go async all the way.
// So make this Method async, too!
public async Task<BalanceEnquiryResponse> GetAccountBalance(string accountNo)
{
_logger.LogInformation("Accessing Own Account");
var url = $"https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber={accountNo}";
var response = await _myClient.GetAsync(url);
// vv Get your payload out of the Http Response.
var responseResults = await response.Content.ReadAsAsync<BalanceEnquiryResponse>();
return responseResults;
}
}
I tested the third party api with the headers on POOSTMAN:
https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber=112123412
and it gives me expected result. But from my code, when I tried to call GetAccountBalance from the code below and I supplied model.account_number:
public async Task<BaseResponse> FinalResult(RequestDto model)
{
var response = new BaseResponse();
try
{
//Check account Balance
var accBalance = _dataAccess.GetAccountBalance(model.account_number);
if (!accBalance.IsSuccessful)
{
response.response_code = "";
response.response_description = "Could not fetch account for subscriber";
return response;
}
}
}
I got this error in:
response.response_description = "Could not fetch account for subscriber";
What am I doing wrongly, especially in public class DataService and how do I resolve it?
Thanks.
It might just be that we talking about a GetCall but sometimes i had problems with headers which are set directly on the client layer. So i would try to set them on the request, instead of the client.
I wrote this without a editor so i could not test it.
But you should get the gist of what im trying to do
public async Task<BalanceEnquiryResponse> GetAccountBalance(string accountNo)
{
_logger.LogInformation("Accessing Own Account");
var url = $"https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber={accountNo}";
using(var request = new HttpRequestMessage(HttpMethod.Get, url))
{
//add headers to the request not the client
PrepareAPIHeaders(request);
var result = await _myClient.SendAsync(request);
result.EnsureSuccessStatusCode();
/*Read response and parse it into an object and return*/
}
return null;
}
private void PrepareAPIHeaders(HttpRequestMessage request)
{
request.Headers.Add("X-GivenID", "Given2211");
/*Add other Headers*/
}
I'm writing an ASP.NET Core API, which should follow an existing format.
The response should always start with a data element in the json
{
"data": {
"batch_id": null,
"created_at": "2019-01-29T16:58:04+00:00",
"current_operation": null,
"description": "This watch has a new name",
"model": {
"brand_id": null,
"created_at": null,
...
}
}
}
I created a class Batch which contains all the properties that we see, and my controller is taking care of serialize it in the background.
public class Batch
{
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
[HttpGet("{id}")]
public ActionResult<Batch> Get(string id)
{
...
return batch;
}
Is there a way to write something so that every payload has to start with a data property?
I would like to avoid writing a class DataWrapper<T> that I have to use for every response.
You could just use an anonymous object to wrap the response from the controller:
[HttpGet("{id}")]
public ActionResult<Batch> Get(string id)
{
...
return Ok(new { data = batch });
}
You can use GenericClass
public class GenericResponseClass<T>{
public T data;
}
then you can pass your batch(or any other) class to GenericResposneClass just like
public class Batch
{
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
}
GenericResponseClass<Batch> response = new GenericResponseClass<Batch>();
Batch batch = new Batch();
batch.Label = "New Label";
batch.SerialNumber = "Serial Number";
response.data = batch;
then pass/serialize your response
I'm trying to collect data from a client through an API. Eventually sending individual parts of the data that I collected to another project.
So for example I'm collecting an entire data object with id,Name,Status, etc. And I only want the id variable with it's value to be send to another project.
Everything works fine except from the sending part. I still had to paste all related classes to make it easier to understand.
I'm trying to send a list of objects. The problem is that I don't know how to send it the right way. You can see what I tried below. I'm creating my own object named WatchdogDTO which contains only the values that I want to send. Eventually I serialize it to send it to http://localhost:53661/api/Values. Which is another project I use to test if it works. Now it just literally sends the name of the list instead of the actual WatchdogDTO.
This is the data I'm collecting:
[
{
"id": "200",
"name": "Kerno (Camera)",
"hostName": "Amsterdam",
"status": false,
"hasCamera": true,
"cameraStatus": "0",
"lastSeen": "34324"
},
{
"id": "202",
"name": "Bassy (Location)",
"hostName": "Limburg",
"status": true,
"hasCamera": false,
"cameraStatus": "-1",
"lastSeen": "2344"
}
]
I created a corresponding model:
public class ServerStateDto
{
public string id { get; set; }
public string Name { get; set; }
public string HostName { get; set; }
public bool Status { get; set; }
public bool hasCamera { get; set; }
public string CameraStatus { get; set; }
public DateTime LastSeen { get; set; }
}
In the class ApiManager I collect the data above and deserialize it:
class ApiManager
{
HttpClient client = new HttpClient();
public List<ServerStateDto> CollectWatchdogData()
{
client.BaseAddress = new Uri("https://classified.Data.net:4802");
HttpResponseMessage response = client.GetAsync("/api/pdf/process/getalldata").Result;
if (response.IsSuccessStatusCode)
{
string dto = response.Content.ReadAsStringAsync().Result;
List<ServerStateDto> model = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ServerStateDto>>(dto);
return model;
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return null;
}
}
}
This is just a general class that calls the functions CollectWatchdogData(); and PostWatchdogData();
{
public bool Start(HostControl hostControl)
{
ApiManager x = new ApiManager();
List<ServerStateDto> returnedData = x.CollectWatchdogData();
if (returnedData == null)
{
return false;
}
WatchdogApiClient cli = new WatchdogApiClient();
cli.PostWatchdogData(returnedData);
return true;
}
}
This is a model for the object that I'm sending. I only want to send Id from the model above:
public class WatchdogDTO
{
public string id { get; set; }
}
This class actually sends the WatchdogDTO from above to localhost:53661 which is another visual studio project of my which I use to test if the sending works.
public class WatchdogApiClient
{
private static readonly HttpClient client = new HttpClient();
//public async void PostWatchdogData(ServerStateDto toSend)
public async void PostWatchdogData(List<ServerStateDto> toSend)
{
//to actually send the data
WatchdogDTO toActuallySend = new WatchdogDTO()
{
id = toSend.ToString()
};
//Serialize data to bytestream so it can be sent
string serializedWatchDogData = JsonConvert.SerializeObject(toActuallySend);
if (string.IsNullOrEmpty(serializedWatchDogData))
{
return;
try
{
var response = await client.PostAsync("http://localhost:53661/api/Values", new StringContent(serializedWatchDogData, Encoding.UTF8, "application/json"));
}
catch (Exception ex)
{
Console.WriteLine($"Encountered an exception during PostAsync in method PostWatchDogData: {ex.Message}");
throw ex;
}
}
}
Eventually the destination of the data has a class and a model aswell to receive the data sent
model:
public class WatchdogDTO
{
public string id { get; set; }
}
This is the sending part, the sending works but the problem is that the value of WatchdogDTO data is the name of the list instead of the actual list with its values.
Class to receive the data send:
public class ValuesController : ApiController
{
[HttpPost]
public void PostWatchdogData([FromBody] WatchdogDTO data)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.BroadcastMessage("Server", "b");
}
}
How can I send individual parts like for example only the id, or the host name, or the id AND the hostname.
Thanks in advance and sorry for the bunch of code.
I need to get GET response from REST API. I use RestSharp. The problem is, that one name of the response attribute is "$". This is the response:
[
{
"CodeId": {
"$": "00000000"
},
"Entity": {
"LegalName": {
"#xml:lang": "cs",
"$": "xxxxx"
}
}
}
]
How should I use the RestSharp to get the value of Entity.LegalName.$ ?
I found the answer thanks by #fredrik.
var client = new RestClient(url);
var request = new RestRequest(urlRequest, DataFormat.Json);
var response = client.Get(request);
Console.WriteLine(JsonSerializer.Deserialize<List<TestRestResponseTemplate>>(response.Content)[0].Entity.LegalName.Value);
TestRestResponseTemplate:
public class TestRestResponseTemplate
{
public Entity Entity { get; set; }
}
public class LegalName
{
[JsonPropertyName("#xml:lang")]
public string Language { get; set; }
[JsonPropertyName("$")]
public string Value { get; set; }
}
public class Entity
{
public LegalName LegalName { get; set; }
}
I am connecting to an API, but not having any luck retrieving the data in Json. I have different endpoints to use, but cant seem the any to work. I believe /products should give me the entire list but am having no luck.
class Program
{
static void Main(string[] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
using (var client = new HttpClient())
{
//go get the data
string token = "auth token";
client.BaseAddress = new Uri("");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Console.WriteLine("GET");
HttpResponseMessage response = await client.GetAsync("/products/6");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Connected");
RootObject product = await response.Content.ReadAsAsync<RootObject>();
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", product.data.id,product.data.name,product.data.sort,product.data.designation_id,product.data.designation_id);
}
else
{
Console.WriteLine("Failed");
Console.WriteLine(response.Headers);
}
}
}
}
The response I am getting in the console on this is:
0
1/1/0001 12:00:00 AM
1/1/0001 12:00:00 AM
Product class:
public class RootObject
{
public Product data { get; set; }
}
public class Product
{
public int id { get; set; }
public object designation_id { get; set; }
public string name { get; set; }
public object alternate_name { get; set; }
public object description { get; set; }
public int sort { get; set; }
public string created_at { get; set; }
public string updated_at { get; set; }
}
The response I get when testing connection with postman is as follows:
{
"data": {
"id": 6,
"designation_id": null,
"name": "Multirate Fork Springs Kit",
"alternate_name": null,
"description": null,
"sort": 0,
"created_at": "2016-06-17 20:47:51",
"updated_at": "2018-05-25 09:40:50"
}
}
nuget Newtonsoft.Json
and you can do something like this:
using Newtonsoft.Json;
in client, using response
Product product = JsonConvert.DeserializeObject<Product>(your response string);
this works really well, as long as your product class has all the right attributes you should be good to go!