当前位置: 移动技术网 > IT编程>开发语言>.net > Asp.Net Core控制器如何接收原始请求正文内容详解

Asp.Net Core控制器如何接收原始请求正文内容详解

2018年09月21日  | 移动技术网IT编程  | 我要评论

美人心计 下载,三星f218拆机,无人驾驶好看吗

主要目标

在asp.net core控制器中,通过自定义格式化程序来映射自定义处理控制器中的“未知”内容。本文将给大家详细介绍关于asp.net core控制器接收原始请求正文内容的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

简单案例

为了演示这个问题,我们用vs2017创建一个默认的asp.net core web api项目。

 [route("api/[controller]")]
 [apicontroller]
 public class valuescontroller : controllerbase{
 [httpget]
 public actionresult<string> get() {
  return "ok";
 }

 [httppost]
 [route("postx")]
 public actionresult<string> post([frombody] string value)
 {
  return value;
 }
 }

json请求

我们从最常见的json输入请求开始。

user-agent: fiddler
host: localhost:5000
content-type: application/json
content-length: 16 

请求body:

{"123456"}

通过后台调试和fiddler抓包,我们可以看到请求输入和返回。

后台调试,查看请求输入结果

fiddler查看请求header

fiddler查看返回结果

注意!!

别忘了[frombody],有时候会忘的。
后台action接收类型为string的时候,请求body只能是字符串,不能传json对象。我演示这个例子时,被这点坑了。如果接收对象是一个类的时候,才可以传json对象。

没有json

虽然传输json数据是最常用的,但有时候我们需要支持普通的文本或者二进制信息。我们将content-type改为
text/plain

user-agent: fiddler
host: localhost:5000
content-type:text/plain
content-length: 16 

请求body:

{"123456"}

悲剧的事情来,报404!


不支持text/plain

事情到此就变得稍微复杂了一些,因为asp.netcore只处理它认识的类型,如json和formdata。默认情况下,原始数据不能直接映射到控制器参数。这是个小坑,不知你踩到过没有?仔细想想,这是有道理的。mvc具有特定内容类型的映射,如果您传递的数据不符合这些内容类型,则无法转换数据,因此它假定没有匹配的端点可以处理请求。
那么怎么支持原始的请求映射呢?

支持原始正文请求

不幸的是,asp.net core不允许您仅通过方法参数以任何有意义的方式捕获“原始”数据。无论如何,您需要对其进行一些自定义处理request.body以获取原始数据,然后对其进行反序列化。

您可以捕获原始数据request.body并从中直接读取原始缓冲区。

最简单,最不易侵入,但不那么明显的方法是使用一个方法接受没有参数的 post或put数据,然后从request.body以下位置读取原始数据:

读取字符串缓冲区

 [httppost]
 [route("posttext")]
 public async task<string> posttext()
 {
  using (streamreader reader = new streamreader(request.body, encoding.utf8))
  {
  return await reader.readtoendasync();
  }
 }

这适用于一下http和文本

user-agent: fiddler
host: localhost:5000
content-type: text/plain
content-length: 6

要读取二进制数据,你可以使用以下内容:

读取byte缓冲区

 [httppost]
 [route("postbinary")]
 public async task<byte[]> postbinary()
 {
  using (var ms = new memorystream(2048))
  {
  await request.body.copytoasync(ms);
  return ms.toarray(); // returns base64 encoded string json result
  }
 }

查看执行结果

接收文本内容

接收二进制数据

httprequest静态扩展

如果你为了方便,写了很多httprequest的扩展,接收参数时,可以看起来更简洁一些。

public static class httprequestextension
 {
 /// <summary>
 /// 
 /// </summary>
 /// <param name="httprequest"></param>
 /// <param name="encoding"></param>
 /// <returns></returns>
 public static async task<string> getrawbodystringformater(this httprequest httprequest, encoding encoding)
 {
  if (encoding == null)
  {
  encoding = encoding.utf8;
  }

  using (streamreader reader = new streamreader(httprequest.body, encoding))
  {
  return await reader.readtoendasync();
  }
 }
 /// <summary>
 /// 二进制
 /// </summary>
 /// <param name="httprequest"></param>
 /// <param name="encoding"></param>
 /// <returns></returns>
 public static async task<byte[]> getrawbodybinaryformater(this httprequest httprequest, encoding encoding)
 {
  if (encoding == null)
  {
  encoding = encoding.utf8;
  }

  using (streamreader reader = new streamreader(httprequest.body, encoding))
  {
  using (var ms = new memorystream(2048))
  {
   await httprequest.body.copytoasync(ms);
   return ms.toarray(); // returns base64 encoded string json result
  }
  }
 }
 }
 [httppost]
 [route("posttextx")]
 public async task<string> posttextx()
 {
  return await request.getrawbodystringasyn();
 }
 /// <summary>
 /// 接收
 /// </summary>
 /// <returns></returns>
 [httppost]
 [route("postbinaryx")]
 public async task<byte[]> postbinaryx()
 {
  return await request.getrawbodybinaryasyn();
 }

自动转换文本和二进制值

上面虽然解决了原始参数转换问题,但不够友好。如果你打算像原生mvc那样自动映射参数的话,你需要做一些自定义格式化适配。

创建一个asp.net mvc inputformatter

asp.net core使用一种干净且更通用的方式来处理内容的自定义格式inputformatter。输入格式化程序挂钩到请求处理管道,让您查看特定类型的内容以确定是否要处理它。然后,您可以阅读请求正文并对入站内容执行自己的反序列化。

inputformatter有几个要求

  • 您需要使用[frombody]去获取
  • 您必须能够查看请求并确定是否以及如何处理内容。

在这个例子中,对于“原始内容”,我想查看具有以下类型的请求:

  • text/plain(文本)
  • appliaction/octet-stream(byte[])
    没有内容类型(string)

要创建格式化程序,你可以实现iinputformatter或者从inputformatter继承。

 public class rawrequestbodyformatter : iinputformatter
 {
  public rawrequestbodyformatter()
  {

  }

  public bool canread(inputformattercontext context)
  {
   if (context == null) throw new argumentnullexception("argument is null");
   var contenttype = context.httpcontext.request.contenttype;
   if (string.isnullorempty(contenttype) || contenttype == "text/plain" || contenttype == "application/octet-stream")
    return true;
   return false;
  }

  public async task<inputformatterresult> readasync(inputformattercontext context)
  {
   var request = context.httpcontext.request;
   var contenttype = context.httpcontext.request.contenttype;
   if (string.isnullorempty(contenttype) || contenttype.tolower() == "text/plain")
   {
    using (streamreader reader = new streamreader(request.body, encoding.utf8))
    {
     var content = await reader.readtoendasync();
     return await inputformatterresult.successasync(content);
    }
   }
   if (contenttype == "application/octet-stream")
   {
    using (streamreader reader = new streamreader(request.body, encoding.utf8))
    {
     using (var ms = new memorystream(2048))
     {
      await request.body.copytoasync(ms);
      var content = ms.toarray();

      return await inputformatterresult.successasync(content);
     }
    }
   }
   return await inputformatterresult.failureasync();
  }
 }

格式化程序用于canread()检查对内容类型的请求以支持,然后将readrequestbodyasync()内容读取和反序列化为应在控制器方法的参数中返回的结果类型。

inputformatter必须在configureservices()启动代码中注册mvc :

 public void configureservices(iservicecollection services)
  {
   services.addmvc(o=>o.inputformatters.insert(0,new rawrequestbodyformatter())).setcompatibilityversion(compatibilityversion.version_2_1);
  }

接受原始输入

  [httppost]
  [route("posttextplus")]
  public string posttextplus([frombody] string value)
  {
   return value;
  }

然后你就可以发送post请求,像这样:

user-agent: fiddler
host: localhost:5000
content-length: 6

或者

user-agent: fiddler
host: localhost:5000
content-type:text/plain
content-length: 6

请注意,您可以使用内容类型调用相同的控制器方法application/json并传递json字符串,这也将起作用。在rawrequestbodyformatter 简单地增加它支持的附加内容类型的支持。

二进制数据

  [httppost]
  [route("postbinaryplus")]
  public byte[] postbinaryplus([frombody] byte[] value)
  {
   return value;
  }

请求内容如下:

user-agent: fiddler
host: localhost:5000
content-length: 6
content-type: application/octet-stream

源代码

示例代码已上传到 csharpfandemo ()

参考链接

本文包含翻译和自己实践。主要思路和代码来源于以下链接:
accepting raw request body content in asp.net core api controllers

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网