اخبار، مطالب و رویدادهای مرتبط با توسعه نرم افزار رادکام

فراخوانی Web API داخل کد های برنامه های Dot Net

در این مقاله خواهیم دید که چگونه یک Web API را از داخل برنامه دات نت با استفاده از System.Net.Http.HttpClient فراخوانی کنیم.
در این مثال یک برنامه سمت کلاینت خواهیم نوشت که 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
برای یادگیری نحوه پیاده سازی API اشاره شده با Asp.Net Web API می توانید به مقالات پیشین از این سری از مطالب مراجعه فرمایید.
برای ساده سازی مطالب، برنامه کلاینت در این مثال یک برنامه تحت ویندوز Console است. HttpClient در برنامه های Windows Phone و Windows Store نیز پشتیبانی می شود.

ایجاد برنامه Console

در Visual Studio یک برنامه Windows Console ایجاد کرده و آن را HttpClientSample نامگذاری کرده و کد زیر را جایگزین کد ایجاد شده داخل آن بکنید.

        
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace HttpClientSample
{
    public class Product
     {
	public string Id { get; set; }
	public string Name { get; set; }
	public decimal Price { get; set; }
	public string Category { get; set; }
    }

    class Program
    {
        static HttpClient client = new HttpClient();

        static void ShowProduct(Product product)
        {
			Console.WriteLine($"Name: {product.Name}\tPrice: " + $"{product.Price}\tCategory: {product.Category}");
        }

        static async Task<Uri>CreateProductAsync(Product product)
        {
          
            HttpResponseMessage response = await client.PostAsJsonAsync("api/products", product);

            response.EnsureSuccessStatusCode();

            // return URI of the created resource.
            return response.Headers.Location;
        }

        static async Task<Product>GetProductAsync(string path)
        {
            Product product = null;

            HttpResponseMessage response = await client.GetAsync(path);
            if(response.IsSuccessStatusCode)
            {
	            product = await response.Content.ReadAsAsync<Product>();
            }
            return product;
        }

        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();
        }

        static async Task RunAsync()
        {
            // Update port # in the following line.
            client.BaseAddress = new Uri("http://localhost:64195/");

            client.DefaultRequestHeaders.Accept.Clear();

            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            try
            {
	            // Create a new product
	            Product product = new Product
	            {
		            Name = "Gizmo",
		            Price = 100,
		            Category = "Widgets"
	            };
        
	            var url = await CreateProductAsync(product);

	            Console.WriteLine($"Created at {url}");

	            // Get the product

	            product = await GetProductAsync(url.PathAndQuery);

	            ShowProduct(product);

	            // 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();
        }
    }
}
            
        
کد نوشته شده بالا یک کد کامل سمت کلاینت است.
RunAsync اجرا شده و تا زمانی که اجرای آن کامل شود بلوکه می شود. بسیاری از متد های کلاس HttpClient به همین صورت عمل می کنند.به این دلیل که آنها عملیات I/O درون شبکه انجام می دهند. تمام عملیات آسنکرون داخل RunAsync انجام می گیرند. به صورت طبیعی، یک برنامه thread اصلی را بلوکه نمی کند، اما این برنامه اجازه هیج تعاملی را نخواهد داد.

        
    static async Task RunAsync()                        
    {                                
    	// Update port # in the following line.                                
    	client.BaseAddress = new Uri("http://localhost:64195/");                                
    	client.DefaultRequestHeaders.Accept.Clear();                               
   		client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));                          
    	try
    	{
    		// Create a new product
		   	Product product = new Product                              
    		{
    	   		Name ="Gizmo",
    		  	Price = 100,
	         	Category = "Widgets"
	         };
            var url = await CreateProductAync(product);
            Console.WriteLine($"Created at {url}");
                                                             
            // Get the product
            
            product = await GetProductAsync(url.PathAndQuery);
            ShowProduct(product);
            // 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();     
}
        
        

نصب کتابخانه های لازم برای استفاده از امکانات Web API سمت کلاینت

مثل اکثر موارد، در اینجا نیز برای نصب کتابخانه های مورد نیازمان برای استفاده از امکانات Web API سمت کلاینت ( سرویس گیرنده ) از NuGet Package Manager استفاده می کنیم.
در Visual Studio از منوی Tools زیر منوی NuGet Package Manager ، روی زیر منوی Package Manager Console کلیک کنید. در کنسول (PMC) باز شده، عبارت زیر را وارد کرده و اجرا کنید (کلید اینتر را فشار دهید.)

        
Install-Package Microsoft.AspNet.WebApi.Client
        
        

دستور وارد شده بسته های مورد نیاز زیر را به پروژه ما اضافه خواهد کرد:
  • Microsoft.AspNet.WebApi.Client
  • Newtonsoft.Json
همانطور که اکثر شما می دانید، Json.Net یک فریموورک Json قدرتمند و با کارایی بالا برای Dot Net است.

اضافه کردن کلاس مدل

کلاس مدل محصول خود را اینگونه تعریف می کنیم:

        
public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}
        
        
کلاسی که مشاهده می کنید،منطبق با مدل داده ای است که در Web API استفاده شده است. یک برنامه می تواند از HttpClient برای خواندن اطلاعات یک محصول از طریق پاسخ HTTP استفاده کند. برنامه نیاز ندارد که کدی برای deserialize کردن بنویسد.

ایجاد و مقدار دهی اولیه HttpClient

مشخصه استاتیک HttpClient را در نظر می گیریم:

static HttpClient client = new HttpClient();

HttpClient به این صورت در نظر گرفته می شود که یک بار ایجاد و  مقدار دهی گردیده و در طول حیاط پروژه مکررا مورد استفاده قرار می گیرد. شرایط زیر می توانند منجر به خطای SocketException گردند:
  • ایجاد نمونه های جدید برای هر درخواست
  • بار سنگین برای سرور
ایجاد نمونه جدید از HttpClient برای هر درخواست ممکن است باعث از بین رفتن سوکت های موجود شود.
کد زیر یک نمونه از HttpClient را ایجاد، مقدار دهی اولیه و آماده استفاده می کند

        
static async Task RunAsync()
{
    // Update port # in the following line.        
    client.BaseAddress = new Uri("http://localhost:64195/"); 
    client.DefaultRequestHeaders.Accept.Clear();     
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
		
        
کد نوشته شده فوق:
  • URI پایه را برای درخواست های HTTP ست می کند. شماره پورت را مطابق با شماره پورت استفاده شده در برنامه سمت سرور تنظیم می کند. اگر پورت را منطبق با پورت تنظیم شده سمت سرور قرار ندهیم، برنامه ما کار نخواهد کرد.
  • هدر Accept را برابر با "application/json" قرار می دهد. ست کردن این هدر به سرور می گوید که دیتای ارسالی را با قالب و پیکربندی JSON ارسال کند.


ارسال درخواست GET برای واکشی و دریافت اطلاعات

کد زیر یک درخواست GET برای دریافت اطلاعات یک محصول را ارسال می کند
            
static async Task<Product> GetProductAsync(string path)
{
Product product = null;
HttpResponseMessage response = await client.GetAsync(path); if (response.IsSuccessStatusCode) { product = await response.Content.ReadAsAsync<Product>(); } return product; }
متد GetAsync یک درخواست HTTP GET ارسال می کند. زمانی که کار متد تمام شد، یک شیء از نوع HttpResponseMessage برمی گرداند که شامل پاسخ HTTP است. اگر کد وضعیت پاسخ کد نشان دهنده موفقیت آمیز بودن کار متد باشد، بدنه پاسخ شامل قالب JSON از اطلاعات محصول مورد نظر ما خواهد بود. متد ReadAsAsync را فراخوانی کرده ایم تا قالب JSON را به یک نمونه از شیء محصول تبدیل کنیم. متد ReadAsAsync به صورت آسنکرون عمل می کند، چون بدنه پاسخ ممکن است شامل حجم زیادی از اطلاعات باشد.
زمانی که پاسخ HTTP شامل کد خطا باشد، یعنی نشان دهنده وجود خطا باشد، HttpClient خطا را throw نخواهد کرد. به جای آن مشخصه IsSuccessStatusCode برابر با مقدار false خواهد بود، اگر کد وضعیت یک کد خطا باشد. اگر شما ترجیح می دهید با کد های خطای HTTP مانند exception رفتار کنید، متد HttpResponseMessage.EnsureSuccessStatusCode را در شیء پاسخ فراخوانی کنید. EnsureStatusCode خطای throwخواهد کرد البته هنگامی که کد خطای رخ داده بیرون از بازه 200 تا 299 قرار گرفته باشد. در نظر داشته باشید که HttpClient برای دلایل دیگری نیز می تواند خطا throw کند، مثلا اگر پاسخگویی به درخواست باعث time out شود.

Media-Type Formatter های مورد نیاز برای deserialize کردن

زمانی که متد ReadAsAsync بدون پارامتر مربوط به Media-Type فراخوانی می شود، از مجموعه پیش فرض از media formatter ها برای خواندن بدنه پاسخ استفاده می کند. media formatter های پیش فرض از JSON ، XML و دیتای Form-url-encoded پشتیبانی می کند.
به جای استفاده از media formatter های پیش فرض ما می توانیم لیستی از media formatter به متد ReadAsAsync ارسال کنیم. اگر از media-type formatter های سفارشی و شخصی سازی شده استفاده می کنیم، بهتر است از این طریق آنها را به متد ReadAsAsync ارسال کنیم. به مثال زیر توجه نمایید:

		
var formatters = new 
List<MediaTypeFormatter>() 
{
	new MyCustomFormatter(),
	new JsonMediaTypeFormatter(),
	new XmlMediaTypeFormatter()
};
resp.Content.ReadAsAsync<IEnumerable<Product>>(formatters);
	
		

ارسال درخواست POST

کد نوشته شده زیر یک درخواست POST ارسال می کند، این درخواست دارای یک نمونه از شیء product است که در قالب JSON تهیه شده است. یعنی به قالب JSON تبدیل شده و سپس داخل درخواست ارسال شده است.

		
static async Task<Uri> CreateProductAsync(Product product)			
{  		    
	HttpResponseMessage response = await 
	client.PostAsJsonAsync("api/products", product);
	response.EnsureSuccessStatusCode();

	// return URI of the created resource.
	return response.Headers.Location;
}
		
		
متد PostAsJsonAsync:
  • شیء مورد استفاده ما را به قالب JSON تبدیل می کند (deseralize می کند).
  • محتوای آماده شده در قالب JSON را درون درخواست POST ارسال می کند.
اگر پاسخ درخواست موفقیت آمیز باشد:
  • کد پاسخ ارسالی باید 201 باشد.
  • پاسخ باید شامل آدرس URL شیء ایجاد شده در هدر Location باشد.

ارسال یک درخواست PUT برای بروز رسانی یک شیء-موجودیت

کد زیر یک درخواست PUT را برای بروزرسانی یک محصول ارسال می کند.

        
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; }
متد PutAsJsonAsync مشابه یا متد PostAsJsonAsync عمل می کند، تنها تفاوتش در این است که به جای درخواست POST درخواست PUT ارسال می کند.

ارسال درخواست DELETE برای حذف یک شیء-موجودیت

کد زیر یک درخواست DELETE برای حذف یک محصول ارسال می کند.

  
static async Task<HttpStatusCode> DeleteProductAsync(string id)
{
  	HttpResponseMessage response = await 
	client.DeleteAsync($"api/products/{id}");
	return response.StatusCode;
}
		
		
مشابه با GET، درخواست DELETE نیز بدون بدنه درخواست است، بدین معنی که نیاز نیست تا فرمت XML یا JSON را برای DELETE مشخص کنیم.

منابع: br />
ASP.NET Web API
ASP.NET MVC - Web API

پست های مرتبط

نام را وارد کنید
تعداد کاراکتر باقیمانده: 1000
نظر خود را وارد کنید