【问题标题】:Read Request Body in ASP.NET在 ASP.NET 中读取请求正文
【发布时间】:2011-09-01 18:09:04
【问题描述】:

如何在 ASP.NET 中读取请求正文?我正在使用 Firefox 的 REST 客户端插件在我在本地托管的网站上形成对资源的 GET 请求,在请求正文中,我只是将字符串“test”放在上面以尝试阅读它服务器。

在服务器代码中(这是一个非常简单的 MVC 操作)我有这个:

var reader = new StreamReader(Request.InputStream);
var inputString = reader.ReadToEnd();

但是当我调试它时,inputString 总是空的。我不确定如何(例如在 FireBug 中)确认请求正文确实被正确发送,我想我只是假设附加组件正确地执行了该操作。也许我读错了值?

【问题讨论】:

  • 通过请求正文,您真的是指正文 - 还是包括标头在内的所有内容?
  • @Adam Tuliper:身体。我没有针对此请求的任何自定义标头或类似的特殊标头。最终目标是能够将 ID 列表传递给 RESTful 资源。作为路由本身的一部分,传递单个 ID 很容易,但我不确定如何传递 ID 列表。尽管我当然对我在这里走错路的可能性持开放态度。 (也就是说,正文中需要一个 XML 字符串。)
  • 你将如何在 GET 请求中执行此操作?这将在查询字符串中,否则它将是一个发布请求,不是吗?我同意下面的 Stripling 的观点。

标签: asp.net rest httpwebrequest


【解决方案1】:

也许我记错了我的学业,但我认为 GET 请求实际上并没有 实体。 This page states.

HTML 规范在技术上定义了“GET”和“POST”之间的区别,前者意味着表单数据将被(通过浏览器)编码为 URL,而后者意味着表单数据将出现在一个 URL 中。消息正文。

所以也许你做的事情是正确的,但你必须 POST 数据才能拥有消息正文?

更新

针对您的评论,最“正确”的 RESTful 方式是将每个值作为其自己的参数发送:

site.com/MyController/MyAction?id=1&id=2&id=3...

如果你给它一个同名的数组参数,你的动作将自动绑定这些:

public ActionResult MyAction(int[] id) {...}

或者,如果您是受虐狂,您可以尝试一次从Request.QueryString 中提取值。

【讨论】:

  • 这很有趣。这绝对是有道理的。但是接下来的问题会变成,“什么是正确的 RESTful 方式来执行请求以 get 一批指定项目?”也就是说,我需要将一个 ID 列表传递给资源,以指定我想要检索的项目列表。在 URL 上一次一个很容易,但我不想一次访问该服务数千次。
  • 很公平。我想我希望我可以从“干净的 URL”角度避免查询字符串参数。这个想法是,该服务将在 API 可用和人类可用之间取得某种平衡,我想知道我们是否会给消费客户端开发人员带来过度的负担,让他们拥有这样的查询字符串。
  • 另外,我突然想到,当我们谈论超过 1,000 个 ID 时,这很容易达到请求长度边界。我想我需要重新考虑一下……
  • @David:是的。即使没有官方的最大请求长度 (boutell.com/newfaq/misc/urllength.html),如果 URL 超过一千个字符,您可能没有遵循良好的做法。您可能会考虑使用 OData 的 URI 约定 (odata.org/developers/protocols/uri-conventions) 之类的东西,这样您就可以根据条件和跳过/获取参数请求批次,而不是通过 ID 显式请求项目。
【解决方案2】:

我最近想起了这个老问题,并想根据我自己工作中最近的实现添加另一个完整的答案。

供参考,最近I've blogged on the subject

本质上,这个问题的核心是,“如何将更大、更复杂的搜索条件传递给资源,以获取经过过滤的对象列表?”它最终归结为两个选择:

  1. 一堆GET查询字符串参数
  2. 请求正文中带有 DTO 的 POST

第一个选项并不理想,因为实现起来很丑陋,而且 URL 在某些时候可能会超过最大长度。第二个选项虽然很实用,但在“RESTful”的意义上并不适合我。毕竟,我正在获取数据,对吧?

但是,请记住,我不是只是获取数据。我正在创建一个对象列表。每个对象都已经存在,但 list 本身并不存在。这是一个全新的东西,通过向服务器上的完整对象存储库发出搜索/过滤条件来创建。 (毕竟,请记住,对象集合本身仍然是一个对象。)

这是一个纯粹的语义差异,但绝对重要。因为,在最简单的情况下,这意味着我可以轻松地使用 POST 将这些搜索条件发布到服务器。响应是我收到的数据,所以我正在“获取”数据。但从某种意义上说,我并不是在“获取”数据,因为我实际上是在执行一种创建行为,即创建恰好由预先存在的元素组成的对象列表的新实例。

我完全承认,限制从来都不是技术上的,它只是语义上的。它只是从来没有和我“坐好”。非技术问题需要非技术解决方案,在这种情况下是语义解决方案。从稍微不同的语义角度看待问题会产生一个更简洁的解决方案,这恰好是我最终使用的解决方案。

【讨论】:

    【解决方案3】:

    除了 GET/POST 问题,我确实发现您需要将 Request.InputStream 位置设置回开始。感谢this answer我找到了。

    特别是评论

    Request.InputStream // 确保在读取后重置位置,否则以后读取可能会失败

    我翻译成的

    Request.InputStream.Seek(0,0)
    

    【讨论】:

      【解决方案4】:

      我会尝试使用 HttpClient(可通过 Nuget 获得)来执行此类操作。它比 System.Net 对象容易得多

      【讨论】:

        【解决方案5】:

        直接从 Request.InputStream 读取很危险,因为即使数据存在,重新读取时也会得到 null。这在实践中得到验证。 可靠读取执行如下:

        /*Returns a string representing the content of the body 
        of the HTTP-request.*/
        public static string GetFromBodyString(this HttpRequestBase request)
        {
            string result = string.Empty;
        
            if (request == null || request.InputStream == null)
                return result;
        
            request.InputStream.Position = 0;
        
            /*create a new thread in the memory to save the original 
            source form as may be required to read many of the 
            body of the current HTTP- request*/
            using (MemoryStream memoryStream = new MemoryStream())
            {
                request.InputStream.CopyToMemoryStream(memoryStream);
                using (StreamReader streamReader = new StreamReader(memoryStream))
                {
                    result = streamReader.ReadToEnd();
                }
            }
            return result;
        }
        
        /*Copies bytes from the given stream MemoryStream and writes 
        them to another stream.*/
        public static void CopyToMemoryStream(this Stream source, MemoryStream destination)
        {
            if (source.CanSeek)
            {
                int pos = (int)destination.Position;
                int length = (int)(source.Length - source.Position) + pos;
                destination.SetLength(length);
        
                while (pos < length)
                    pos += source.Read(destination.GetBuffer(), pos, length - pos);
            }
            else
                source.CopyTo((Stream)destination);
        }
        

        【讨论】:

        • request.InputStream 始终不为空,您可以测试的是request.InputStream.CanRead &amp;&amp; request.InputStream.CanSeek,因此您可以安全地将光标重新定位在流中,稍后可以读取它...
        猜你喜欢
        • 1970-01-01
        • 2020-03-23
        • 2019-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-13
        相关资源
        最近更新 更多