Search code examples
c#.netconsole-applicationwebapi

Consume a Web API using a .Net Client App


I have been following some tutorials (e.g., https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client) and trying to retrieve a list of jobs and print them out in my .Net console app.

The test data I am using is located at https://boards-api.greenhouse.io/v1/boards/vaulttec/jobs and supplied it to client.BaseAddress.

Since I am able to compile and run the tutorial successfully, I simply used the same code and changed some of it to run the above test data (see below).

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
 
namespace GreenhouseJobs
{
    public class Job
    {
        public string Id { get; set; }
        public string Title { get; set; }
        public string Location { get; set; }
        public DateTime LastUpdated { get; set; }
    }
 
    class GreenhouseJobsClient
    {
        static HttpClient client = new HttpClient();
 
        static void ShowJob(Job job)
        {
            Console.WriteLine($"Id: {job.Id}\tTitle: " +
                $"{job.Title}\tLocation: {job.Location}\tLast Updated: {job.LastUpdated}");
        }
 
        static async Task<Uri> CreateJobAsync(Job job)
        {
            HttpResponseMessage response = await client.PostAsJsonAsync(
                "vaulttec/jobs", job);
            response.EnsureSuccessStatusCode();
 
            // return URI of the created resource.
            return response.Headers.Location;
        }
 
        static async Task<Job> GetJobAsync(string path)
        {
            Job job = null;
            HttpResponseMessage response = await client.GetAsync(path);
            if (response.IsSuccessStatusCode)
            {
                job = await response.Content.ReadAsAsync<Job>();
            }
            return job;
        }
 
        //static async Task<Product> UpdateProductAsync(Product product)
        //{
        //    HttpResponseMessage response = await client.PutAsJsonAsync(
        //        $"api/products/{product.Id}", product);
        //    response.EnsureSuccessStatusCode();
 
        //    // Deserialize the updated product from the response body.
        //    product = await response.Content.ReadAsAsync<Product>();
        //    return product;
        //}
 
        //static async Task<HttpStatusCode> DeleteProductAsync(string id)
        //{
        //    HttpResponseMessage response = await client.DeleteAsync(
        //        $"api/products/{id}");
        //    return response.StatusCode;
        //}
 
        static void Main()
        {
            RunAsync().GetAwaiter().GetResult();
            Console.ReadLine();
        }
 
        static async Task RunAsync()
        {
            // Update port # in the following line.
            client.BaseAddress = new Uri("https://boards-api.greenhouse.io/v1/boards/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
 
            try
            {
                // Create a new product
                Job job = new Job
                {
                    Id = "323232",
                    Title = "Test",
                    Location = "Test",
                    LastUpdated = DateTime.Now
                };
 
                var url = await CreateJobAsync(job);
                Console.WriteLine($"Created at {url}");
 
                // Get the product
                job = await GetJobAsync(url.PathAndQuery);
                ShowJob(job);
 
                // Update the product
                //Console.WriteLine("Updating price...");
                //product.Price = 80;
                //await UpdateProductAsync(product);
 
                // Get the updated product
                //product = await GetProductAsync(url.PathAndQuery);
                //ShowProduct(product);
 
                // Delete the product
                //var statusCode = await DeleteProductAsync(product.Id);
                //Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
 
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
 
            Console.ReadLine();
        }
    }
}

The problem is, when I run the app, it does not return anything. Error message:

"Response status code does not indicate success: 404 (Not Found)".


Solution

  • There are couple issues here

    1. You are not just trying to retrieve but also create first. I am not sure if this api supports a POST or not, it probably doesn't, hence a 404
    2. The Job entity is incorrect. Location needs to be an object itself and not a mere string if you need it deserialized correctly. Here is a working version of get call

    https://pastebin.com/85NnAQY9

    namespace ConsoleApp3
    {
    
        public class JobsJson
        {
            public List<Job> Jobs { get; set; }
        }
        public class Job
        {
            public string Id { get; set; }
            public string Title { get; set; }
            public Location Location { get; set; }
            public DateTime LastUpdated { get; set; }
        }
    
        public class Location
        {
            public string Name { get; set; }
        }
    
        class GreenhouseJobsClient
        {
            static HttpClient client = new HttpClient();
    
            static void ShowJobs(List<Job> jobs)
            {
                foreach (var job in jobs)
                {
                    Console.WriteLine($"Id: {job.Id}\tTitle: " +
                                      $"{job.Title}\tLocation: {job.Location}\tLast Updated: {job.LastUpdated}");
                }
            }
    
    
            static async Task<List<Job>> GetJobAsync(string path)
            {
                var jobs = new List<Job>();
                HttpResponseMessage response = await client.GetAsync(path);
    
                if (response.IsSuccessStatusCode)
                {
                    var stringResponse = await response.Content.ReadAsStringAsync();
                    var re = JsonConvert.DeserializeObject<JobsJson>(stringResponse);
                    jobs = re.Jobs;
                }
                return jobs;
            }
    
            static void Main()
            {
                RunAsync().GetAwaiter().GetResult();
                Console.ReadLine();
            }
    
            static async Task RunAsync()
            {
                // Update port # in the following line.
                client.BaseAddress = new Uri("https://boards-api.greenhouse.io/v1/boards/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
                try
                {
                    // Get the product
                    var jobs = await GetJobAsync("vaulttec/jobs");
                    ShowJobs(jobs);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
    
                Console.ReadLine();
            }
        }
    }