Call a Web API From a .NET Client (C#)
by Mike Wasson and Rick Anderson
This tutorial shows how to call a web API from a .NET application, using System.Net.Http.HttpClient.
In this tutorial, a client app is written that consumes the following web API:
Action | HTTP method | Relative URI |
---|---|---|
Get a product by ID | GET | /api/products/id |
Create a new product | POST | /api/products |
Update a product | PUT | /api/products/id |
Delete a product | DELETE | /api/products/id |
To learn how to implement this API with ASP.NET Web API, see (xref:)Creating a Web API that Supports CRUD Operations.
For simplicity, the client application in this tutorial is a Windows console application. HttpClient is also supported for Windows Phone and Windows Store apps. For more information, see Writing Web API Client Code for Multiple Platforms Using Portable Libraries
## Create the Console Application
In Visual Studio, create a new Windows console app named HttpClientSample and paste in the following code:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
The preceding code is the complete client app.
RunAsync
runs and blocks until it completes. Most HttpClient methods are async, because they perform network I/O. All of the async tasks are done inside RunAsync
. Normally an app doesn???t block the main thread, but this app doesn???t allow any interaction.
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
## Install the Web API Client Libraries
Use NuGet Package Manager to install the Web API Client Libraries package.
From the Tools menu, select NuGet Package Manager > Package Manager Console. In the Package Manager Console (PMC), type the following command:
Install-Package Microsoft.AspNet.WebApi.Client
The preceding command adds the following NuGet packages to the project:
- Microsoft.AspNet.WebApi.Client
- Newtonsoft.Json
Json.NET is a popular high-performance JSON framework for .NET.
Examine the Product
class:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
This class matches the data model used by the web API. An app can use HttpClient to read a Product
instance from an HTTP response. The app doesn???t have to write any deserialization code.
## Create and Initialize HttpClient
Examine the static HttpClient property:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
HttpClient is intended to be instantiated once and reused throughout the life of an application. The following conditions can result in SocketException errors:
- Creating a new HttpClient instance per request.
- Server under heavy load.
Creating a new HttpClient instance per request can exhaust the available sockets.
The following code initializes the HttpClient instance:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
The preceding code:
- Sets the base URI for HTTP requests. Change the port number to the port used in the server app. The app won???t work unless port for the server app is used.
- Sets the Accept header to ???application/json???. Setting this header tells the server to send data in JSON format.
## Send a GET request to retrieve a resource
The following code sends a GET request for a product:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
The GetAsync method sends the HTTP GET request. When the method completes, it returns an HttpResponseMessage that contains the HTTP response. If the status code in the response is a success code, the response body contains the JSON representation of a product. Call ReadAsAsync to deserialize the JSON payload to a Product
instance. The ReadAsAsync method is asynchronous because the response body can be arbitrarily large.
HttpClient does not throw an exception when the HTTP response contains an error code. Instead, the IsSuccessStatusCode property is false if the status is an error code. If you prefer to treat HTTP error codes as exceptions, call HttpResponseMessage.EnsureSuccessStatusCode on the response object. EnsureSuccessStatusCode
throws an exception if the status code falls outside the range 200???299. Note that HttpClient can throw exceptions for other reasons ??? for example, if the request times out.
### Media-Type Formatters to Deserialize
When ReadAsAsync is called with no parameters, it uses a default set of media formatters to read the response body. The default formatters support JSON, XML, and Form-url-encoded data.
Instead of using the default formatters, you can provide a list of formatters to the ReadAsAsync method. Using a a list of formatters is useful if you have a custom media-type formatter:
var formatters = new List<MediaTypeFormatter>() {
new MyCustomFormatter(),
new JsonMediaTypeFormatter(),
new XmlMediaTypeFormatter()
};
resp.Content.ReadAsAsync<IEnumerable<Product>>(formatters);
For more information, see Media Formatters in ASP.NET Web API 2
Sending a POST Request to Create a Resource
The following code sends a POST request that contains a Product
instance in JSON format:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
The PostAsJsonAsync method:
- Serializes an object to JSON.
- Sends the JSON payload in a POST request.
If the request succeeds:
- It should return a 201 (Created) response.
- The response should include the URL of the created resources in the Location header.
## Sending a PUT Request to Update a Resource
The following code sends a PUT request to update a product:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
The PutAsJsonAsync method works like PostAsJsonAsync, except that it sends a PUT request instead of POST.
## Sending a DELETE Request to Delete a Resource
The following code sends a DELETE request to delete a product:
[!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
Like GET, a DELETE request does not have a request body. You don???t need to specify JSON or XML format with DELETE.
Test the sample
To test the client app:
- Download and run the server app. Download instructions. Verify the server app is working. For exaxmple,
http://localhost:64195/api/products
should return a list of products. Set the base URI for HTTP requests. Change the port number to the port used in the server app. [!code-csharpMain]
1: #region snippet_all
2: using System;
3: using System.Net;
4: using System.Net.Http;
5: using System.Net.Http.Headers;
6: using System.Threading.Tasks;
7:
8: namespace HttpClientSample
9: {
10: #region snippet_prod
11: public class Product
12: {
13: public string Id { get; set; }
14: public string Name { get; set; }
15: public decimal Price { get; set; }
16: public string Category { get; set; }
17: }
18: #endregion
19:
20: class Program
21: {
22: #region snippet_HttpClient
23: static HttpClient client = new HttpClient();
24: #endregion
25:
26: static void ShowProduct(Product product)
27: {
28: Console.WriteLine($"Name: {product.Name}\tPrice: " +
29: $"{product.Price}\tCategory: {product.Category}");
30: }
31:
32: #region snippet_CreateProductAsync
33: static async Task<Uri> CreateProductAsync(Product product)
34: {
35: HttpResponseMessage response = await client.PostAsJsonAsync(
36: "api/products", product);
37: response.EnsureSuccessStatusCode();
38:
39: // return URI of the created resource.
40: return response.Headers.Location;
41: }
42: #endregion
43:
44: #region snippet_GetProductAsync
45: static async Task<Product> GetProductAsync(string path)
46: {
47: Product product = null;
48: HttpResponseMessage response = await client.GetAsync(path);
49: if (response.IsSuccessStatusCode)
50: {
51: product = await response.Content.ReadAsAsync<Product>();
52: }
53: return product;
54: }
55: #endregion
56:
57: #region snippet_UpdateProductAsync
58: static async Task<Product> UpdateProductAsync(Product product)
59: {
60: HttpResponseMessage response = await client.PutAsJsonAsync(
61: $"api/products/{product.Id}", product);
62: response.EnsureSuccessStatusCode();
63:
64: // Deserialize the updated product from the response body.
65: product = await response.Content.ReadAsAsync<Product>();
66: return product;
67: }
68: #endregion
69:
70: #region snippet_DeleteProductAsync
71: static async Task<HttpStatusCode> DeleteProductAsync(string id)
72: {
73: HttpResponseMessage response = await client.DeleteAsync(
74: $"api/products/{id}");
75: return response.StatusCode;
76: }
77: #endregion
78:
79: static void Main()
80: {
81: RunAsync().GetAwaiter().GetResult();
82: }
83:
84: #region snippet_run
85: #region snippet5
86: static async Task RunAsync()
87: {
88: // Update port # in the following line.
89: client.BaseAddress = new Uri("http://localhost:64195/");
90: client.DefaultRequestHeaders.Accept.Clear();
91: client.DefaultRequestHeaders.Accept.Add(
92: new MediaTypeWithQualityHeaderValue("application/json"));
93: #endregion
94:
95: try
96: {
97: // Create a new product
98: Product product = new Product
99: {
100: Name = "Gizmo",
101: Price = 100,
102: Category = "Widgets"
103: };
104:
105: var url = await CreateProductAsync(product);
106: Console.WriteLine($"Created at {url}");
107:
108: // Get the product
109: product = await GetProductAsync(url.PathAndQuery);
110: ShowProduct(product);
111:
112: // Update the product
113: Console.WriteLine("Updating price...");
114: product.Price = 80;
115: await UpdateProductAsync(product);
116:
117: // Get the updated product
118: product = await GetProductAsync(url.PathAndQuery);
119: ShowProduct(product);
120:
121: // Delete the product
122: var statusCode = await DeleteProductAsync(product.Id);
123: Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
124:
125: }
126: catch (Exception e)
127: {
128: Console.WriteLine(e.Message);
129: }
130:
131: Console.ReadLine();
132: }
133: #endregion
134: }
135: }
136: #endregion
Run the client app. The following output is produced:
Created at http://localhost:64195/api/products/4
Name: Gizmo Price: 100.0 Category: Widgets
Updating price...
Name: Gizmo Price: 80.0 Category: Widgets
Deleted (HTTP Status = 204)
|