HttpResponseMessage.Content.Headers ContentDisposi

2019-02-16 05:59发布

问题:

When downloading a file with HttpClient, I'm downloading first the headers and then the content. When headers are downloaded, I can see Headers collection on the Content property of HttpResponseMessage, but when accessing it through ContentDisposition on Headers, get null

Why this is happening? Fiddler shows headers are fine...

Code:

var responseMessage = await httpClient.GetAsync(uri, 
HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(continueOnCapturedContext: false);

Update 1

It looks like this class is following Content-Disposition implementation outlined in RFC 2616 and fails to handle Content-Disposition implementation update RFC 6266. RFC 2616 defines filename parameter value to be a quoted-string, where update RFC 6266 just states it should be value.

RFC 2616 Grammar

content-disposition = "Content-Disposition" ":"
                          disposition-type *( ";" disposition-parm )
    disposition-type = "attachment" | disp-extension-token
    disposition-parm = filename-parm | disp-extension-parm
    filename-parm = "filename" "=" quoted-string
    disp-extension-token = token
    disp-extension-parm = token "=" ( token | quoted-string )

RFC 6266 Grammar

content-disposition = "Content-Disposition" ":"
                        disposition-type *( ";" disposition-parm )

 disposition-type    = "inline" | "attachment" | disp-ext-type
                     ; case-insensitive
 disp-ext-type       = token

 disposition-parm    = filename-parm | disp-ext-parm

 filename-parm       = "filename" "=" value
                     | "filename*" "=" ext-value

 disp-ext-parm       = token "=" value
                     | ext-token "=" ext-value
 ext-token           = <the characters in token, followed by "*">

where ext-value = <ext-value, defined in [RFC5987], Section 3.2>

Examples

Working case

Failing case

Update 2

Opened a ticket with MS connect.

Update 3

Microsoft has acknowledged that this is a bug and will fix it.

回答1:

The problem is with the trailing ; in the content-disposition header

        [Fact]
        public void ParseContentDispositionHeader()
        {
            var value = ContentDispositionHeaderValue.Parse("attachment; filename=GeoIP2-City_20140107.tar.gz");
            Assert.Equal("GeoIP2-City_20140107.tar.gz",value.FileName);
        }

If I add the semi-colon the parsing will fail. If you look at the RFC6266 grammar, the semi-colon is only supposed to precede the parameter.



回答2:

Thank you - finding this definitely helped me. For the benefit of others, here is my workaround (as apparently this is still a thing today???)

I am in a somewhat controlled environment, so the following code assumes:

  • Only one Content-Disposition Header
  • The tag is in the format: inline; "filename";

This will reset the response's ContentDisposition header, so subsequent code works seamlessly:

<!-- language: c# -->
if (response.Content.Headers.ContentDisposition == null)
{
  IEnumerable<string> contentDisposition;
  if (response.Content.Headers.TryGetValues("Content-Disposition", out contentDisposition))
  {
   response.Content.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse(contentDisposition.ToArray()[0].TrimEnd(';').Replace("\"",""));
  }
}