Запись в поток вывода из действия

По каким-то странным причинам я хочу записать HTML-код прямо в поток ответа из действия контроллера. (Я понимаю разделение MVC, но это особый случай.)

Могу ли я написать прямо в HttpResponseпоток? В таком случае, какой IViewобъект должно возвращать действие контроллера? Могу ли я вернуть «ноль»?

3.06.2009 04:56:34
5 ОТВЕТОВ

Вы можете сделать, return Content(...);где, если я правильно помню, ...будет то, что вы хотите записать непосредственно в выходной поток, или вообще ничего.

Взгляните на Contentметоды на Controller: http://aspnet.codeplex.com/SourceControl/changeset/view/22907#266451

А также ContentResult: http://aspnet.codeplex.com/SourceControl/changeset/view/22907#266450

3
3.06.2009 05:07:53

Да, вы можете написать прямо в ответ. После того, как вы закончите, вы можете вызвать CompleteRequest (), и вам не нужно ничего возвращать.

Например:

// GET: /Test/Edit/5
public ActionResult Edit(int id)
{

    Response.Write("hi");
    HttpContext.ApplicationInstance.CompleteRequest();

    return View();     // does not execute!
}
9
3.06.2009 06:09:50
Вам следует избегать Response.End () stevesmithblog.com/blog/…
John Sheehan 3.06.2009 05:10:26
Затем обновлен для использования CompleteRequest ().
womp 3.06.2009 06:09:05
Может быть полезно заменить "return View ()" на "return Content (" ")", чтобы избежать ошибок, связанных с отсутствием представления. Но безопасен ли этот подход?
Andrey Shvydky 28.06.2012 07:36:35
это не очень хороший подход, потому что никакие атрибуты ActionFilter не будут выполняться после завершения этого метода.
Knaģis 5.12.2012 16:37:22

Напишите свой собственный результат действий. Вот пример одного из моих:

public class RssResult : ActionResult
{
    public RssFeed RssFeed { get; set; }

    public RssResult(RssFeed feed) {
        RssFeed = feed;
    }

    public override void ExecuteResult(ControllerContext context) {
        context.HttpContext.Response.ContentType = "application/rss+xml";
        SyndicationResourceSaveSettings settings = new SyndicationResourceSaveSettings();
        settings.CharacterEncoding = new UTF8Encoding(false);
        RssFeed.Save(context.HttpContext.Response.OutputStream, settings);
    }
}
5
3.06.2009 05:00:33

Я использовал класс, полученный FileResultдля достижения этой цели, используя обычный шаблон MVC:

/// <summary>
/// MVC action result that generates the file content using a delegate that writes the content directly to the output stream.
/// </summary>
public class FileGeneratingResult : FileResult
{
    /// <summary>
    /// The delegate that will generate the file content.
    /// </summary>
    private readonly Action<System.IO.Stream> content;

    private readonly bool bufferOutput;

    /// <summary>
    /// Initializes a new instance of the <see cref="FileGeneratingResult" /> class.
    /// </summary>
    /// <param name="fileName">Name of the file.</param>
    /// <param name="contentType">Type of the content.</param>
    /// <param name="content">Delegate with Stream parameter. This is the stream to which content should be written.</param>
    /// <param name="bufferOutput">use output buffering. Set to false for large files to prevent OutOfMemoryException.</param>
    public FileGeneratingResult(string fileName, string contentType, Action<System.IO.Stream> content,bool bufferOutput=true)
        : base(contentType)
    {
        if (content == null)
            throw new ArgumentNullException("content");

        this.content = content;
        this.bufferOutput = bufferOutput;
        FileDownloadName = fileName;
    }

    /// <summary>
    /// Writes the file to the response.
    /// </summary>
    /// <param name="response">The response object.</param>
    protected override void WriteFile(System.Web.HttpResponseBase response)
    {
        response.Buffer = bufferOutput;
        content(response.OutputStream);
    }
}

Метод контроллера теперь будет выглядеть так:

public ActionResult Export(int id)
{
    return new FileGeneratingResult(id + ".csv", "text/csv",
        stream => this.GenerateExportFile(id, stream));
}

public void GenerateExportFile(int id, Stream stream)
{
    stream.Write(/**/);
}

Обратите внимание, что если буферизация отключена,

stream.Write(/**/);

становится чрезвычайно медленным Решением является использование BufferedStream. Это позволило повысить производительность примерно в 100 раз в одном случае. Видеть

Небуферизованный вывод очень медленный

46
23.05.2017 12:09:39
Лучший ответ - просто добавьте файл один раз и повторно используйте эту концепцию в любой другой ситуации, используя параметр гибкого делегата.
Froyke 24.01.2013 18:35:32
Обратите внимание, что если вы пишете большой файл таким образом, вы можете получить OutOfMemoryException. Вы можете решить, отключив буферизацию. Добавьте строку, чтобы WriteFile()полюбить это:response.Buffer = false;
Eric J. 24.09.2014 06:49:39
Хорошее решение. +1 Взял на себя смелость @EricJ. предложение в этот ответ (и обновление xmldocs). Не стесняйтесь откатиться, если это оскорбляет.
spender 25.09.2014 02:06:37
@spender: я обнаружил предупреждение: отключение буферизации делает вывод крайне медленным. Добавление в BufferedStream разрешает это. Я обновил ответ с этой информацией.
Eric J. 25.09.2014 16:00:49
Работает ли это, если запись выполняется асинхронно и метод WriteFile возвращается до завершения запроса? Я ничего не могу найти, прибегая к помощи «FileResult» и «async».
John 28.10.2014 13:21:34

Если вы не хотите получать свой собственный тип результата, вы можете просто написать Response.OutputStreamи вернуться new EmptyResult().

4
5.02.2014 08:15:11