{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "source": [
    "# HttpClient 使用请求数据"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "source": [
    "## 0、初始化及全局设置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "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.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",
    "\n",
    "// 引用nuget包和类库文件\n",
    "#r \"nuget:System.Net.Http.Json\"\n",
    "#r \"nuget:Microsoft.Net.Http.Headers\"\n",
    "#r \"nuget:Microsoft.Extensions.Http\"\n",
    "#r \"./Publish/HttpClientStudy.Model/HttpClientStudy.Model.dll\"\n",
    "#r \"./Publish/HttpClientStudy.Core/HttpClientStudy.Core.dll\"\n",
    "\n",
    "//全局引用\n",
    "global using System;\n",
    "global using System.Collections;\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",
    "global using System.Net.Mime;\n",
    "global using System.Net.Http.Json;\n",
    "\n",
    "global using HttpClientStudy.Config;\n",
    "global using HttpClientStudy.Model;\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、使用url 传参"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "参数放在Url里,形如:http://www.baidu.com?name=zhangsan&age=18, GET、Head请求用的比较多。优点是简单、方便,能被浏览器缓存;缺点是参数长度等限制较多,数据暴露url中,可能比较长。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "配置文件根目录:e:\\王高峰\\我的项目\\学习项目\\HttpClientStudy\\Docs\\Publish\\HttpClientStudy.Core\n",
      "{\"data\":{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},\"code\":1,\"message\":\"成功\"}\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"
    },
    {
     "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": [
    "/**\n",
    "    URL传参:适合简单数据类型\n",
    "*/\n",
    "{\n",
    "    //获取响应字符串\n",
    "    {\n",
    "        var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=1\");\n",
    "        response.EnsureSuccessStatusCode();\n",
    "        var content = await response.Content.ReadAsStringAsync();\n",
    "        Console.WriteLine(content);\n",
    "    }\n",
    "\n",
    "    //获取响应对象\n",
    "    {\n",
    "        var response = await SharedClient.GetAsync(\"api/Normal/GetAccount?id=1\");\n",
    "        response.EnsureSuccessStatusCode();\n",
    "\n",
    "        var result = await response.Content.ReadFromJsonAsync<BaseResult<Account>>();\n",
    "        result.Display();\n",
    "    }\n",
    "\n",
    "    //直接获取响应对象\n",
    "    {\n",
    "        var result = await SharedClient.GetFromJsonAsync<BaseResult<Account>>(\"api/Normal/GetAccount?id=1\");\n",
    "        result.Display();\n",
    "    }\n",
    "    \n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2、使用路由传参"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "路由参数,指参数使为URL的一部分,一般由后端API设定好,前端按规定使用即可。例如:后端指定/user/:id,前端使用/user/1,此时id为路由参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    }
   ],
   "source": [
    "/*\n",
    "** 路由参数:参数是URL的一部分,由后端指定\n",
    "*/\n",
    "{//大括号:方便折叠、开成单独的作用域\n",
    "\n",
    "    var response = await SharedClient.GetAsync(\"api/Normal/GetAccount/管理员01\");\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": [
    "## 3、使用请求头"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "把请求数据放入请求头中,传给后端。当然也可以传多个数据。不过,请求头中传数据限制比较多,一般只传简单的数据:比如jwt token 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":{\"id\":1,\"name\":\"管理员01\",\"password\":\"123456\",\"role\":\"Admin\"},\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    }
   ],
   "source": [
    "/*\n",
    "* 请求头: 值必须是字符串,如果有中文等需进行编码\n",
    "*/\n",
    "{\n",
    "    //设置请求头\n",
    "\n",
    "    //中文先编码,服务端接收后要解码\n",
    "    var codedName = System.Net.WebUtility.UrlEncode(\"管理员01\");\n",
    "\n",
    "    //SharedClient是共用的,所以不能多次添加,先移除旧值\n",
    "    if (SharedClient.DefaultRequestHeaders.Contains(\"name\"))\n",
    "    {\n",
    "        SharedClient.DefaultRequestHeaders.Remove(\"name\");\n",
    "    }\n",
    "    \n",
    "    //添加请求头\n",
    "    SharedClient.DefaultRequestHeaders.Add(\"name\", codedName);\n",
    "\n",
    "    var response = await SharedClient.GetAsync(\"api/Normal/GetAccountFromHeader\");\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": [
    "## 4、使用请求体"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "把数据放在请求体中,发送到服务端。可以是简单的字符串,也可以是二进制数据(上传文件)、form表单项、编码过的form表单项、json、流式数据等形式,甚至是这个类型的组合。\n",
    "\n",
    "说下个人的几点理解:\n",
    "1、不管哪种请求体数据,都是放在请求体中,以二进制形式通过网络发往服务器,由服务器接收使用。客户端发送请求数据需要与服务端接收相配合;\n",
    "2、客户端通过Content-Typey请求头设置,告诉服务端请求发送的是哪种类型的数据;服务端根据Content-Type来识别、解析请求数据。常见的类型有:multipart/form-data、application/x-www-form-urlencoded、application/json、text/plain等;\n",
    "3、ASP.NET Core中,默认的请求体类型是FormUrlEncodedContent,所以默认情况下,客户端发送的请求数据是form表单项; asp.net 框架对json格式数据和表单数据进行了特殊处理,支持参数绑定等,可以使用[FromBody]特性,将请求数据绑定到对应的模型上。但普通的文本类型等不支持绑定等功能,需要从请求体中获取原始数据,自行处理。\n",
    "4、一次可发送多种格式数据,由统一的分隔符分隔。服务端可以从请求头[] Content-Type: multipart/mixed; boundary=\"d2e38916-df08-4fec-a40e-3e5179736f32\"]拿到分隔符,然后根据分隔符将数据拆分出来。\n",
    "\n",
    "HttpClient 中,请求体也分为这几种(常见的)类型:\n",
    "1. MultipartFormDataContent\n",
    "2. FormUrlEncodedContent\n",
    "3. JsonContent\n",
    "4. StringContent\n",
    "5. ByteArrayContent\n",
    "6. StreamContent\n",
    "7. MultipartContent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### MultipartFormDataContent :Form表单"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Form表单,提交数据方式之一。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":\"id=1&name=管理员01\",\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    }
   ],
   "source": [
    "/*\n",
    "*   Form表单,提交数据。一般为Put或Post提交。\n",
    "*/\n",
    "{\n",
    "    // 创建一个 MultipartFormDataContent 对象, 用来存入 Form表单 各项及值\n",
    "    var formContent = new MultipartFormDataContent();\n",
    " \n",
    "    // 添加表单字段\n",
    "    formContent.Add(new StringContent(\"1\"), \"id\");\n",
    "    formContent.Add(new StringContent(\"管理员01\"), \"name\");\n",
    " \n",
    "    // 发送POST请求\n",
    "    var response = await SharedClient.PostAsync(\"api/AdvancedGet/PostFormData\", formContent);\n",
    " \n",
    "    // 读取响应内容\n",
    "    string responseString = await response.Content.ReadAsStringAsync();\n",
    " \n",
    "    Console.WriteLine(responseString);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### FormUrlEncodedContent :Form表单 asp.net core 默认接收方式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "/*\n",
    "    \n",
    "*/\n",
    "{\n",
    "\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "source": [
    "### StringContent 普通文本(也包括很多格式,甚至是自定义格式。要和服务器配合)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":\"我是请求体第一段内容\",\"code\":1,\"message\":\"成功\"}\r\n"
     ]
    }
   ],
   "source": [
    "/*\n",
    "    \n",
    "*/\n",
    "{\n",
    "    var requestContent = new StringContent(\"我是请求体第一段内容\", Encoding.UTF8, \"text/plain\");\n",
    "\n",
    "    var response = await SharedClient.PostAsync(\"/api/AdvancedPost/TextData\", requestContent);\n",
    "\n",
    "    var content = await response.Content.ReadAsStringAsync();\n",
    "\n",
    "    Console.WriteLine(content);\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
}