3

Currently I am doing an API call via jQuery, my question is, is there away to do this call in C# or away to convert the results of an API call to an ASP.NET List of Model Objects?

Here is my Model

public class TeamStatsClass
{
        public int id { get; set; }
        public string name { get; set; }
        public string league { get; set; }
        public string division { get; set; }
}

And here is my current ajax call

$.ajax({
        url: "https://statsapi.web.nhl.com/api/v1/teams?sportId=1",
        success: function (data) {

            for (var team of data.teams) {

                console.log(team.name);

            }

        }
    });

UPDATE

I changed my classes to look like so:

public class StatsTeamsClass
    {
        public IEnumerable<Teams> teams { get; set; }
        public string copyright { get; set; }
    }
    public class Division
    {
        public int id { get; set; }
        public string name { get; set; }
        public string link { get; set; }
    }

    public class Teams
    {
        public int id { get; set; }
        public string name { get; set; }
        public string link { get; set; }
        public League league { get; set; }
        public Division division { get; set; }
    }

and created this method which indeeds puts the results in model object:

public async System.Threading.Tasks.Task<StatsTeamsClass> GetTeams()
{

            HttpClient Http = new HttpClient();
            var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
            StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);

            return teams;
}

But when I try to call this method in another controller, it just hangs there, no error, no nothing, I am assuming it will just time out after a while

public class HomeController : Controller
{

        APIController webService = new APIController();

        public ActionResult Index()
        {

            var item = webService.GetTeams().Result.teams;

            return View();
        }
}

(GetTeams() is inside the controller APIController)

So what would be the proper way to A. get the results of an API in object model and then call those results?

user979331
  • 11,039
  • 73
  • 223
  • 418
  • You can easily consume web api using HttpClient class https://stackoverflow.com/questions/26571451/how-to-consume-a-webapi-from-asp-net – Wamiq Rehman Aug 27 '19 at 20:27

3 Answers3

7

The controller action needs to be made async as well to avoid mixing async-await and blocking calls like .Result or .Wait() that could potentially cause deadlocks.

Reference Async/Await - Best Practices in Asynchronous Programming

public class HomeController : Controller {    
    APIController webService = new APIController();

    public async Task<ActionResult> Index() {    
        var model = await webService.GetTeams();
        var teams = model.teams;
        return View();
    }
}

Assuming APIController is an actual ApiContoller

public class APIController : ApiController {

    //Your original code
    public async Task<StatsTeamsClass> GetTeams() {        
        HttpClient Http = new HttpClient();
        var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
        StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);
        return teams;
    }

    //...
}

I would suggest not calling APIController directly like that from the HomeController and instead extract the GetTeams() method out into a reusable service

public class WebService {
    static Lazy<HttpClient> http = new Lazy<HttpClient>();

    public async Task<T> GetAsync<T>(string url) {        
        var json = await http.Value.GetStringAsync(url);
        return JsonConvert.DeserializeObject<T>(json);
    }

    public Task<StatsTeamsClass> GetTeamsAsync() {        
        var url = "https://statsapi.web.nhl.com/api/v1/teams?sportId=1";
        return GetAsync<StatsTeamsClass>(url);
    }
}

Reference You're using HttpClient wrong

that can be properly used in HomeController

public class HomeController : Controller {        
    public async Task<ActionResult> Index() {
        // Ideally web service should be injected but that topic
        // is outside of the scope of the question at the moment.
        var webService = new WebService();

        var model = await webService.GetTeamsAsync();
        var teams = model.teams;

        //...

        return View(teams);
    }
}

The assumption here is that the project is a mixed Asp.Net MVC and Web Api 2+

Index.cshtml

@model IEnumerable<Teams>
@{
    ViewBag.Title = "Teams";
}
@if(Model != null && Model.Count() > 0) {
    @foreach (var @team in Model) {
        <p>@team.name</p>
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • @Nkosi could you toss a reference to this "latest update" you mention at the beginning, forcing people to use async in cases like these, to ensure proper execution of the code? – SpiritBob Sep 04 '19 at 16:40
  • @SpiritBob I was referring to the latest update to their question/post. They did not initially include the relevant information about the controller action being async. Check the [revision history](https://stackoverflow.com/posts/57681576/revisions) – Nkosi Sep 04 '19 at 16:45
  • @SpiritBob I've now edited that statement out to avoid any further confusion – Nkosi Sep 04 '19 at 17:06
3

Yes, the equivalent in C# would be to use HttpClient. You're best off creating a static instance of the class that you reuse for a particular kind of repeated call:

private static readonly HttpClient Http = new HttpClient(); 

and then used it from an async method using Newtonsoft.Json like this:

var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");

You can then parse this string of JSON into a model class like this:

var model = JsonConvert.DeserializeObject<TeamStatsClass>(json);
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
2

As the question is answered by @Daniel above just want to add couple of more points here The json you are getting cannot be directly casted to TeamStatsClass you might have to introduce another base class as teams is the collection in the json you are getting.

Im posting it here to get a clearer view

    public class ResponseBaseClass
    {
        public IEnumerable<TeamStatsClass> teams { get; set; }
        public string copyright { get; set; }
    }
    public class TeamStatsClass
    {
        public int id { get; set; }
        public string name { get; set; }
        public Division division { get; set; }
    }
    public class Division
    {
        public int id { get; set; }
        public string name { get; set; }
        public string nameShort { get; set; }
        public string link { get; set; }

    }

 HttpClient Http = new HttpClient();
 var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
 var model = JsonConvert.DeserializeObject<ResponseBaseClass>(json);
 var yourTeamModelObj = model.teams;
Wamiq Rehman
  • 566
  • 3
  • 11
  • @user979331 HttpClient is basically designed for async processes you have to use WebClient class for synchronous work `WebClient webpage = new WebClient(); string json = webpage.DownloadString("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");` – Wamiq Rehman Aug 30 '19 at 14:15
  • How would I call this method in another controller? – user979331 Aug 30 '19 at 21:08
  • Something simple would be to Create a new class say Teams and Write this above code as a static function in a new class with a return type of ResponseBaseClass and call this on every controller. – Wamiq Rehman Aug 30 '19 at 21:25
  • @user979331 The only thing i noticed wrong is the controller method index should also be async .Because for aysn/await architecture all the preceding and succeeding methods should be async otherwise it may cause deadlock .There are alot of ways that this code can be written perfectly it depends on your application scope and architecture .but i think for the basics what your are doing is not bad. – Wamiq Rehman Sep 01 '19 at 18:39