.NetCore WebAPI采坑之路(持续更新) - 长沙大鹏 - 博客园

来源: .NetCore WebAPI采坑之路(持续更新) – 长沙大鹏 – 博客园

1、WebAPI新增日志过滤器or中间件后Action读取到的请求Body为空问题

案例:

自定义了一个中间件,用于记录每次访问webapi的入参,以及引用了Swagger。

先看下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
       {
           if (env.IsDevelopment())
           {
               app.UseDeveloperExceptionPage();
           }
           else
           {
               app.UseHsts();
           }
           app.UseMvc();
           //各种中间件 
           app.UseVisitLogger();
           app.UsAirExceptionHandler();
           app.UseHttpsRedirection();
           //SignalR
           app.UseSignalR(routes =>
           {
               routes.MapHub<TriageHub>("/triage", (options) =>
               {
                   //指定采取WebSoket协议进行双工通讯
                   options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
               });
           });
      
           //swagger
           app.UseSwagger();
           app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json""TestWebAPI"));
       }

上面这段代码看上去是不是人畜无害,没啥问题。 恩,F5启动后,我们试下执行post方式的webapi。执行结果是ok的,但是UseVisitLogger中间件里的

日志记录却没有记录webapi的访问日志。

我们把Startup下的Configure方法改成下面这种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
     {
         if (env.IsDevelopment())
         {
             app.UseDeveloperExceptionPage();
         }
         else
         {
             app.UseHsts();
         }      
         //各种中间件 
         app.UseVisitLogger();
         app.UsAirExceptionHandler();
         app.UseHttpsRedirection();
         //SignalR
         app.UseSignalR(routes =>
         {
             routes.MapHub<TriageHub>("/triage", (options) =>
             {
                 //指定采取WebSoket协议进行双工通讯
                 options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
             });
         });
         app.UseMvc();
         //swagger
         app.UseSwagger();
         app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json""TestWebAPI"));
     }

我们把UseMvc()放置 各类中间件的后面,再来试下webapi的post方式(post的入参是一个模型,不是基础数据类型!)

 

执行结果如上图。。。这一次api执行失败,但是日志记录成功了,如下图:

哎。。。是不是很坑。。目前先记录下,还要想办法怎么去解决

———————————————————————解决新增自定义中间件后执行带模型Post请求400问题———————————-

原因: 因为自定义中间件里有一个是写日志的,会将HttpContext对象的Request.Body对象(一个Steam对象)的读取位置改变。原来的日志中间件是这样写的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public async Task Invoke(HttpContext context)
  {
      visitLog = new VisitLog();
      HttpRequest request = context.Request;
      visitLog.Url = request.Path.ToString();
      visitLog.Headers = request.Headers.ToDictionary(k => k.Key, v => string.Join(";", v.Value.ToList()));
      visitLog.Method = request.Method;
      visitLog.ExcuteStartTime = DateTime.Now;
      using (StreamReader reader = new StreamReader(request.Body))
      {
          visitLog.RequestBody = reader.ReadToEnd();
      }
      context.Response.OnCompleted(ResponseCompletedCallback, context);
      await _next(context);
  }

上面的 requst.Body 在通过ReadToEnd后实际上的context.Request.Body的读取位置已经是最后了,在后续的中间件接收到的context因为读取位置不是0而是内容的

长度,所以怎么都读不到数据。这就是为什么post请求会出现400错误,因为到action那里的时候确实Requst.Body的内容他啥也没读到啊!!

现在找到了问题所在就好处理了,处理方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public async Task Invoke(HttpContext context)
      {
          visitLog = new VisitLog();
          HttpRequest request = context.Request;
          visitLog.Url = request.Path.ToString();
          visitLog.Headers = request.Headers.ToDictionary(k => k.Key, v => string.Join(";", v.Value.ToList()));
          visitLog.Method = request.Method;
          visitLog.ExcuteStartTime = DateTime.Now;
          var originalRequestBody = request.Body;
          var requestBodyStream = new MemoryStream();
          await request.Body.CopyToAsync(requestBodyStream);
          //设置当前流的位置未0
          requestBodyStream.Seek(0, SeekOrigin.Begin);
          //这里ReadToEnd执行完毕后requestBodyStream流的位置会从0到最后位置(即request.ContentLength)
          visitLog.RequestBody = new StreamReader(requestBodyStream).ReadToEnd();
          //需要将流位置重置偏移到0,不然后续的action读取不到request.Content的值
          requestBodyStream.Seek(0, SeekOrigin.Begin);
          context.Response.OnCompleted(ResponseCompletedCallback, context);
          context.Request.Body = requestBodyStream;
          await _next(context);
          context.Request.Body = originalRequestBody;
      }

好啦,我们把流的读取位置重新偏移下就好了!

 2、.NetCore项目智能提示英文更改为中文的方法

.NetCore下载后,在VS2017开发时候,智能提示是下图这样的:

很不爽是吧,尤其是我们这种英文渣。解决方案呢其实就是需要将框架里的Nuget包的注释xml文件替换成中文版的(至少我是这么做的,其他办法未知..)

首先从C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5.1 这个目录下将zh-Hans文件夹下的所有文件copy到

C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1目录下。 这里我是因为用的.NetCore2.1,如果

是2.0则将目录里的2.1更改成2.0。 然后重新打开VS2017就可以发现智能提示是中文啦~~~~

 3、字符集GB2312引用错误处理

在对csv文件读取时候乱码,后来发现.NetCore需要安装System.Text.Encoding.CodePages这个Nuget包,然后在类的构造函数里加上这一段代码System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance)

 4、Linux上部署.NetCore站点端口号与launchSettings.json设置不一致的解决办法

在Linux上部署站点绝对会出现这个问题,除非你的端口号恰巧就和默认的站点一致。不然你在launchSettings.json上的端口设置好后发现在Linux上启动你的应用后监听的端口不一致。

如何解决呢?没办法,在dotnet命令后加urls参数。如:

1
dotnet HY.Admin.Host.dll --urls http://*:5001

上面就是指定了端口5001,结果如下:

 

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏