首页 > 解决方案 > 在动作方法中抛出 HttpResponseException 时处理 HttpResponseMessage 及其内容

问题描述

我的问题的来源是以下代码,它是Microsoft 文档中包含的用于在 asp.net web api 中处理异常的代码片段的一部分:

var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
    Content = new StringContent(string.Format("No product with ID = {0}", id)),
    ReasonPhrase = "Product ID Not Found"
};
throw new HttpResponseException(resp);

两者都HttpResponseMessage实现StringContentIDisposable接口,但是上面的代码中没有一个调用方法IDisposable.Dispose
这是一个问题吗?不处理这些对象是否有任何副作用?

根据这篇文章,一个可能的解决方案可能是将上面的代码更改为以下代码:

var content = new StringContent(string.Format("No product with ID = {0}", id));
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
    Content = content,
    ReasonPhrase = "Product ID Not Found"
};

this.Request.RegisterForDispose(content);
this.Request.RegisterForDispose(resp);

throw new HttpResponseException(resp);

这真的有必要吗,还是可以避免这种情况(根据 Microsoft 文档中显示的内容)?

标签: c#asp.netasp.net-web-apiidisposable

解决方案


检查HttpResponseMessage.CS 的 Microsoft 源

protected virtual void Dispose(bool disposing)
{
    // The reason for this type to implement IDisposable is that it contains instances of 
    // types that implement IDisposable (content). 
    if (disposing && !_disposed)
    {
        _disposed = true;
        if (_content != null)
        {
            _content.Dispose();
        }
    }
}

content属于 HttpContent 类型。检查HttpContent.cs 的 Microsoft 源代码

protected override void Dispose(bool disposing)
{
    Debug.Assert(_buffer != null);

    ArrayPool<byte>.Shared.Return(_buffer);
    _buffer = null;

    base.Dispose(disposing);
}

ArrayPool的评论说:

/// Renting and returning buffers with an <see cref="ArrayPool{T}"/> can increase performance
/// in situations where arrays are created and destroyed frequently, resulting in significant
/// memory pressure on the garbage collector.

检查 ArrayPool 的源代码会产生这个可爱的宝石:

    /// <summary>
    /// Retrieves a shared <see cref="ArrayPool{T}"/> instance.
    /// </summary>
    /// <remarks>
    /// The shared pool provides a default implementation of <see cref="ArrayPool{T}"/>
    /// that's intended for general applicability.  It maintains arrays of multiple sizes, and 
    /// may hand back a larger array than was actually requested, but will never hand back a smaller 
    /// array than was requested. Renting a buffer from it with <see cref="Rent"/> will result in an 
    /// existing buffer being taken from the pool if an appropriate buffer is available or in a new 
    /// buffer being allocated if one is not available.
    /// </remarks>
    public static ArrayPool<T> Shared
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get { return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated(); }
    }

ArrayPool不使用WeakReferences 或任何类似机制来确保正确处置。如果您从 租用缓冲区ArrayPool.Shared,则必须将其归还,否则会导致内存泄漏。

所以,是的,我想说尊重IDisposable在这里非常重要。


推荐阅读