从零开始搭建Wpf基础7-Api数据接入

前言:使用HttpClient获取Api数据。

第一步:对获取数据结构进行规划,我们还是采用经典的Business作为中间层,HttpClient等底层请求数据的方法放在Service层。

ViewModel -> Business -> Service

第二步:HttpClient的使用httpClientFactory.CreateClient()来获取,避免端口号未能及时释放导致耗尽的情况,我们用单例来构造HttpClientHelper。

public class HttpClientHelper
{
    private static HttpClientHelper instance = null;
    private static object obj = new object();
    private IHttpClientFactory httpClientFactory;

    public static HttpClientHelper Instance
    {
        get
        {
            if (instance == null)
            {
                lock (obj)
                {
                    if (instance == null)
                    {
                        instance = new HttpClientHelper();
                        var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
                        instance.httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
                    }
                }
            }
            return instance;
        }
    }      

    /// <summary>
    /// 记录日志
    /// </summary>
    public Action<string> HandleLog { get; set; }


    /// <summary>
    /// 使用post方法异步请求
    /// </summary>
    /// <param name="url">目标链接</param>
    /// <param name="json">发送的参数字符串,只能用json</param>
    /// <returns>返回的字符串</returns>
    public async Task<string> PostAsyncJson(string url, string json, Dictionary<string, string> header = null, TimeSpan? timeSpan = null)
    {
        HttpClient client = httpClientFactory.CreateClient();
        if (timeSpan.HasValue)
        {
            client.Timeout = timeSpan.Value;
        }
        HttpContent content = new StringContent(json);
        if (header != null)
        {
            client.DefaultRequestHeaders.Clear();
            foreach (var item in header)
            {
                client.DefaultRequestHeaders.Add(item.Key, item.Value);
            }
        }
        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

        string responseBody = string.Empty;
        string resData = string.Empty;
        DateTime startTime = DateTime.Now;

        try
        {
            HttpResponseMessage response = await client.PostAsync(url, content);
            response.EnsureSuccessStatusCode();
            responseBody = await response.Content.ReadAsStringAsync();
            resData = responseBody;
        }
        catch (Exception ex)
        {
            resData = $"异常:{ExceptionHelper.GetExceptionAllMsg(ex)}";

            throw ex;
        }
        finally
        {
            var time = DateTime.Now - startTime;
            if (resData?.Length > 1000)
            {
                resData = resData.Substring(0, 1000);
                resData += "......";
            }

            string log =
$@"方向:请求外部接口
url:{url}
method:{"Post"}
耗时:{(int)time.TotalMilliseconds}ms

返回:{resData}
";
            HandleLog?.Invoke(log);
        }

        return responseBody;
    }


}

第三步:我们需要两个方法:一个登录的时候获取Token的方法(使用jwt登录校验,登录后服务返回一个密匙Token,客户端用这个Token去访问服务),一个Post请求数据的方法,如下:

public class ApiDataProvider : IDataProvider
{
    private string Url { get; set; }
    public string Token { get; set; }

    //携带Token
    public Dictionary<string, string> SetHeader()
    {
        Dictionary<string, string> header = new Dictionary<string, string>();
        header.Add("Authorization", string.Format("Bearer {0}", Token));

        return header;
    }

    //获取数据
    public async Task<AjaxResult<T>> GetData<T>(string url, string json = "{}")
    {
        if (!url.StartsWith("http"))
        {
            url = Url + url;
        }
        var content = await HttpClientHelper.Instance.PostAsyncJson(url, json, SetHeader());
        var result = JsonConvert.DeserializeObject<AjaxResult<T>>(content);
        return result;
    }

    //获取Token
    public async Task<AjaxResult> GetToken(string url, string userName, string password)
    {
        Url = url;

        var content = await HttpClientHelper.Instance.PostAsyncJson((string.Format("{0}/Base_Manage/Home/SubmitLogin", Url)), JsonConvert.SerializeObject(new { userName = userName, password = password }));
        var result = JsonConvert.DeserializeObject<AjaxResult>(content);
        Token = result.Data as string;

        return result;
    }

}

同样我们用接口实现,好处是方便随时切换数据获取方法,比如HttpClient换成了WebSocket、SignalR,使用GetToken和GetData的地方是不用动的,只要实现WebSocketDataProvider就行了。

public interface IDataProvider
{
    Task<AjaxResult> GetToken(string url, string userName, string password);


    //[LogHandler]
    Task<AjaxResult<T>> GetData<T>(string url, string json = "{}");
}

第四步:添加Core(有些也叫Util)工程,一些基础公共的方法和类放在这里,此处不在贴代码,暂时目录结构如下:

从零开始搭建Wpf基础7-Api数据接入

第五步:修改登录页面LoginViewModel的逻辑,使用_dataProvider.GetToken来实现登录。

IContainerExtension _container;
IRegionManager _regionManager;
IDataProvider _dataProvider;
public LoginViewModel(IContainerExtension container, IRegionManager regionManager, IDataProvider dataProvider)
{
    _container = container;
    _regionManager = regionManager;
    _dataProvider = dataProvider;

    Title = "Login";
}
private async void Login()
{
    if (!string.IsNullOrEmpty(UserName)  && !string.IsNullOrEmpty(Password))
    {
        var mD5Password = Password.ToMD5String();
        var token = await _dataProvider.GetToken(ServerIP, UserName, mD5Password);
        if (!token.Success)
        {
            MessageBox.Show(token.Msg);
            return;
        }

        _regionManager.RequestNavigate("MainContentRegion", nameof(IntroduceView));

    }
    else
    {
        MessageBox.Show("请输入用户名或密码!");
    }


}

好了,运行看效果,很遗憾报错,因为IDataProvider并没有实现注册,程序并不知道IDataProvider是用ApiDataProvider实现的,当然你直接new一个也是可以的_dataProvider = new ApiDataProvider()。这就违背了我们IOC的初衷了。

第六步:在App.xaml.cs中注册IDataProvider。

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.Register<IDataProvider, ApiDataProvider>();
}

再次运行,效果上还是一样的,但是我们成功的访问了后台。 从零开始搭建Wpf基础7-Api数据接入

后续:下一章将实现,将登录界面做成独立窗口,登录后再显示主窗口。

源码地址:https://gitee.com/akwkevin/aistudio.-wpf.-client.-stepby-step

另外推荐一下我的Wpf客户端框架:https://gitee.com/akwkevin/aistudio.-wpf.-aclient

从零开始搭建Wpf基础7-Api数据接入

上一篇:winform使用Resize设置窗口控件大小按照窗口大小等比例缩放


下一篇:C# 为什么你应该更喜欢 is 关键字而不是 == 运算符