четверг, 10 октября 2013 г.

Web Api Wpf Client

http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-wpf-application

Calling a Web API From a WPF Application(C#)

By |
This tutorial shows how to call a web API from a Windows Presentation Foundation (WPF) application, usingHttpClient.
The main purpose of this tutorial is to see how asynchronous operations are handled in HttpClient. In this tutorial, we will consume the "ProductStore" API, described in Creating a Web API that Supports CRUD Operations.
Before you read this tutorial, you might want to read Calling a Web API From a .NET Client. That article introduces some of the concepts that I use in this tutorial.

Asynchronous Calls

HttpClient is designed to be non-blocking. Potentially long-running operations are implemented as asynchonrous methods, such as GetAsync and PostAsync. These methods return without waiting for the operation to complete. The previous tutorial (Calling a Web API From a Console Application) showed only blocking calls:
HttpResponseMessage response = client.GetAsync("api/products").Result;  // Blocking call!
This code blocks the calling thread by taking the Result property. That's OK for a console application, but you should not do it from a UI thread, because it blocks the UI from responding to user input.
The asynchronous methods of HttpClient return Task objects that represent the asynchronous operation.

Create the WPF Project

Start Visual Studio. From the Start menu, select New Project. In the Templates pane, select Installed Templatesand expand the Visual C# node. In the list of project templates, select WPF Application. Name the project and click OK.
Open MainWindow.xaml and add the following XAML markup inside the Grid control:
<StackPanel Width="250" >
    <Button Name="btnGetProducts" Click="GetProducts">Get Products</Button>
    <ListBox Name="ProductsList">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Margin="2">
                    <TextBlock Text="{Binding Path=Name}" />
                    <TextBlock >Price: $<Run Text="{Binding Path=Price}" /> 
                        (<Run Text="{Binding Path=Category}" />)</TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>
This markup defines a ListBox that will be data-bound to the list of products. The DataTemplate defines how each product will be displayed.

Add the Model Class

Add the following class to the application:
class Product
{
    public string Name { get; set; }
    public double Price { get; set; }
    public string Category { get; set; }
}
This class defines a data object that HttpClient will write into the HTTP request body and read from the HTTP response body.
We'll also add an observable class for data binding:
class ProductsCollection : ObservableCollection<Product>
{
    public void CopyFrom(IEnumerable<Product> products)
    {
        this.Items.Clear();
        foreach (var p in products)
        {
            this.Items.Add(p);
        }

        this.OnCollectionChanged(
            new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

Install NuGet Package Manager

NuGet Package Manager is the easiest way to add the Web API Client library to a project. If you do not have NuGet Package Manager already installed, install it as follows.
  1. Start Visual Studio.
  2. From the Tools menu, select Extensions and Updates.
  3. In the Extensions and Updates dialog, select Online.
  4. If you don't see "NuGet Package Manager", type "nuget package manager" in the search box.
  5. Select the NuGet Package Manager and click Download.
  6. After the download completes, you will be prompted to install.
  7. After the installation completes, you might be prompted to restart Visual Studio.

Install the Web API Client Libraries

After NuGet Package Manager is installed, add the Web API Client Libraries package to your project.
  1. From the Tools menu, select Library Package ManagerNote: If do you not see this menu item, make sure that NuGet Package Manager installed correctly.
  2. Select Manage NuGet Packages for Solution...
  3. In the Manage NugGet Packages dialog, select Online.
  4. In the search box, type "Microsoft.AspNet.WebApi.Client".
  5. Select the ASP.NET Web API Self Host package and click Install.
  6. After the package installs, click Close to close the dialog.

Initialize HttpClient

From Solution Explorer, open the file MainWindow.xaml.cs. Add the following code.
namespace WpfProductClient
{
    using System;
    using System.Collections.Generic;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Windows;

    public partial class MainWindow : Window
    {
        HttpClient client = new HttpClient();
        ProductsCollection _products = new ProductsCollection();

        public MainWindow()
        {
            InitializeComponent();

            client.BaseAddress = new Uri("http://localhost:9000");
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            this.ProductsList.ItemsSource = _products;
        }
    }
}
This code creates a new instance of HttpClient. It also sets the base URI to "http://localhost:9000/", and sets the Accept header to "application/json", which tells the server to send data in JSON format.
Notice that we also created a new ProductsCollection class and set it as the binding for the ListBox.

Getting a Resource (HTTP GET)

If you are targeting .NET Framework 4.5, the async and await keywords make it much easier to write asynchronous code.
If you are targeting .NET Framework 4.0 with Visual Studio 2012, you can install the Async Targeting Pack to get async/await support.
The following code queries the API for a list of products. Add this code to the MainWindow class:
private async void GetProducts(object sender, RoutedEventArgs e)
{
    try
    {
        btnGetProducts.IsEnabled = false;

        var response = await client.GetAsync("api/products");
        response.EnsureSuccessStatusCode(); // Throw on error code.

        var products = await response.Content.ReadAsAsync<IEnumerable<Product>>();
        _products.CopyFrom(products);
    }
    catch (Newtonsoft.Json.JsonException jEx)
    {
        // This exception indicates a problem deserializing the request body.
        MessageBox.Show(jEx.Message);
    }
    catch (HttpRequestException ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        btnGetProducts.IsEnabled = true;
    }
}
The GetAsync method sends an HTTP GET request. If the HTTP response indicates success, the response body contains a list of products in JSON format. To parse the list, call ReadAsAsync. This method reads the response body and tries to deserialize it to a specified CLR type.
As their names imply, GetAsync and ReadAsAsync are asynchronous methods, meaning they return immediately, without waiting for the operation to complete. The await keyword suspends execution until the operation completes. For example:
var response = await client.GetAsync("api/products");
The code that appears after this statement does not execute until the HTTP request is completed. But that does notmean the event handler blocks, waiting for GetAsync to complete. Just the opposite — control returns to the caller. When the HTTP request is completed, execution continues from the point where it was suspended.
If a method uses await, it must have the async modifier:
private async void GetProducts(object sender, RoutedEventArgs e)
Without the await keyword, you would need to call ContinueWith on the Task object:
private void GetProducts(object sender, RoutedEventArgs e)
{
    btnGetProducts.IsEnabled = false;

    client.GetAsync("api/products/2").ContinueWith((t) =>
    {
        if (t.IsFaulted)
        {
            MessageBox.Show(t.Exception.Message);
            btnGetProducts.IsEnabled = true;
        }
        else
        {
            var response = t.Result;
            if (response.IsSuccessStatusCode)
            {
                response.Content.ReadAsAsync<IEnumerable<Product>>().
                    ContinueWith(t2 =>
                        {
                            if (t2.IsFaulted)
                            {
                                MessageBox.Show(t2.Exception.Message);
                                btnGetProducts.IsEnabled = true;
                            }
                            else
                            {
                                var products = t2.Result;
                                _products.CopyFrom(products);
                                btnGetProducts.IsEnabled = true;
                            }
                        }, TaskScheduler.FromCurrentSynchronizationContext());
            }

        }
    }, TaskScheduler.FromCurrentSynchronizationContext());
}
This type of code is difficult to get right, so it's recommended to target .NET 4.5, or if that's not possible, install the Async Targeting Pack.

Creating a Resource (HTTP POST)

Go back to the MainWindow.xaml file and add some (very) UI for creating a new product:
<Label FontWeight="ExtraBold">New Product</Label>
<Label>Name</Label>
<TextBox Name="textName"></TextBox>
<Label>Price</Label>
<TextBox Name="textPrice"></TextBox>
<Label>Category</Label>
<TextBox Name="textCategory"></TextBox>
<Button Name="btnPostProduct" Click="PostProduct">Post Product</Button>
Now add the following code to the MainWindow class.
private async void PostProduct(object sender, RoutedEventArgs e)
{
    btnPostProduct.IsEnabled = false;

    try
    {
        var product = new Product()
        {
            Name = textName.Text,
            Price = decimal.Parse(textPrice.Text),
            Category = textCategory.Text
        };
        var response = await client.PostAsJsonAsync("api/products", product);
        response.EnsureSuccessStatusCode(); // Throw on error code.

        _products.Add(product);
    }
    catch (HttpRequestException ex)
    {
        MessageBox.Show(ex.Message);
    }
    catch (System.FormatException)
    {
        MessageBox.Show("Price must be a number");
    }
    finally
    {
        btnPostProduct.IsEnabled = true;
    }
}
This code sends a POST request that contains a Product instance in JSON format. PostAsJsonAsync is an extension method defined in System.Net.Http.HttpClientExtensions. Internally, this method uses the JSON media-type formatter to serialize the Product to JSON and write it into the request body. For XML format, use thePostAsXmlAsync method.
Mike Wasson

Комментариев нет:

Отправить комментарий