2009-06-03 7 views
24

いくつかの奇妙な理由から、コントローラアクションからレスポンスストリームに直接HTMLを書きたいと思います。 (私はMVC分離を理解していますが、これは特殊なケースです)アクションからの出力ストリームへの書き込み

ストリームに直接書き込むことはできますか?HttpResponse?その場合、コントローラーのアクションはどのIViewオブジェクトに返されるべきですか? 「null」を返すことはできますか?

答えて

8

はい、あなたは応答に直接書き込むことができます。完了したら、CompleteRequest()を呼び出すことができ、何も返す必要はありません。例えば

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

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

    return View();  // does not execute! 
} 
+1

Response.End()を避ける必要があります。http://stevesmithblog.com/blog/use-httpapplication-completerequest-instead-of-response-end/ –

+1

CompleteRequest()を使用するように更新しました。 – womp

+1

ビューの欠落に関するエラーを避けるために、 "return View()"を "return Content(" ")に置き換えると便利です。しかし、このアプローチは安全ですか? –

5

は、独自のアクション結果を書きます。ここで鉱山の一例は次のとおり

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); 
    } 
} 
43

今回用いた通常のMVCパターンを達成するために、FileResultから派生したクラスを使用する:

/// <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を使用することです。 1つのケースでは、パフォーマンスが約100倍向上しました。あなたがあなた自身の結果の型を派生させたくない場合は、あなたは、単にResponse.OutputStreamへの書き込みとnew EmptyResult()を返すことができ

Unbuffered Output Very Slow

+0

この質問に対する最善の回答。良いアイデア。 – pylover

+0

他の答えはハックです – Andrey

+0

ベストアンサー - ファイルを一度追加して、フレキシブルなデリゲートパラメータを使用して他のすべての状況でこの概念を再利用してください。 – Froyke

3

を参照してください。

関連する問題