{
 "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": 48,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "## 初始化\n",
       "这是全局共用文件,包括Nuget包引用、全局类库引用、全局文件引用、全局命名空间引用、全局变量、全局方法、全局类定义等功能。\n",
       "\n",
       "在业务笔记中引用,执行其它单元格之前先执行一次。"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Microsoft.Extensions.DependencyInjection, 9.0.3</span></li><li><span>Microsoft.Extensions.Http, 9.0.3</span></li><li><span>Microsoft.Extensions.Http.Polly, 9.0.3</span></li><li><span>Microsoft.Extensions.Logging, 9.0.3</span></li><li><span>Microsoft.Extensions.Logging.Console, 9.0.3</span></li><li><span>Microsoft.Net.Http.Headers, 9.0.3</span></li><li><span>Polly, 8.5.2</span></li><li><span>Refit, 8.0.0</span></li><li><span>Refit.HttpClientFactory, 8.0.0</span></li><li><span>System.Net.Http.Json, 9.0.3</span></li></ul></div></div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
      "配置文件根目录:d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
      "启动WebApi项目...\n",
      "程序[d:\\软件项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.WebApp\\HttpClientStudy.WebApp.exe]已在新的命令行窗口执行。如果未出现新命令行窗口,可能是程序错误造成窗口闪现!\n",
      "已启动WebApi项目,保持窗口打开状态!\n",
      "初始化完成!\n"
     ]
    }
   ],
   "source": [
    "//初始化:必须先执行一次\n",
    "#!import ./ini.ipynb"
   ]
  },
  {
   "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是无状态的请求/响应模式。\n",
    "\n",
    "客户端请求: 请求行(方法 URL 协议版本 回车换行符)、请求头、空行及可选的请求体;\n",
    "\n",
    "![客户端请求消息](./Assets/RequestMessage.png)\n",
    "\n",
    "服务器响应:状态行(协议版本 状态码 状态码描述 回车换行符)、响应头及可选的响应体。\n",
    "\n",
    "![客户端请求消息](./Assets/ResponseMessage.jpg)\n"
   ]
  },
  {
   "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": [
    "### 使用 HttpClient 发出请求"
   ]
  },
  {
   "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": [
    "### Http Get"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "GET 请求不应发送正文,而是用于从资源检索数据。 要在给定 HttpClient 和 URI 的情况下发出 HTTP GET 请求,推荐使用 HttpClient.GetAsync 方法。\n",
    "\n",
    "> Get 请求,一般不会发送数据(服务器支持的话,也能带),最大特点是:可以缓存请求结果,减少服务器压力。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Index\r\n"
     ]
    }
   ],
   "source": [
    "//Get 请求\n",
    "{\n",
    "    //使用共享客户端发送Get请求\n",
    "    var response = await SharedClient.GetAsync(\"/api/hello/index\");\n",
    "\n",
    "    //确保请求成功\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    //读取响应内容\n",
    "    var content = await response.Content.ReadAsStringAsync();\n",
    "\n",
    "    //输出 响应内容\n",
    "    Console.WriteLine(content);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Http Get Json"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "System.Net.Http.Json 库,为请求Json格式数据和接受json格式数据提供了很多扩展方法,使用 HttpClient 发送和请求Json格式数据非常方便;    \n",
    "后面单独章节讲解,这里只是一个例子."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>HttpClientStudy.Model.Account</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>1</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>Hello</pre></div></td></tr><tr><td>Password</td><td><div class=\"dni-plaintext\"><pre>pwd</pre></div></td></tr><tr><td>Role</td><td><div class=\"dni-plaintext\"><pre>Devlopment</pre></div></td></tr></tbody></table></div></details><style>\r\n",
       ".dni-code-hint {\r\n",
       "    font-style: italic;\r\n",
       "    overflow: hidden;\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview {\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview td {\r\n",
       "    vertical-align: top;\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "details.dni-treeview {\r\n",
       "    padding-left: 1em;\r\n",
       "}\r\n",
       "table td {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "table tr { \r\n",
       "    vertical-align: top; \r\n",
       "    margin: 0em 0px;\r\n",
       "}\r\n",
       "table tr td pre \r\n",
       "{ \r\n",
       "    vertical-align: top !important; \r\n",
       "    margin: 0em 0px !important;\r\n",
       "} \r\n",
       "table th {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>HttpClientStudy.Model.Account</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>1</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>Hello</pre></div></td></tr><tr><td>Password</td><td><div class=\"dni-plaintext\"><pre>pwd</pre></div></td></tr><tr><td>Role</td><td><div class=\"dni-plaintext\"><pre>Devlopment</pre></div></td></tr></tbody></table></div></details><style>\r\n",
       ".dni-code-hint {\r\n",
       "    font-style: italic;\r\n",
       "    overflow: hidden;\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview {\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview td {\r\n",
       "    vertical-align: top;\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "details.dni-treeview {\r\n",
       "    padding-left: 1em;\r\n",
       "}\r\n",
       "table td {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "table tr { \r\n",
       "    vertical-align: top; \r\n",
       "    margin: 0em 0px;\r\n",
       "}\r\n",
       "table tr td pre \r\n",
       "{ \r\n",
       "    vertical-align: top !important; \r\n",
       "    margin: 0em 0px !important;\r\n",
       "} \r\n",
       "table th {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>HttpClientStudy.Model.Account</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Id</td><td><div class=\"dni-plaintext\"><pre>0</pre></div></td></tr><tr><td>Name</td><td><div class=\"dni-plaintext\"><pre>&lt;null&gt;</pre></div></td></tr><tr><td>Password</td><td><div class=\"dni-plaintext\"><pre>&lt;null&gt;</pre></div></td></tr><tr><td>Role</td><td><div class=\"dni-plaintext\"><pre>Dev</pre></div></td></tr></tbody></table></div></details><style>\r\n",
       ".dni-code-hint {\r\n",
       "    font-style: italic;\r\n",
       "    overflow: hidden;\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview {\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview td {\r\n",
       "    vertical-align: top;\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "details.dni-treeview {\r\n",
       "    padding-left: 1em;\r\n",
       "}\r\n",
       "table td {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "table tr { \r\n",
       "    vertical-align: top; \r\n",
       "    margin: 0em 0px;\r\n",
       "}\r\n",
       "table tr td pre \r\n",
       "{ \r\n",
       "    vertical-align: top !important; \r\n",
       "    margin: 0em 0px !important;\r\n",
       "} \r\n",
       "table th {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "/*\n",
    "    方式一:使用 System.Net.Http.Json\n",
    "    Get Json\n",
    "    需要先引入 System.Net.Http.Json 和 命名空间(全局设置里已完成)\n",
    "*/\n",
    "{\n",
    "\n",
    "    //使用共享客户端发送Get请求\n",
    "    var content = await SharedClient.GetFromJsonAsync<HttpClientStudy.Model.Account>(\"/api/hello/GetAccount\");\n",
    "\n",
    "    //框架显示方法\n",
    "    content.Display();\n",
    "}\n",
    "\n",
    "/*\n",
    "    方式二:ReadFromJsonAsync 方法,读取响应内容(HttpResonseMessage.Content)\n",
    "*/\n",
    "{\n",
    "    var response = await SharedClient.GetAsync(\"/api/hello/GetAccount\");\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var content = await response.Content.ReadFromJsonAsync<HttpClientStudy.Model.Account>();\n",
    "    content.Display();\n",
    "}\n",
    "\n",
    "/*\n",
    "    方式三:手动序列化读取的响应内容\n",
    "*/\n",
    "{\n",
    "    var response = await SharedClient.GetAsync(\"/api/hello/GetAccount\");\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var content = await response.Content.ReadAsStringAsync();\n",
    "    \n",
    "    var account =  System.Text.Json.JsonSerializer.Deserialize<HttpClientStudy.Model.Account>(content);\n",
    "    account.Display();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTTP Post"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "POST 请求将数据发送到服务器进行处理。 请求的 Content-Type 标头表示正文发送的 MIME 类型。 要在给定 HttpClient 和 Uri 的情况下发出 HTTP POST 请求,请使用 HttpClient.PostAsync 方法.\n",
    "请求数据可选:包括 请求URL、路由、请求头、请求体(包括多种方式,后面详解)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Post Success\n",
      "添加成功\n"
     ]
    }
   ],
   "source": [
    "//不带请求体\n",
    "{\n",
    "    var response = await SharedClient.PostAsync(\"/api/hello/Post\",null);\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var context = await response.Content.ReadAsStringAsync();\n",
    "\n",
    "    Console.WriteLine(context);\n",
    "}\n",
    "\n",
    "//带请求体\n",
    "{\n",
    "    var account = new Account()\n",
    "    {\n",
    "        Id =0,\n",
    "        Name = \"小王\",\n",
    "    };\n",
    "\n",
    "    //请求体有很多:后面专门章节讲解\n",
    "    var requestContent = new StringContent(System.Text.Json.JsonSerializer.Serialize(account), System.Text.Encoding.UTF8, System.Net.Mime.MediaTypeNames.Application.Json);\n",
    "\n",
    "    var response = await SharedClient.PostAsync(\"/api/hello/AddAccount\", requestContent);\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var context = await response.Content.ReadAsStringAsync();\n",
    "\n",
    "    Console.WriteLine(context);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTTP Put"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PUT 请求方法会替换现有资源,或使用请求正文有效负载创建一个新资源。 要在给定 HttpClient 和 URI 的情况下发出 HTTP PUT 请求,请使用 HttpClient.PutAsync 方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":{\"id\":2,\"name\":\"管理员02\",\"password\":\"123456\",\"role\":\"Admin\"},\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>HttpClientStudy.Model.BaseResult`1[HttpClientStudy.Model.Account]</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Data</td><td><div class=\"dni-plaintext\"><pre>HttpClientStudy.Model.BaseResult`1[HttpClientStudy.Model.Account]</pre></div></td></tr><tr><td>Code</td><td><div class=\"dni-plaintext\"><pre>1</pre></div></td></tr><tr><td>Message</td><td><div class=\"dni-plaintext\"><pre>成功</pre></div></td></tr><tr><td>Data</td><td><div class=\"dni-plaintext\"><pre>HttpClientStudy.Model.BaseResult`1[HttpClientStudy.Model.Account]</pre></div></td></tr></tbody></table></div></details><style>\r\n",
       ".dni-code-hint {\r\n",
       "    font-style: italic;\r\n",
       "    overflow: hidden;\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview {\r\n",
       "    white-space: nowrap;\r\n",
       "}\r\n",
       ".dni-treeview td {\r\n",
       "    vertical-align: top;\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "details.dni-treeview {\r\n",
       "    padding-left: 1em;\r\n",
       "}\r\n",
       "table td {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "table tr { \r\n",
       "    vertical-align: top; \r\n",
       "    margin: 0em 0px;\r\n",
       "}\r\n",
       "table tr td pre \r\n",
       "{ \r\n",
       "    vertical-align: top !important; \r\n",
       "    margin: 0em 0px !important;\r\n",
       "} \r\n",
       "table th {\r\n",
       "    text-align: start;\r\n",
       "}\r\n",
       "</style>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "/* Put请求, 一般是添加或修改资源:需要数据。\n",
    "   这里故意使用查询,没有资源:\n",
    "   意在说明:Get、Post、Put等谓词的使用,只是行业规范(有些服务器做了特殊处理),不具有强制性。并且,从http协议上说,没有本质区别。      \n",
    "*/\n",
    "{\n",
    "    var response = await SharedClient.PutAsync(\"/api/Normal/PutDemo?id=2\", null);\n",
    "\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var content = await response.Content.ReadAsStringAsync();\n",
    "    Console.WriteLine(content);\n",
    "\n",
    "    //或者直接获取对象\n",
    "    var result = await response.Content.ReadFromJsonAsync<BaseResult<Account>>();\n",
    "    result.Display();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTTP Head"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "HEAD 请求类似于请求 GET。 它只返回与资源关联的标头,而不返回资源。 对 HEAD 请求的响应不会返回正文。  \n",
    "要在给定 HttpClient 和 URI 的情况下发出 HTTP HEAD 请求,请使用 HttpClient.SendAsync 方法并将 HttpMethod 设置为 HttpMethod.Head \n",
    "注意:系统没有提供单独的Head方法,要使用通用的Send或者SendAsync"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "无响应体\r\n"
     ]
    }
   ],
   "source": [
    "//Head请求,借助SendAsync方法\n",
    "{\n",
    "    var requestMessage = new HttpRequestMessage(HttpMethod.Head, \"/api/Normal/HeadDemo?id=2\");\n",
    "\n",
    "    var response = await SharedClient.SendAsync(requestMessage);\n",
    "\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    //Head请求,没有响应内容。\n",
    "    var content = await response.Content.ReadAsStringAsync();\n",
    "    Console.WriteLine(response.Content.Headers.ContentLength > 0 ? \"有响应体\":\"无响应体\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTTP Patch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PATCH 请求是对现有资源执行部分更新。 它不会创建新资源,也不会替换现有资源。 而是只更新部分资源。 要在给定 HttpClient 和 URI 的情况下发出 HTTP PATCH 请求,请使用 HttpClient.PatchAsync 方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":{\"id\":1,\"name\":\"Patch更新\",\"password\":\"123456\",\"role\":\"Admin\"},\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    }
   ],
   "source": [
    "//Patch请求\n",
    "{\n",
    "    var account = new Account()\n",
    "    {\n",
    "        Id = 1,\n",
    "        Name = \"Patch更新\"\n",
    "    };\n",
    "\n",
    "    StringContent jsonContent = new(System.Text.Json.JsonSerializer.Serialize(account), Encoding.UTF8, System.Net.Mime.MediaTypeNames.Application.JsonPatch);\n",
    "\n",
    "    HttpResponseMessage response = await SharedClient.PatchAsync( \"api/Normal/PatchDemo\", jsonContent);\n",
    "\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var context = await response.Content.ReadAsStringAsync();\n",
    "    Console.WriteLine($\"{context}\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTTP Delete"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "DELETE 请求会删除现有资源。 DELETE 请求是幂等的但不是安全的,这意味着对同一资源发出的多个 DELETE 请求会产生相同的结果,但该请求会影响资源的状态。 要在给定 HttpClient 和 URI 的情况下发出 HTTP DELETE 请求,请使用 HttpClient.DeleteAsync 方法."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":1,\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    }
   ],
   "source": [
    "//Delete 删除资源\n",
    "{\n",
    "    HttpResponseMessage response = await SharedClient.DeleteAsync( \"api/Normal/Delete?id=1\");\n",
    "\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var context = await response.Content.ReadAsStringAsync();\n",
    "    Console.WriteLine($\"{context}\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTTP Options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OPTIONS 请求用于标识服务器或终结点支持哪些 HTTP 方法。 要在给定 HttpClient 和 URI 的情况下发出 HTTP OPTIONS 请求,请使用 HttpClient.SendAsync 方法并将 HttpMethod 设置为 HttpMethod.Options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":\"简单OPTIONS请求\",\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    }
   ],
   "source": [
    "//Options 预检请求\n",
    "{\n",
    "    var requestMessage = new HttpRequestMessage(HttpMethod.Options, \"api/Normal/OptionsSimple\");\n",
    "\n",
    "    HttpResponseMessage response = await SharedClient.SendAsync(requestMessage);\n",
    "\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var context = await response.Content.ReadAsStringAsync();\n",
    "    Console.WriteLine($\"{context}\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTTP Trace"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TRACE 请求可用于调试,因为它提供请求消息的应用程序级环回。 要发出 HTTP TRACE 请求,请使用 HttpMethod.Trace 创建 HttpRequestMessage\n",
    "特别注意:ASP.NET WebApi 并不直接支持 Trace 请求;一个简单方法是使用一个专用的中间件,来支持所有接口的Trace请求.(需要服务器支持)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我是请求内容\r\n"
     ]
    }
   ],
   "source": [
    "//trace 服务器已设置专门中间件支持所有接口的Trace请求\n",
    "{\n",
    "    var requestMessage = new HttpRequestMessage(HttpMethod.Trace,\"api/Normal/TraceDemo?accountId=1\");\n",
    "    requestMessage.Content = new StringContent(\"我是请求内容\",Encoding.UTF8,System.Net.Mime.MediaTypeNames.Text.Plain);\n",
    "\n",
    "    var response = await SharedClient.SendAsync(requestMessage);\n",
    "\n",
    "    response.EnsureSuccessStatusCode();\n",
    "\n",
    "    var context = await response.Content.ReadAsStringAsync();\n",
    "\n",
    "    Console.WriteLine(context);\n",
    "}"
   ]
  }
 ],
 "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
}