Как получить курсы валют в приложении ASP.NET Core MVC

Дата публикации: 20.01.2020. Категория: ASP.NET Core MVC
Последнее обновление: 21.01.2020

В этом примере мы разберем, как можно получить данные о курсах валют в приложении типа ASP.NET Core, и как с этими данными можно работать.

Небольшая вводная, что будет реализовано:

  • версия платформы .NET Core: 2.1 или выше;
  • тип приложения: ASP.NET Core MVC;
  • данные о курсах валют берутся с сайта ЦБ РФ;
  • формат входящих данных: файл XML (не веб-сервис, не JSON);
  • процесс получения данных реализован в виде фоновой задачи, которая выполняется с некоторой периодичностью, пока приложение запущено;
  • класс-модель, с которым удобно работать и который содержит в себе данные о курсах и позволяет проводить конвертацию валют;
  • данные хранятся в кэше в оперативной памяти на сервере.

Это наиболее общий функционал, который будет реализован в приложении. Дальнейшая специализация на ваше усмотрение.

Создадим в Visual Studio новое приложение типа ASP.NET Core. Сразу выберем шаблон MVC, чтобы Visual Studio за нас подготовила минимальный необходимый функционал для запуска приложения. Режим аутентификации или защищенное https-соединение в этом примере не используется.

Создаем новое ASP.NET Core MVC приложение
Создаем новое ASP.NET Core MVC приложение

Сразу замечу, что функционал этого примера во многом заточен именно под работу с ЦБ РФ, как источником данных о курсах валют. В работе с ним есть некоторые нюансы, о чем сказано ниже. Если вы хотите брать курсы валют из другого источника, а они в целом более-менее все похожи, то изучите особенности API того сервиса, и в соответствии с этим скорректируйте код приложения.

Ознакомиться с информацией о том, как получать данные от ЦБ РФ, используя XML, можно по этой ссылке: Получение данных, используя XML

Придумаем себе техническое задание, в котором сказано, что основная валюта на сайте – это российские рубли. Также должна быть возможность представить какую-то цену на товар в иностранной валюте, а именно в долларах США, евро или гривнах.

Под это ТЗ создадим простейший класс-модель для того, чтобы работать с данными в объектно-ориентированной манере. Пусть также в обязанности этого класса входит конвертация одной валюты в другую. Для простоты демонстрации нарушим принцип SRP (Single Responsibility Principle).

файл CurrencyConverter.cs
public class CurrencyConverter
{
    public decimal USD { get; set; }
    public decimal ConvertToUSD(decimal priceRUB) => priceRUB / USD;

    public decimal EUR { get; set; }
    public decimal ConvertToEUR(decimal priceRUB) => priceRUB / EUR;

    //10 гривен номинал (такие данные от ЦБ)
    public decimal UAH10 { get; set; }
    public decimal ConvertToUAH(decimal priceRUB) => priceRUB / (UAH10 / 10);
}

После того как мы определили модель, реализуем функционал получения курсов валют и обработки их внутри нашего приложения. Для того чтобы этот процесс был автоматизирован и работал фоном с некоторой периодичностью, в ASP.NET Core очень удобно использовать класс BackgroundService, который в свою очередь реализует интерфейс IHostedService.

Создадим свой класс и наследуемся от класса BackgroundService:

файл CurrencyService.cs
public class CurrencyService : BackgroundService
{
    private readonly IMemoryCache memoryCache;

    public CurrencyService(IMemoryCache memoryCache)
    {
        this.memoryCache = memoryCache;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                //так как эта задача выполняется в другом потоке, то велика вероятность, что
                //культура по умолчанию может отличаться от той, которая установлена в нашем приложении,
                //поэтому явно укажем нужную нам, чтобы не было проблем с разделителями, названиями и т.д.
                Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-RU"); // <== нужная вам культура

                //кодировка файла xml с сайта ЦБ == windows-1251
                //по умолчанию она недоступна в .NET Core, поэтому регистрируем нужный провайдер 
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

                //т.к. мы знаем что данные к нам приходят именно в файле, именно в формате XML,
                //поэтому нет необходимости использовать WebRequest,
                //используем в работе класс XDocument и сразу забираем файл с удаленного сервера
                XDocument xml = XDocument.Load("http://www.cbr.ru/scripts/XML_daily.asp");

                //далее парсим файл и находим нужные нам валюты по их коду ID, и заполняем класс-модель
                CurrencyConverter currencyConverter = new CurrencyConverter();
                currencyConverter.USD = Convert.ToDecimal(xml.Elements("ValCurs").Elements("Valute").FirstOrDefault(x => x.Element("NumCode").Value == "840").Elements("Value").FirstOrDefault().Value);
                currencyConverter.EUR = Convert.ToDecimal(xml.Elements("ValCurs").Elements("Valute").FirstOrDefault(x => x.Element("NumCode").Value == "978").Elements("Value").FirstOrDefault().Value);
                currencyConverter.UAH10 = Convert.ToDecimal(xml.Elements("ValCurs").Elements("Valute").FirstOrDefault(x => x.Element("NumCode").Value == "980").Elements("Value").FirstOrDefault().Value);

                memoryCache.Set("key_currency", currencyConverter, TimeSpan.FromMinutes(1440));
            }
            catch (Exception e)
            {
                //logger.LogError(e.InnerException.Message);
            }

            //если указаний о завершении данной задачи не поступало,
            //то запрашиваем обновление данных каждый час
            await Task.Delay(3600000, stoppingToken);
        }
    }
}

* в коде выше добавлены поясняющие комментарии по некоторым неочевидным вопросам, это опять же возвращаясь к тому, что данный пример во многом заточен именно под ЦБ РФ как источник данных курсов валют.

Также не забудем добавить поддержку кэширования в проект и подключим наш CurrencyService как сервис в файле Startup.cs:

файл Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddHostedService<CurrencyService>();
    services.AddMemoryCache();
    services.AddControllersWithViews();
}

На данном этапе если мы запустим приложение на выполнение, то сразу же запустится фоновая задача и произойдет попытка получения данных о курсах валют. После этого класс-модель будет заполнен этими данными и помещен в кэш.

Давайте для примера извлечем нашу модель из кэша в HomeController, и передадим ее в представление Index():

файл HomeController.cs
public IActionResult Index()
{
    if (!memoryCache.TryGetValue("key_currency", out CurrencyConverter model))
    {
        throw new Exception("Ошибка получения данных");
    }
    return View(model);
}
файл Index.cshtml
@model CurrencyConverter
@{
    decimal priceRub = 1000;
}
<div>
    Цена товара в рублях: @priceRub руб.
    <div>
        @($"Курс доллара: {Model.USD}, цена в долларах: {Decimal.Round(Model.ConvertToUSD(priceRub), 2)}")
    </div>
    <div>
        @($"Курс евро: {Model.EUR}, цена в евро: {Decimal.Round(Model.ConvertToEUR(priceRub), 2)}")
    </div>
    <div>
        @($"Курс гривны: {Model.UAH10 / 10}, цена в гривнах: {Decimal.Round(Model.ConvertToUAH(priceRub), 2)}")
    </div>
</div>
Передаем класс CurrencyConverter в представление в качестве модели
Передаем класс CurrencyConverter в представление в качестве модели

 

Вернуться наверх
наверх