【问题标题】:C# MVC Instant Response to Browser?C# MVC 对浏览器的即时响应?
【发布时间】:2010-11-05 20:51:52
【问题描述】:

再次向 MVC 新手提问。请原谅我缺乏经验。

我有一种情况,我正在使用 MVC 路由返回一个大型 XML 文件。有时它可能非常大。目前,我正在使用 StringBuilder 来构建我想要的 XML 输出,然后像这样返回它:

var sb = new StringBuilder();
XmlObject.WriteXml(sb);
return Content(sb.ToString(), "text/xml", Encoding.UTF8);

我遇到的是(由于各种原因)XML 博客可能需要很长时间才能生成。

在 XmlObject.WriteXml() 方法中调用了大量其他小方法,这些小方法会在调用时输出 XML 的点点滴滴,因此我立即开始构建 XML 字符串,只需要一段时间即可完成。这些方法中的每一个都接受一个 StringBuilder 作为参数,因此可以创建一个,然后将其传递到各处,在每个小方法中使用 sb.Append() 来构建最终的 XML blob。

好的,所以我想做的是在字符串开始构建后立即开始向客户端返回一些内容。在 Webforms 中,我会用 HttpResponse 替换所有 StringBuilder 参数并使用 HttpResponse.Write() 而不是 StringBuilder.Append(),其方式类似于:

this.Response.BufferOutput = false;
XmlObject.WriteXml(Response);

然后,随着每一小段 XML 写入响应,文本将被发送到客户端。

我遇到的问题是 ActionResult 必须有一个 return 语句。我不知道如何使用 MVC 路由和 ActionResult 进行类似的处理。也许我需要使用 ActionResult 以外的东西?

谢谢大家!

【问题讨论】:

标签: asp.net-mvc asp.net-mvc-routing httpresponse actionresult


【解决方案1】:

您可以更改您的操作方法以返回void,然后直接写入响应。
(这是可能的;我试过了)

【讨论】:

  • 有点疯狂,肯定会破坏封装,但考虑到其他选项(例如自定义 IHttpHandler ),这并不是一个可怕的想法。 . .
【解决方案2】:

如果它不需要是 MVC 操作的结果,并且您不想破坏那里的可测试性/封装性,您可以按照我们一直做的方式来做——使用直接流入的自定义 IHttpHandler回应。

更新

示例可能没有意义——您的问题中包含了这个概念。唯一的转折是跳过 StringBuilder 并使用 XmlTextWriter 设置流式传输到 Response.OutputStream 以跳过中间人。

也就是说,既然我正在阅读您在做什么,我个人会采取“离线构建并合理缓存”的角度,而不是担心流式传输。处理罐头可能比处理活的更容易。

【讨论】:

  • 如果你身边有一个可以剪下来放在这里的例子,我会很高兴看到这样的例子。
  • 谢谢你的想法......我会试着沿着这条路走一点路。
  • 成功了。该死的快,现在,谢谢!我最终在自定义处理程序中使用了 XmlTextWriter,并缓存了输出:)。 cpuboards.com/sitemap-cpu-boards.xml。谢谢!
【解决方案3】:

使用StringBuilder 构建大量 XML 会给您的记忆带来很大压力。如果您可以按深度优先顺序编写 XML,请使用 XmlTextWriter

以下是一些将可能大量 XML 写入输出流的示例代码:

public ActionResult SiteMap()
{
    Response.ClearContent();
    Response.ContentType = "application/xml";
    Response.ContentEncoding = Encoding.UTF8;

    using (var context = new MyDataContext())
    {
        using (var xml = new XmlTextWriter(Response.Output))
        {
            xml.WriteStartElement("urlset",
                                  "http://www.sitemaps.org/schemas/sitemap/0.9");

            var url = new UrlHelper(Request.RequestContext);

            xml.WriteStartElement("url");
            xml.WriteElementString("loc", /* some URL */);
            xml.WriteEndElement();

            foreach (var blogPost in context.BlogPosts)
            {
                xml.WriteStartElement("url");
                xml.WriteElementString("loc", /* blog post URL */);
                xml.WriteEndElement();
            }

            xml.WriteEndElement();
            xml.Flush();
        }
    }

    return null;
}

这个特定示例是我用来写出sitemap.xml 文档的XML 内容的一些代码的缩略版本。在我的例子中,该文档有大约 30,000 个 URL,大小约为 2.5MB。

【讨论】:

    【解决方案4】:
    public ActionResult Xml()
    {
        string xmlString = "xml goes here...";
        return this.Content(xmlString, "text/xml", Encoding.UTF8);
    }
    

    如果您不想在每个请求上重新生成它,可能需要考虑缓存 ActionResult。查看[OutputCache]属性,可以通过参数缓存,指定时间等。

    我不确定你在说什么,听起来你想做某种异步 http 请求。一般来说,他们不是。客户端发送http请求,服务器生成并返回响应。

    【讨论】:

    • 我正在构建一个 XML 站点地图。一个非常复杂的。有必要跨多个数据库发出大量小型数据库请求。在许多情况下,我最终获得了开始构建 XML 字符串和发出下一个数据库请求所需的信息。我更喜欢在等待所有数据库请求超时之前开始提供 Google、Yahoo、Bing 等的结果。
    • 我使用了 ContentResult,但不幸的是我在设置 UTF8 编码时总是得到 encoding="utf-16" :(
    【解决方案5】:

    您总是可以先加载页面并显示友好的内容,然后使用 AJAX 调用执行脏工作的操作方法。

    【讨论】:

    • 整个结果是 XML。没有页面。
    • 啊,我看错了,无视我的评论。嗯,现在我也很好奇如何做到这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    • 1970-01-01
    相关资源
    最近更新 更多