Одним из ключевых моментов работы многих мобильных приложений является возможность взаимодействия с сервером - отправка или получение данных. И в Xamarin Forms мы тоже можем взаимодействовать с сервером. Рассмотрим на примере получения данных в формате json.
В качестве примера возьмем бесплатный API от Yahoo для получения текущих котировок валют. Так, для получения текущих значений для пары "рубль-доллар" в данном API необходимо обратиться по адресу https://query.yahooapis.com/v1/public/yql?q=select+*+from+yahoo.finance.xchange+where+pair+=+%22USDRUB%22&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=. И в качестве ответа мы получим данные в формате json примерно следующего вида:
{ "query":{ "count":1, "created":"2016-08-19T15:46:31Z", "lang":"ru-RU", "results":{ "rate":{ "id":"USDRUB", "Name":"USD/RUB", "Rate":"64.0425", "Date":"8/19/2016", "Time":"12:53pm", "Ask":"64.0513", "Bid":"64.0425" } } } }
Теперь посмотрим, как мы эти данные можем получать и отображать в приложении на Xamarin Forms.
Создадим новый проект и вначале добавим в него класс, который будет представлять загружаемые данные. Назовем этот класс RateInfo:
using System; namespace RestApp { public class RateInfo { public string Id { get; set; } public string Name { get; set; } public decimal Rate { get; set; } public DateTime Date { get; set; } public string Time { get; set; } public decimal Ask { get; set; } public decimal Bid { get; set; } } }
Как можно заметить, данный класс соответствует части ответа от сервера в формате json:
"rate":{ "id":"USDRUB", "Name":"USD/RUB", "Rate":"64.0425", "Date":"8/19/2016", "Time":"12:53pm", "Ask":"64.0513", "Bid":"64.0425" }
Затем поскольку мы загружаем данные в формате json, то нам надо будет десериализовать эти данные. Для этого будем использовать библиотеку Newtonsoft.Json, которую добавим в проект через Nuget:
Далее добавим в проект класс RateViewModel, который будет представлять ViewModel, через которую будет идти загрузка и отображение данных на странице:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.ComponentModel; using System.Net.Http; using System.Windows.Input; using Xamarin.Forms; namespace RestApp { public class RateViewModel : INotifyPropertyChanged { private decimal rate; private decimal ask; private decimal bid; public decimal Rate { get { return rate; } private set { rate = value; OnPropertyChanged("Rate"); } } public decimal Ask { get { return ask; } private set { ask = value; OnPropertyChanged("Ask"); } } public decimal Bid { get { return bid; } private set { bid = value; OnPropertyChanged("Bid"); } } public ICommand LoadDataCommand { protected set; get; } public RateViewModel() { this.LoadDataCommand = new Command(LoadData); } private async void LoadData() { string url = "https://query.yahooapis.com/v1/public/yql?q=select+*+from+yahoo.finance.xchange+where+pair+=+%22USDRUB%22&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback="; try { HttpClient client = new HttpClient(); client.BaseAddress = new Uri(url); var response = await client.GetAsync(client.BaseAddress); response.EnsureSuccessStatusCode(); // выброс исключения, если произошла ошибка // десериализация ответа в формате json var content = await response.Content.ReadAsStringAsync(); JObject o = JObject.Parse(content); var str = o.SelectToken(@"$.query.results.rate"); var rateInfo = JsonConvert.DeserializeObject<RateInfo>(str.ToString()); this.Rate = rateInfo.Rate; this.Ask = rateInfo.Ask; this.Bid = rateInfo.Bid; } catch(Exception ex) { } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string prop = "") { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop)); } } }
Прежде всего надо отметить, что данный класс реализует интерфейс INotifyPropertyChanged. В классе определены свойства Rate, Bid, Ask, которые аналогичны соответствующим свойствам из класса Rate и данные которых будут выводиться на страницу.
Для загрузки данных определено свойство-команда LoadDataCommand. Эта команда при выполнении будет вызывать метод LoadData()
.
Для взаимодействия с сервером применяется класс HttpClient. Он определяет ряд методов, через которые можно отправлять серверу данные или, наоборот, получать
от него данные. В данном случае мы задействует метод client.GetAsync()
для получения ответа по указанному адресу.
С помощью метода response.EnsureSuccessStatusCode()
указываем, что в случае неудачного выполнения запроса будет выброшено исключение, которое
мы можем обработать.
В итоге весь код загрузки данных по определенному адресу выглядеть следующим образом:
HttpClient client = new HttpClient(); client.BaseAddress = new Uri(url); var response = await client.GetAsync(client.BaseAddress); response.EnsureSuccessStatusCode(); // выброс исключения, если произошла ошибка
После получения ответа его надо привести в тот вид, который позволит нам манипулировать содержащимися в нем данными. Для этого вначале получим текст ответа с
помощью метода response.Content.ReadAsStringAsync()
.
Поскольку текст ответа представляет данные в формате json, то нам их надо распарсить и привести к объекту RateInfo. Для этого получаем JObject для упрощения работы с данными:
JObject o = JObject.Parse(content);
Поскольку нас интересуют не все абсолютно данные, а только их часть (элемент rate в полученном ответе), то нам их надо извлечь:
var str = o.SelectToken(@"$.query.results.rate");
Выражение "$.query.results.rate" представляет синтаксис языка запросов в JSON - JsonPath, с помощью которого мы можем обратиться к определенной порции данных. В данном случае мы получаем узел rate, который находится в узле results, который в свою очередь находится в узле query. То есть по факту мы получим следующий узел:
"rate":{ "id":"USDRUB", "Name":"USD/RUB", "Rate":"64.0425", "Date":"8/19/2016", "Time":"12:53pm", "Ask":"64.0513", "Bid":"64.0425" }
И в конце десериализуем этот узел в объект RateInfo (десериализация идет на основании соответствия свойств класса и свойств в json):
JsonConvert.DeserializeObject<RateInfo>(str.ToString())
После этого происходит установка полученных данных:
this.Rate = rateInfo.Rate; this.Ask = rateInfo.Ask; this.Bid = rateInfo.Bid;
И в конце определим главную страницу MainPage. В коде C# определим в качестве контекста страницы объект RateViewModel:
using Xamarin.Forms; namespace RestApp { public partial class MainPage : ContentPage { RateViewModel viewModel; public MainPage() { InitializeComponent(); viewModel = new RateViewModel(); // установка контекста данных this.BindingContext = viewModel; } } }
А в коде XAML определим привязку элементов к свойствам RateViewModel:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="RestApp.MainPage"> <StackLayout> <Button Text="Получить курс" Command="{Binding LoadDataCommand}" /> <Label FontSize="Medium" Text="Текущий курс" /> <Label FontSize="Medium" FontAttributes="Bold" Text="{Binding Rate}" /> <Label FontSize="Medium" Text="Спрос" /> <Label FontSize="Medium" FontAttributes="Bold" Text="{Binding Ask}" /> <Label FontSize="Medium" Text="Предложение" /> <Label FontSize="Medium" FontAttributes="Bold" Text="{Binding Bid}" /> </StackLayout> </ContentPage>
Запустим приложение и нажмем на кнопку, и на странице отобразятся загруженные с сервера данные: