{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "source": [
    "# 基础使用-发送请求"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "source": [
    "## 0、初始化与全局设置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.DependencyInjection, 8.0.0</span></li><li><span>Microsoft.Extensions.Http, 8.0.0</span></li><li><span>Microsoft.Net.Http.Headers, 8.0.8</span></li><li><span>System.Net.Http.Json, 8.0.0</span></li></ul></div></div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "配置文件根目录:e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
      "启动WebApi项目\n",
      "程序[e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\n"
     ]
    }
   ],
   "source": [
    "//全局设置,行运行一次,为后续准备\n",
    "#r \"nuget:System.Net.Http.Json\"\n",
    "#r \"nuget:Microsoft.Net.Http.Headers\"\n",
    "#r \"nuget:Microsoft.Extensions.Http\"\n",
    "#r \"nuget:Microsoft.Extensions.DependencyInjection\"\n",
    "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
    "\n",
    "global using System;\n",
    "global using System.Collections;\n",
    "global using System.Collections.Concurrent;\n",
    "global using System.Linq;\n",
    "global using System.Linq.Expressions;\n",
    "global using System.Threading;\n",
    "global using System.Threading.Tasks;\n",
    "global using System.Net.Http;\n",
    "//System.Net.Http.Json 包含处理json的扩展方法,方便处理请求和影响中的json数据\n",
    "global using System.Net.Http.Json;\n",
    "global using Microsoft.Extensions.DependencyInjection;\n",
    "global using Microsoft.Extensions.DependencyInjection.Extensions;\n",
    "\n",
    "global using HttpClientStudy.Config;\n",
    "global using HttpClientStudy.Core;\n",
    "global using HttpClientStudy.Core.Utilities;\n",
    "\n",
    "//全局变量\n",
    "var webApiBaseUrl = WebApiConfigManager.GetWebApiConfig().BaseUrl;\n",
    "var workDir = Environment.CurrentDirectory;\n",
    "var fullPath = System.IO.Path.GetFullPath(\"./Publish/HttpClientStudy.WebApp/HttpClientStudy.WebApp.exe\", workDir);\n",
    "\n",
    "//全局共享静态 HttpClient 对象\n",
    "public static HttpClient SharedClient = new HttpClient(new SocketsHttpHandler(){ PooledConnectionIdleTimeout = TimeSpan.FromSeconds(30)})\n",
    "{\n",
    "    BaseAddress = new Uri(WebApiConfigManager.GetWebApiConfig().BaseUrl),\n",
    "};\n",
    "\n",
    "//启动已发布的WebApi项目\n",
    "{\n",
    "    Console.WriteLine(\"启动WebApi项目\");\n",
    "    var startMessage = AppUtility.RunWebApiExeFile(fullPath);\n",
    "    Console.WriteLine(startMessage);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1、创建HttpClient"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "根据 系列教程的 `管理客户端` 一章, 创建的HttpClient对象,有非常多的方式。\n",
    "为方便演示,本节使用两种:一种是全局共享对象(SharedClient, 在初始化时创建), 符合共享的使用原则; 一种是临时实例化对象。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2、发出 Http请求"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Http 简介"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "http是无状态的请求/响应模式。请求包括:请求行(方法 URL 协议版本 回车换行符)、请求头及可选的请求体;响应包括:状态行(协议版本 状态码 状态码描述 回车换行符)、响应头及可选的响应体。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Http请求方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Http请求方法可通过多种特性区分:\n",
    "\n",
    "1. 谓词:也就是请求方法,Post、Get、Put、Delete、Options、Head等;\n",
    "2. 幂等性:如果可多次成功处理某个请求方法而不改变结果,则该请求方法是幂等的;\n",
    "3. 可缓存性:如果可以存储请求方法的相应响应以供重复使用,则该请求方法是可缓存的;\n",
    "4. 安全性:如果请求方法不会修改资源的状态,则被视为安全方法。所有安全方法也都是幂等方法,但并非所有幂等方法都是安全方法;\n",
    "\n",
    "    | **HTTP 方法** | **是否是幂等的**   | **是否可缓存** | **是否安全**    |\n",
    "    | ------------- | ----------------  | -------------- | ------------    |\n",
    "    | `GET`         | ✔️ 是             | ✔️ 是           | ✔️ 是         |\n",
    "    | `POST`        | ❌ 否             | ⚠️ †很少        | ❌ 否         |\n",
    "    | `PUT`         | ✔️ 是             | ❌ 否           | ❌ 否         |\n",
    "    | `PATCH`       | ❌ 否             | ❌ 否           | ❌ 否         |\n",
    "    | `DELETE`      | ✔️ 是             | ❌ 否           | ❌ 否         |\n",
    "    | `HEAD`        | ✔️ 是             | ✔️ 是           | ✔️ 是         |\n",
    "    | `OPTIONS`     | ✔️ 是             | ❌ 否           | ✔️ 是         |\n",
    "    | `TRACE`       | ✔️ 是             | ❌ 否           | ✔️ 是         |\n",
    "    | `CONNECT`     | ❌ 否             | ❌ 否           | ❌ 否         |\n",
    "> \n",
    ">†仅当存在相应的 Cache-Control 或 Expires 响应标头时,POST 方法才可缓存。 这在实践中非常罕见。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Http 状态码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Http 状态码是响应的首要标识,指定响应的不同结果,分为以下几类:\n",
    "\n",
    "1. 1xx:信息,表示临时响应,大多数临时响应 (例如 HttpStatusCode.Continue) 使用 HttpClient 在内部处理,并且永远不会显示给用户;\n",
    "2. 2xx:成功,表示请求已被成功接收、理解、并接受;\n",
    "3. 3xx:重定向,表示需要完成一个或多个附加步骤才能完成请求,自动重定向默认处于打开状态,可以使用 HttpClientHandler.AllowAutoRedirect 或 SocketsHttpHandler.AllowAutoRedirect 进行更改;\n",
    "4. 4xx:客户端错误,表示客户端的请求无效(请求包含语法错误或无法完成请求);\n",
    "5. 5xx:服务器错误,表示服务器在处理请求的过程中发生了错误。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 发出 http 请求"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要发出 HTTP 请求,请调用以下任一 API:\n",
    "\n",
    "| HTTP 方法         | API                          |\n",
    "| ----------------- | ---------------------------- |\n",
    "| `GET`             | HttpClient.GetAsync          |\n",
    "| `GET`             | HttpClient.GetByteArrayAsync |\n",
    "| `GET`             | HttpClient.GetStreamAsync    |\n",
    "| `GET`             | HttpClient.GetStringAsync    |\n",
    "| `POST`            | HttpClient.PostAsync         |\n",
    "| `PUT`             | HttpClient.PutAsync          |\n",
    "| `PATCH`           | HttpClient.PatchAsync        |\n",
    "| `DELETE`          | HttpClient.DeleteAsync       |\n",
    "| †`USER SPECIFIED` | HttpClient.SendAsync         |\n",
    "\n",
    "<br>\n",
    "\n",
    "> †USER SPECIFIED 请求指示 SendAsync 方法接受任何有效的 HttpMethod。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Http 内容"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "HttpContent 类型用于表示 HTTP 实体正文和相应的内容标头。 对于需要正文的 HTTP 方法(或请求方法)POST、PUT 和 PATCH,可使用 HttpContent 类来指定请求的正文。\n",
    "大多数示例演示如何使用 JSON 有效负载准备 StringContent 子类,但还有针对其他内容 (MIME) 类型的其他子类。\n",
    "\n",
    "- ByteArrayContent:提供基于字节数组的 HTTP 内容;\n",
    "- FormUrlEncodedContent:为使用 \"application/x-www-form-urlencoded\" MIME 类型编码的名称/值元组提供 HTTP 内容;\n",
    "- JsonContent:提供基于 JSON 的 HTTP 内容;\n",
    "- MultipartContent:提供使用 \"multipart/*\" MIME 类型规范进行序列化的 HttpContent 对象的集合;\n",
    "- MultipartFormDataContent:为使用 \"multipart/form-data\" MIME 类型进行编码的内容提供容器;\n",
    "- ReadOnlyMemoryContent:提供基于 ReadOnlyMemory<T> 的 HTTP 内容;\n",
    "- StreamContent:提供基于流的 HTTP 内容;\n",
    "- StringContent:提供基于字符串的 HTTP 内容\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3、处理 http 响应"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "python"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "csharp",
    "items": [
     {
      "aliases": [],
      "name": "csharp"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}