diff --git a/Docs/1.3.1.基础使用.发送请求.ipynb b/Docs/1.3.1.基础使用.发送请求.ipynb index 188f335..353db7d 100644 --- a/Docs/1.3.1.基础使用.发送请求.ipynb +++ b/Docs/1.3.1.基础使用.发送请求.ipynb @@ -69,6 +69,7 @@ "global using Microsoft.Extensions.DependencyInjection.Extensions;\n", "\n", "global using HttpClientStudy.Config;\n", + "global using HttpClientStudy.Model;\n", "global using HttpClientStudy.Core;\n", "global using HttpClientStudy.Core.Utilities;\n", "\n", @@ -262,6 +263,12 @@ "cell_type": "code", "execution_count": null, "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, "vscode": { "languageId": "polyglot-notebook" } @@ -288,7 +295,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Http Get Json" + "## Http Get Json" ] }, { @@ -303,6 +310,12 @@ "cell_type": "code", "execution_count": null, "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, "vscode": { "languageId": "polyglot-notebook" } @@ -310,6 +323,7 @@ "outputs": [], "source": [ "/*\n", + " 方式一:使用 System.Net.Http.Json\n", " Get Json\n", " 需要先引入 System.Net.Http.Json 和 命名空间(全局设置里已完成)\n", "*/\n", @@ -320,6 +334,340 @@ "\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();\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(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": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "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": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "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>();\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": null, + "metadata": { + "dotnet_interactive": { + "language": "csharp" + }, + "polyglot_notebook": { + "kernelName": "csharp" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "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": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "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": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "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": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "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": null, + "metadata": { + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "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", "}" ] } diff --git a/HttpClientStudy.WebApp/Controllers/HelloController .cs b/HttpClientStudy.WebApp/Controllers/HelloController .cs index 23e8f5f..d6ea8b3 100644 --- a/HttpClientStudy.WebApp/Controllers/HelloController .cs +++ b/HttpClientStudy.WebApp/Controllers/HelloController .cs @@ -78,7 +78,17 @@ namespace HttpClientStudy.WebApp.Controllers [HttpPost] public IActionResult Post() { - return Ok("Hello, world!"); + return Ok("Post Success"); + } + + /// + /// Post 请求:添加账号 + /// + /// + [HttpPost] + public IActionResult AddAccount([FromBody] Account vm) + { + return Ok("添加成功"); } } } diff --git a/HttpClientStudy.WebApp/Controllers/NormalController.cs b/HttpClientStudy.WebApp/Controllers/NormalController.cs index c73630c..459a308 100644 --- a/HttpClientStudy.WebApp/Controllers/NormalController.cs +++ b/HttpClientStudy.WebApp/Controllers/NormalController.cs @@ -264,14 +264,55 @@ namespace HttpClientStudy.WebApp.Controllers BaseResult result = BaseResultUtil.Success("简单OPTIONS请求"); //这样丢失内容协商,只会返回JSON数据 - return new JsonResult(result); + return Ok(result); } #endregion #region PATCH + + /// + /// 部分更新账号 + /// + /// 更新账号 + /// + [HttpPatch] + public IActionResult PatchDemo([FromBody]Account vm) + { + var account = _accountService.GetAllAccounts().FirstOrDefault(a => a.Id == vm.Id); + + if (account == null) + { + return BadRequest("要更新的账号不存在!"); + } + + //部分更新 + account.Name = vm.Name; + + var result = BaseResultUtil.Success(account); + + return Ok(result); + } #endregion - #region TRACE + #region TRACE 注意:.NET 不提供直接Trace请求支持,但可以使用单独中间件提供此功能 + + /// + /// 获取指定账号 + /// + /// 账号Id + /// + [HttpGet] + public IActionResult TraceDemo(int accountId) + { + var account = _accountService.GetAllAccounts().FirstOrDefault(a => a.Id == accountId); + + if (account == null) + { + return BadRequest("要更新的账号不存在!"); + } + + return Ok(account); + } #endregion #region CONNECT diff --git a/HttpClientStudy.WebApp/Program.cs b/HttpClientStudy.WebApp/Program.cs index 96b93d2..a1331ba 100644 --- a/HttpClientStudy.WebApp/Program.cs +++ b/HttpClientStudy.WebApp/Program.cs @@ -13,6 +13,7 @@ using Microsoft.OpenApi.Models; using HttpClientStudy.Model; using HttpClientStudy.Service; using HttpClientStudy.Config; +using Microsoft.AspNetCore.Mvc; namespace HttpClientStudy.WebApp { @@ -56,7 +57,10 @@ namespace HttpClientStudy.WebApp #region 向容器注册服务 - builder.Services.AddControllers(); + builder.Services.AddControllers(mvcOption => + { + + }); builder.Services.AddEndpointsApiExplorer(); //Session中间件依赖项 @@ -209,6 +213,7 @@ namespace HttpClientStudy.WebApp //耗时统计中间件 app.UseMiddleware(); + //健康检查中间件 app.MapHealthChecks("api/health"); app.UseSwagger(); @@ -236,6 +241,25 @@ namespace HttpClientStudy.WebApp await Task.CompletedTask; }); + // 自定义中间件来处理 TRACE 请求 + app.Use(async (context, next) => + { + if (context.Request.Method == HttpMethods.Trace) + { + // 设置响应的内容类型为 HTTP 消息格式 + context.Response.ContentType = "message/http"; + context.Response.StatusCode = StatusCodes.Status200OK; + + // 返回请求的详细信息(包括头和内容) + await context.Request.Body.CopyToAsync(context.Response.Body); + } + else + { + await next(); + } + }); + + app.Run(); } }