ASP.NET Core 从零开始用小化的代码演示 HttpClient 和 IHttpClientFactory 的使用

yufei       2 年, 5 月 前       466

最近一直在思考一个问题,就是写教程的时候,要如何用最少的文字和代码尽可能的把教程涉及到的方方面面演示出来!

如果只是语言层面,感觉还是可以的,但是如果用到了框架,用到了系统给予的解决方案,也的确有难度。

这篇,我们尝试 从零开始用小化的代码演示 HttpClient 和 IHttpClientFactory 的使用 ,看看篇幅怎么样!

1. 创建项目

我们使用 Visual Studio 或 Visual Studio Code 创建一个名为 myhttpclient1 的模板为 控制台应用程序 的项目

dotnet_core_httpclient1_1.png

如果你习惯使用命令行,比如我,那么可以用下面的命令创建

dotnet new console --name myhttpclient1 

创建完成后目录结果如下

├── Program.cs
├── myhttpclient1.csproj
└── obj
    ├── myhttpclient1.csproj.nuget.dgspec.json
    ├── myhttpclient1.csproj.nuget.g.props
    ├── myhttpclient1.csproj.nuget.g.targets
    ├── project.assets.json
    └── project.nuget.cache

dotnet_core_httpclient1_2.png

2. 改变项目的程序集

使用 Visual Studio Code 打开 myhttpclient1.csproj 文件,将 <Project Sdk="Microsoft.NET.Sdk"> 改成 <Project Sdk="Microsoft.NET.Sdk.Web">

- <Project Sdk="Microsoft.NET.Sdk">
+ <Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

如果你使用 Visual Studio,则是在 myhttpclient1 项目上 点右键,然后点击 编辑项目文件

dotnet_core_httpclient1_3.png

为什么要改变程序集

因为原本的 Microsoft.Net.SDK 没有包含 Web 应用程序所需的命名空间,只有 Microsoft.Net.SDK.Web 包含

3. 编辑 Program.cs 文件添加一些命名空间

打开 Program.cs 文件后,我们可以看到原文件内容大概是这样的

using System;

namespace myhttpclient1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

我们需要加入以下命名空间

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

这些命名空间我们不用刻意加入,因为编辑器的自动完成功能可以很好的帮我们完成这些。

4. 在 Program.cs 中添加一个服务,名字为 MyService

接下来我们需要在 myhttpclient1 空间下添加一个服务 MyService

按照 C# 和 .NET Core 程序惯例,我们将添加一个名为 IMyService 的接口和 MyService 的实现类

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace myhttpclient1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }

    interface IMyService
    {

    }

    class MyService:IMyService
    {

    }
}

IMyServiceMyService 没有添加任何方法和属性,这不重要,我们首先需要的就是一个架子

5. 改造 Main() 方法创建一个 IHost 对象并注入 MyService 服务

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace myhttpclient1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. 创建一个 IHostBuilder 对象
            // 2. 通过 IHostBuilder 对象的 ConfigureServices 添加 HttpClient 服务和 `MyService` 服务
            var builder = new HostBuilder()
           .ConfigureServices((HostBuilderContext, services) => {
               services.AddHttpClient();
               services.AddTransient<IMyService, MyService>();
           }).UseConsoleLifetime();

            // 4. 创建一个 IHost 对象
            var host = builder.Build();

            Console.WriteLine("Hello World!");
        }
    }

    interface IMyService
    {

    }

    class MyService:IMyService
    {

    }
}

这里的代码很熟悉是不是?

其实这里的代码就是 ASP.NET Core 一般项目的 Program.csStartUp.cs 的合并。

  1. 首先我们通过 new HostBuilder() 创建一个 IHostBuilder 的实例,我们将用这个实例来创建 Host 主机对象。

  2. IHostBuilderConfigureServices() 方法其实就是 StartUp.cs 里的 ConfigureServices() 方法,我们在这个方法里通过 services.AddHttpClient() 加入了 HttpClient 服务,通过 services.AddTransient<IMyService, MyService>() 把我们自定义的 MyService 添加到服务列表。

  3. IHostBuilder 对象的 UseConsoleLifetime() 表示接受 Ctrl+C 来结束生命周期,也就是停止服务。

  4. 最后通过 IHostBuilder 对象的 Build() 方法创建一个 Host 对象。

其实,这段文字详细的给我们描述了一般的 ASP.NET Core 项目中的 Program 类和 StartUp 类之间的关系。如果你有空,可以尝试删掉 StartUp 类,并把里面的内容搬到 Program 中试一试

6. 完善 MyService,添加一个 GetPage() 方法获取百度首页的内容

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace myhttpclient1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. 创建一个 IHostBuilder 对象
            // 2. 通过 IHostBuilder 对象的 ConfigureServices 添加 HttpClient 服务和 `MyService` 服务
            var builder = new HostBuilder()
           .ConfigureServices((HostBuilderContext, services) => {
               services.AddHttpClient();
               services.AddTransient<IMyService, MyService>();
           }).UseConsoleLifetime();

            // 4. 创建一个 IHost 对象
            var host = builder.Build();

            Console.WriteLine("Hello World!");
        }
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService : IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage()
        {
            var request = new HttpRequestMessage(HttpMethod.Get,
            "http://www.baidu.com/");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            return $"StatusCode:{response.StatusCode}";
        }
    }
}
  1. 首先改造 IMyService 接口添加 GetPage 异步方法

    Task<string> GetPage()
    
    1. 改造 MyService 类,添加类型为 IHttpClientFactory只读属性 _clientFactory,我们将用这个 IHttpClientFactory 对象创建 HttpClient 实例。

      private readonly IHttpClientFactory _clientFactory;

  2. 改造 MyService 类,将 IHttpClientFactory 的实例作为构造函数的一部分。这样,系统在创建 MyService 类时,将会自动注入 HttpClientFactory 对象。而这个 HttpClientFactory 对象是通过 services.AddHttpClient() 注入的。

    public MyService(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }
    
  3. 好了,最后我们在 GetPage 方法中通过 _clientFactoryCreateClient() 创建 HttpClient 对象,并且调用该对象的 SendAsync() 来发起一个请求,并把请求结果的前 500 个字符座位结果返回

    public async Task<string> GetPage()
    {
        // 1. 使用 HttpRequestMessage 构造一个 Http 请求,需要指定请求方法和请求 Url
        var request = new HttpRequestMessage(HttpMethod.Get,
        "http://www.baidu.com/");
    
        // 2. 调用 CreateClient() 方法创建 HttpClient 对象
        var client = _clientFactory.CreateClient();
    
        // 3. 调用 HttpClient.SendAsync() 发起请求
        var response = await client.SendAsync(request);
    
        // 4. 通过 Http 响应的  IsSuccessStatusCode 来判断是否返回 200 服务
        if (response.IsSuccessStatusCode)
        {
            // 5. 获取响应的内容
            return await response.Content.ReadAsStringAsync();
        }
        return $"StatusCode:{response.StatusCode}";
    }
    

7. 调用 MyService.GetPage() 方法

然后我们就可以在 Main() 方法中使用我们的 MyService 服务,并且调用 GetPage 方法

static async Task<int> Main(string[] args)
{
    var builder = new HostBuilder()
    .ConfigureServices((HostBuilderContext,services) => {
        services.AddHttpClient();
        services.AddTransient<IMyService,MyService>();
    }).UseConsoleLifetime();

    var host = builder.Build();

    try {
        var myService = host.Services.GetRequiredService<IMyService>();
        var pageContent = await myService.GetPage();
        Console.WriteLine(pageContent.Substring(0,500));
    } catch(Exception ex) {
        var logger = host.Services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex,"An error occurred!");
    }
    return 0;
}
  1. 我们通过 Host 实例的 Services 属性上的 GetRequiredService<IMyService>() 方法获取我们加入的 MyService 的实例。
  2. 然后我们就可以通过 MyService.GetPage() 获取百度的内容

运行

代码阶段我们已经完全结束了,全部代码如下

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace myhttpclient1
{
    class Program
    {
        static async Task<int> Main(string[] args)
        {
            var builder = new HostBuilder()
            .ConfigureServices((HostBuilderContext,services) => {
                services.AddHttpClient();
                services.AddTransient<IMyService,MyService>();
            }).UseConsoleLifetime();

            var host = builder.Build();

            try {
                var myService = host.Services.GetRequiredService<IMyService>();
                var pageContent = await myService.GetPage();
                Console.WriteLine(pageContent.Substring(0,500));
            } catch(Exception ex) {
                var logger = host.Services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex,"An error occurred!");
            }
            return 0;
        }
    }

    public interface IMyService
    {
        Task<string> GetPage();
    }

    public class MyService: IMyService
    {
        private readonly IHttpClientFactory _clientFactory;

        public MyService(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }

        public async Task<string> GetPage() 
        {
            // 1. 使用
            var request = new HttpRequestMessage(HttpMethod.Get,
            "http://www.baidu.com/");
            var client = _clientFactory.CreateClient();
            var response = await client.SendAsync(request);
            if (response.IsSuccessStatusCode) {
                return await response.Content.ReadAsStringAsync();
            }
            return $"StatusCode:{response.StatusCode}";
        }
    }
}

60+ 行代码就演示了如何使用 HttpClient 和 IHttpClientFactory ,还附带让大家了解了 StartUp 类的由来

上面的代码,运行结果如下

<!DOCTYPE html><!--STATUS OK-->


    <html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta content="always" name="referrer"><meta name="theme-color" content="#2932e1"><meta name="description" content="全球领先的中文搜索引擎、致力于让网民更便捷地获取信息,找到所求。百度超过千亿的中文网页数据库,可以瞬间找到相关的搜索结果。"><link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" /><link rel="search" type="application/opensearchdescription+xml" href="/cont
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.