使用.NET 6(全球市场)构建Angular 13应用程序——第2部分

2023-11-19

目录

介绍

Yahoo Finance API

全球市场API

金融控制器

报价响应模型

雅虎金融设置

金融服务

Http获取报价端点

全球市场前端

我们尝试做什么?

集成后端API项目

Angular Material

显示符号下拉列表

显示获取报价结果

显示进度条

全球市场新面貌

调试Angular前端和.NET后端

如何使用源代码

结论


介绍

第1部分中,我们从Visual Studio 2022创建了一个Angular 13前端和.NET 6后端。本文继续构建这个金融应用程序,在后端调用Yahoo Finance API并使用Angular Material增强前端。

Yahoo Finance API

Yahoo Finance API是一系列库/API/方法,用于获取各种金融市场和产品的历史和实时数据,如Yahoo Finance所示。

其中一些产品包括加密货币、常规货币、股票和债券、基本面和期权数据以及市场分析和新闻的市场数据。使用Yahoo Finance API的一个很好的理由是它可以完全免费。此外,它简单易用。

在开始使用之前,您需要登录Yahoo Finance以获取您自己的API密钥。

全球市场API

让我们回到GlobalMarketAPI项目。删除默认创建的WeatherForecastController.csWeatherForecast.cs我们将使用Yahoo Finance API构建金融控制器。

查看Yahoo Finance API规范,我们使用http Get /v6/finance/quote获取股票、加密货币、期货等的实时报价数据。

金融控制器

右键单击Controllers文件夹以添加新控制器。

选择API Controller – Empty添加一个空控制器。

将其命名为FinanceController.cs,使用以下代码创建一个空的API控制器:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace GlobalMarketAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class FinanceController : ControllerBase
    {
    }
}

报价响应模型

Yahoo Finance API规范中,我们得到Get Quote API的响应JSON

根据规范,我们首先添加Quote类,它有ShortNameFullExchangeNameQuoteTypeRegularMarketPriceRegularMarketDayHighRegularMarketDayLowBidAsk

namespace GlobalMarketAPI.Models
{
    public class Quote
    {
        public string? ShortName { get; set; }

        public string? FullExchangeName { get; set; }

        public string? QuoteType { get; set; }

        public decimal RegularMarketPrice { get; set; }

        public decimal RegularMarketDayHigh { get; set; }

        public decimal RegularMarketDayLow { get; set; }

        public decimal Bid { get; set; }

        public decimal Ask { get; set; }
    }
}

然后添加具有Quoteerror string列表的QuoteResult类。

namespace GlobalMarketAPI.Models
{
    public class QuoteResult
    {
        public List<Quote>? Result { get; set; }

        public string? Error { get; set; }
    }
}

最后是具有QuoteResultYahooQuoteResponse类。

namespace GlobalMarketAPI.Models
{
    public class YahooQuoteResponse
    {
        public QuoteResult? QuoteResponse { get; set; }
    }
}

雅虎金融设置

当我们调用Yahoo Finance API时,我们需要API URLAPI密钥。我们将这些放在应用程序设置中。appsettings.json中添加Yahoo Finance设置。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "YahooFinanceSettings": {
    /* Please replace with your own api key */
    "APIKey": "****************",
    "BaseURL": "https://yfapi.net"
}

基本URL是常量字符串https://yfapi.net。将API密钥替换为您自己的API密钥。

创建YahooFinanceSettings类。

namespace GlobalMarketAPI.Settings
{
    public class YahooFinanceSettings
    {
        public string? APIKey { get; set; }
        public string? BaseURL { get; set; }
    }
}

Program.cs中注入雅虎金融设置。

using GlobalMarketAPI.Settings;

var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;

builder.Services.AddTransient(p =>
{
    YahooFinanceSettings settings = configuration.GetSection
                (nameof(YahooFinanceSettings)).Get<YahooFinanceSettings>();

    return settings;
});

金融服务

现在创建雅虎金融服务。报价URL/v6/finance/quote

我们可以像这样编写获取报价函数:

var url = $"v6/finance/quote?symbols={symbol}";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("X-API-KEY", new[] { settings.APIKey });
httpClient.BaseAddress = new Uri(settings.BaseURL ?? "");
var data = await httpClient.GetStringAsync(url);

添加金融服务接口:

using GlobalMarketAPI.Models;

namespace GlobalMarketAPI.Services
{
    public interface IFinanceService
    {
        Task<Quote> GetQuote(string symbol);
    }
}

添加金融服务类:

using GlobalMarketAPI.Models;
using Newtonsoft.Json;
using GlobalMarketAPI.Settings;

namespace GlobalMarketAPI.Services
{
    public class FinanceService : IFinanceService
    {
        private readonly HttpClient _httpClient;
        const string QuoteURL = "v6/finance/quote";
        
        public FinanceService(YahooFinanceSettings settings)
        {
            _httpClient = new HttpClient();
            _httpClient.DefaultRequestHeaders.Add
                 ("X-API-KEY", new[] { settings.APIKey });
            _httpClient.BaseAddress = new Uri(settings.BaseURL ?? "");
        }

        public async Task<Quote> GetQuote(string symbol)
        {
            var url = QuoteURL + $"?symbols={symbol}";
            try
            {
                var data = await _httpClient.GetStringAsync(url);
                var result = JsonConvert.DeserializeObject<YahooQuoteResponse>(data);
                return result?.QuoteResponse?.Result?.FirstOrDefault() ?? new Quote();
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

Program.cs中注入FinanceService实例:

builder.Services.AddTransient<IFinanceService, FinanceService>();

Http获取报价端点

现在我们可以在金融控制器中编写HttpGet Quote端点。

[HttpGet]
[Route("quote")]
public async Task<Quote> GetQuote([FromQuery] string symbol)
{
    return await _financeService.GetQuote(symbol);
}

每个控制器方法的路由模板是前缀加上Route属性中指定的string。对于该GetQuote方法,route模板包含字符串quoteURL包含string Symbol作为查询字符串参数。

现在单击开始按钮运行解决方案。GlobalMarketAPIGlobalMarket都启动了。

您应该能够看到GlobalMarketAPI控制台窗口。

然后我们可以检查Swagger UI来验证API端点。

http://localhost:5219/swagger

全球市场前端

我们尝试做什么?

交易小部件在我们的前端运行良好。现在我们只是增加一点乐趣。我们想要一个符号下拉列表。当我们从下拉列表中选择一个交易品种时,调用后端API以获取该交易品种的实时报价,同时重新加载该交易品种的交易小部件。

集成后端API项目

更改反向代理配置以连接后端金融控制器。

更新GlobalMarket/projects/trading下的proxy.config.js

const PROXY_CONFIG = [
  {
    context: [
      "/api",
    ],
    target: "https://localhost:7219",
    secure: false
  }
]

module.exports = PROXY_CONFIG;

虽然我们可以在上下文中添加多个URL,但如果您不想在每个控制器中更改此代理配置文件,可以添加父URL,例如/api

现在我们开始使用Angular Material样式前端。

Angular Material

Angular团队构建和维护常见的UI组件和工具,以帮助您构建自己的自定义组件。Angular MaterialAngular应用程序的Material Design UI组件。

Visual Studio 2022解决方案资源管理器中右键单击GlobalMarket项目,然后单击在终端中打开

运行以下命令来安装Angular Material

ng add @angular/material
  • 为自定义主题选择预建主题名称或自定义
  • 设置全局Angular Material排版样式。
  • Angular Material设置浏览器动画。

当然,我们也需要最流行的CSS bootstrap库。

npm install bootstrap --save

安装后,我们在GlobalMarkt/projects/trading/src/styles.css导入bootstrapAngular Material CSS样式。

/* You can add global styles to this file, and also import other style files */

@import "~bootstrap/dist/css/bootstrap.css";

@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

Angular Material提供高质量的组件。您可以 在此链接中查看。在使用这些组件之前,您需要导入组件模块。以下是我们前端使用的组件。

  • mat-card

<mat-card>是单个主题上下文中的文本、照片和动作的内容容器。

  • mat-form-field

<mat-form-field>是一个组件,用于包装多个Angular Material组件并应用常见的文本字段样式,例如下划线、浮动标签和提示消息。

  • mat-select

<mat-select>是一个表单控件,用于从一组选项中选择一个值,类似于原生<select>元素。

  • mat-chip-list

<mat-chip-list>显示单个、键盘可访问、chip等值的列表。

  • mat-progress-bar

<mat-progress-bar>是用于指示进度和活动的水平进度条。

  • mat-divider

<mat-divider>是一个组件,它允许使用各种方向选项对行分隔符进行Material样式设置。

app.module.ts中导入这些模块。

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { TradingviewWidgetModule } from 'tradingview-widget';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ClipboardModule } from '@angular/cdk/clipboard';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatCardModule } from '@angular/material/card';
import { MatChipsModule } from '@angular/material/chips'
import { MatDividerModule } from '@angular/material/divider'
import { MatFormFieldModule } from '@angular/material/form-field'
import { MatSelectModule } from '@angular/material/select'
import { MatProgressBarModule } from '@angular/material/progress-bar'

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    TradingviewWidgetModule,
    BrowserAnimationsModule,
    FormsModule,
    ReactiveFormsModule,
    ClipboardModule,
    DragDropModule,
    MatCardModule,
    MatChipsModule,
    MatDividerModule,
    MatFormFieldModule,
    MatSelectModule,
    MatProgressBarModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

我们想使用mat-form-field mat-select,所以需要导入FomrsModuleReactiveFormsModule

现在使用这些组件来更改app.component.html

整个页面分为两张卡片。最上面的一张是符号卡,另一张是显示交易小部件。

<mat-card>
  <mat-card-subtitle style="margin-top:-10px">Symbol Quote</mat-card-subtitle>
</mat-card>
<mat-divider></mat-divider>
<mat-card>
  <mat-card-subtitle style="margin-top:-10px">Live Chart</mat-card-subtitle>
  <tradingview-widget [widgetConfig]="widgetConfig"></tradingview-widget>
</mat-card>

显示符号下拉列表

打开AppComponent类文件并定义符号数组。

symbols = ['MSFT',
    'AAPL',
    'AMZN',
    'TSLA',
    'WTC',
    'BTCUSD',
    'ETHUSD',
    'CLN2022'
  ];

打开AppComponent模板文件添加<mat-select>。使用ngFor显示列表,并将选定的符号值绑定到widgetConfig.symbol

<mat-form-field appearance="fill">

  <mat-select id="symbol" class="symbol" [ngModel]="widgetConfig.symbol" 
                          (ngModelChange)="onSymbolChange($event)" required>

<mat-option *ngFor="let symbol of symbols" [value]="symbol">{{symbol}}</mat-option>

  </mat-select>

</mat-form-field>

我们希望在所选symbol更改时重新加载交易视图小部件。打开AppComponent类文件以添加OnSymbolChange事件。

onSymbolChange(event: any) {
  this.widgetConfig = {
    symbol: event,
    widgetType: 'widget',
    allow_symbol_change: true,
    height: 560,
    width: 980,
    hideideas: true,
    hide_legend: false,
    hide_side_toolbar: true,
    hide_top_toolbar: false,
    theme: Themes.LIGHT,
  };
  this.ngOnInit();
}

Angular中,调用ngOnInit会刷新组件。

显示获取报价结果

添加一个新app.model类(app.model.ts),然后定义Quote接口和DataItem接口。

export interface Quote {
  shortName: string;
  fullExchangeName: string;
  quoteType: string;
  regularMarketPrice: number;
  regularMarketDayHigh: number;
  regularMarketDayLow: number;
  bid: number;
  ask: number;
}
export interface DataItem {
  name: string;
  value: number;
}

打开AppComponent类文件调用后端获取报价API

this.http.get<Quote>(`/finance/quote?symbol=
      ${this.widgetConfig.symbol}`).subscribe(result => {
      this.quote = result;
      this.data = [
        { name: "Price", value: this.quote.regularMarketPrice },
        { name: "Day High", value: this.quote.regularMarketDayHigh },
        { name: "Day Low", value: this.quote.regularMarketDayLow },
        { name: "Ask", value: this.quote.ask },
        { name: "Bid", value: this.quote.bid },
      ];
    }, error => console.error(error));

打开AppComponent模板文件添加<mat-chip-list>以显示获取报价结果。

<mat-chip-list class="symbol-price-chip"
           cdkDropList
           cdkDropListOrientation="horizontal"
           (cdkDropListDropped)="drop($event)">
    <mat-chip class="symbol-price-box"
            cdkDrag
            *ngFor="let item of data">
    {{item.name}}: {{item.value}}
    </mat-chip>
</mat-chip-list>

<mat-chip>元素可以通过拖放更改顺序。在AppComponent类中添加drop事件:

drop(event: CdkDragDrop<DataItem[]>) {
  moveItemInArray(this.data, event.previousIndex, event.currentIndex);
}

显示进度条

打开AppComponent模板文件添加<mat-progress-bar>

<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isProgressing">
</mat-progress-bar>

打开AppComponent类文件进行设置isProgressing。在调用API之前,设置isProgessingtrue,然后在订阅回调中设置isProgressingfalse

this.isProgressing = true;
this.http.get<Quote>(`/finance/quote?symbol=
     ${this.widgetConfig.symbol}`).subscribe(result => {
  ...
  this.isProgressing = false;
}, error => console.error(error));

全球市场新面貌

现在我们的应用程序在后端和前端更改后有了新的外观。按F5或单击顶部菜单栏的开始按钮。正如我们之前配置的那样,GlobalMarketAPI(后端)和GlobalMarket(前端)都已启动。

您可以拖放价格”“当日最高” “当日最低价”“要价”“出价以更改为您想要的任何顺序。

在符号下拉列表中将符号更改为AAPL – Apple

然后你可以看到苹果公司的交易图表和报价信息。

如你所知,加密货币最近崩盘了。所以我真的很想看看比特币交易图表。在下拉列表中将符号更改为“BTCUSD”(比特币)。

唉,悲剧了。

但是等等,为什么没有报价信息?让我们调试一下。

调试Angular前端和.NET后端

在前端,我们在调用获取报价API和订阅回调处设置断点。

在后端,我们在金融控制器的GetQuote函数处设置了一个断点。

现在将符号更改为BTCUSD

第一个断点被触发。你可以看到我们正确地传递了符号BTCUSD

F5或单击菜单栏上的继续即可。

金融控制器的断点GetQuote被触发。您还可以看到符号“ BTCISD正确传递。

让我们按F11进入。

Yahoo Finance API URL也是正确的。让我们在异常捕获处设置断点。

F10跳过。

也不例外,但结果似乎什么也没有。这意味着雅虎金融找不到有关此符号的任何信息。为什么?那是因为雅虎金融符号与交易视图符号并不完全相同。例如,雅虎金融符号为BTCUSD变为BTC-USDETHUSD变为“ ETH-USD等。

要解决此问题,我们需要在交易视图符号和Yahoo Finance符号之间添加映射。

我们可以将此映射添加到GlobalMarketAPI应用程序设置中。

"YahooFinanceSettings": {
    /* Please replace with your own api key */
    "APIKey": "******",
    "BaseURL": "https://yfapi.net",
    "SymbolMapInfo": {
      "WTC": "WTC.AX",
      "BTCUSD": "BTC-USD",
      "ETHUSD": "ETH-USD",
      "CLN2022": "CLN22.NYM"
    }
}
  • WTC_智科技环球
  • BTCUSD——比特币
  • ETHUSD——以太坊
  • CLN2022——20227

现在GlobalMarketAPI中将字典添加到YahooFinaceSettings.cs中。它将在程序启动时加载。

namespace GlobalMarketAPI.Settings
{
    public class YahooFinanceSettings
    {
        public string? APIKey { get; set; }
        public string? BaseURL { get; set; }
        public Dictionary<string, string>? SymbolMapInfo
        {
            get;
            set;
        }
    }
}

公开FinanceService类,添加符号映射字典。

private readonly Dictionary<string, string> _symbolMap = new Dictionary<string, string>();

在构造函数中设置此字典。

if (settings.SymbolMapInfo != null && settings.SymbolMapInfo.Count > 0)
{
    _symbolMap = settings.SymbolMapInfo.ToDictionary(x => x.Key, x => x.Value);
}

如果符号有映射符号,请替换它。

symbol = _symbolMap.ContainsKey(symbol) ? _symbolMap[symbol] : symbol;

现在再次调试。

Symbol是映射一个的,BTC-USD

Yahoo Finance API返回结果。

F5继续。触发Angular http客户端回调函数的断点。

F5继续。最后,获取比特币报价信息。

如何使用源代码

下载并提取源代码,使用Visual Studio 2022打开GlobalMaket.sln

然后,在解决方案资源管理器中右键单击GlobalMarket项目,选择在终端中打开

需要先建tradingview-widget库。

npm install

ng build tradingview-widget

结论

1部分继续,在本文中,我们将Angular前端与ASP.NET Core Web API集成,并使用Angular Material来设置前端样式。最后,我们向您展示了在Visual Studio 2022中同时调试前端和后端是多么容易。您可以从github获取所有源代码。

https://www.codeproject.com/Articles/5332007/Building-an-Angular-13-Application-with-NET-6-Gl-2

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用.NET 6(全球市场)构建Angular 13应用程序——第2部分 的相关文章

随机推荐