代码之家  ›  专栏  ›  技术社区  ›  Enrico Massone

在action方法内部引发httpResponseException时处理httpResponseMessage及其内容

  •  3
  • Enrico Massone  · 技术社区  · 6 年前

    我问题的来源是以下代码,它是包含在 Microsoft documentation 对于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 StringContent 实施 IDisposable 接口,但上述代码中没有人调用方法 IDisposable.Dispose .
    这是个问题吗?不处置这些物品会有什么副作用吗?

    根据 this article 可能的解决方案是将上述代码更改为以下代码:

    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文档中显示的内容)?

    2 回复  |  直到 6 年前
        1
  •  1
  •   Evk    6 年前

    响应由包装 HttpResponseException 将由ASP.NET框架处理,就像从操作返回的任何其他响应一样。您可以通过创建虚拟响应消息来轻松测试这一点:

    class DummyResponse : HttpResponseMessage {
        public DummyResponse(HttpStatusCode statusCode) : base(statusCode) {
        }
    
        protected override void Dispose(bool disposing) {
            Console.WriteLine("dispose called");
            base.Dispose(disposing);
        }
    }
    

    然后扔 HTT响应例外 并将断点放入 Dispose 覆盖。你会注意到的 处置 调用,如果查看调用堆栈,您将看到 HttpControllerHandler 负责执行此操作(在ASP.NET Web API控制器中)。

    请注意,此异常被捕获 ApiControllerActionInvoker ,负责调用API控制器操作的类。然后它就抓住了 yourException.Response 并通过管道将其向前推,因此抛出这个异常与从API控制器操作返回相应的响应没有什么不同。很明显,我认为框架在处理完这些响应之后,会处理掉它们。否则它的设计会很差。

    所以,不要把代码和那些 RegisterForDispose 让框架为您处理这个问题。

        2
  •  3
  •   Robert Harvey    6 年前

    检查 Microsoft Source for HttpResponseMessage.CS :

    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类型。检查 Microsoft Source for HttpContent.cs :

    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 不使用 WeakReference 或任何类似机制,以确保正确处置。如果你从 ArrayPool.Shared ,您必须返回它,否则将导致内存泄漏。

    所以是的,我要说尊重 IDisposable 在这里很重要。

    推荐文章