File: Infrastructure\ContentResultExecutor.cs
Web Access
Project: src\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable enable
 
using System.Text;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Logging;
 
namespace Microsoft.AspNetCore.Mvc.Infrastructure;
 
/// <summary>
/// A <see cref="IActionResultExecutor{ContentResult}"/> that is responsible for <see cref="ContentResult"/>
/// </summary>
public partial class ContentResultExecutor : IActionResultExecutor<ContentResult>
{
    private const string DefaultContentType = "text/plain; charset=utf-8";
    private readonly ILogger<ContentResultExecutor> _logger;
    private readonly IHttpResponseStreamWriterFactory _httpResponseStreamWriterFactory;
 
    /// <summary>
    /// Initializes a new instance of <see cref="ContentResultExecutor"/>.
    /// </summary>
    /// <param name="logger">The logger to use.</param>
    /// <param name="httpResponseStreamWriterFactory">The stream writer factory.</param>
    public ContentResultExecutor(ILogger<ContentResultExecutor> logger, IHttpResponseStreamWriterFactory httpResponseStreamWriterFactory)
    {
        _logger = logger;
        _httpResponseStreamWriterFactory = httpResponseStreamWriterFactory;
    }
 
    /// <inheritdoc />
    public virtual async Task ExecuteAsync(ActionContext context, ContentResult result)
    {
        ArgumentNullException.ThrowIfNull(context);
        ArgumentNullException.ThrowIfNull(result);
 
        var response = context.HttpContext.Response;
 
        ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
            result.ContentType,
            response.ContentType,
            (DefaultContentType, Encoding.UTF8),
            MediaType.GetEncoding,
            out var resolvedContentType,
            out var resolvedContentTypeEncoding);
 
        response.ContentType = resolvedContentType;
 
        if (result.StatusCode != null)
        {
            response.StatusCode = result.StatusCode.Value;
        }
 
        Log.ContentResultExecuting(_logger, resolvedContentType);
 
        if (result.Content != null)
        {
            response.ContentLength = resolvedContentTypeEncoding.GetByteCount(result.Content);
 
            await using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
            {
                await textWriter.WriteAsync(result.Content);
 
                // Flushing the HttpResponseStreamWriter does not flush the underlying stream. This just flushes
                // the buffered text in the writer.
                // We do this rather than letting dispose handle it because dispose would call Write and we want
                // to call WriteAsync.
                await textWriter.FlushAsync();
            }
        }
    }
 
    private static partial class Log
    {
        [LoggerMessage(1, LogLevel.Information, "Executing ContentResult with HTTP Response ContentType of {ContentType}", EventName = "ContentResultExecuting")]
        public static partial void ContentResultExecuting(ILogger logger, string contentType);
    }
}