Gzip MultipartFormDataContent Тайм-аут при публикации на сервере - PullRequest
4 голосов
/ 23 апреля 2020

Я пытаюсь POST MultipartFormDataContent в реальном сценарии, объект содержимого данных может содержать что угодно, от простой строки до видеофайла, который я использую там в виде сериализованного объекта, просто подтверждение концепции. Также я хотел бы отметить, что использование JSON объектов не будет служить моему реальному сценарию ios

 public class GzipMultipartContent : MultipartFormDataContent
 {
    public GzipMultipartContent()
    {
       Headers.ContentEncoding.Add("gzip");
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        return Task.Factory.StartNew(() =>
        {
            using (var gzip = new GZipStream(stream, CompressionMode.Compress, true))
                base.SerializeToStreamAsync(gzip, context);
        });
    }
}

, и вот как я его называю

var gzipped = new GzipMultipartContent();
var test = new TestClass();
gzipped.Add(new StringContent(JsonConvert.SerializeObject(test)), "value");
var client = new HttpClient();
var result = client.PostAsync("http://localhost:60001/api/Home/", gzipped).Result;

и вот действие после в контроллере

// POST: api/Home
[HttpPost]
public void Post([FromForm] object value)
{

}

Я добавил точку останова на стороне сервера и убедился, что она даже не достигает метода Post, также я попытался с помощью обычного запроса POST, чтобы убедиться, что это не так проблема конфигурации сервера или опечатка URL

Ответы [ 2 ]

0 голосов
/ 28 апреля 2020

Клиентская сторона

Если рассматриваемый код является вашим реальным кодом, то есть как минимум две проблемы:

Не ожидал base.SerializeToStreamAsync

Вы создали новую задачу, но не подождали, пока базовый класс завершит запись в сжатый поток в задаче. Таким образом, вы можете отправить непредсказуемый контент на сервер.

Не переопределить Content-Length

MultipartFormDataContent вычисляет длину контента на основе несжатых данных, так как вы сжали данные, вы должны пересчитать длину сжатых данных.

Честно говоря, я не думаю, что вам нужно наследовать от MultipartFormDataContent, чтобы сделать их сжатыми. Вместо этого вы можете сжать все MultipartFormDataContent в оболочке HttpContent:

public class GzipCompressedContent : HttpContent
{
    private readonly HttpContent _content;

    public GzipCompressedContent(HttpContent content)
    {
        // Copy original headers
        foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers)
        {
            Headers.TryAddWithoutValidation(header.Key, header.Value);
        }

        Headers.ContentEncoding.Add("gzip");        
        _content = content;     
    }

    protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        using (var gzip = new GZipStream(stream, CompressionMode.Compress, true))
        {
            // Compress the entire original content
            await _content.CopyToAsync(gzip);
        }       
    }

    protected override bool TryComputeLength(out long length)
    {
        // Content-Lenght is optional, so set to -1
        length = -1;
        return false;
    }
}

и использовать его:

var test = new TestClass();

using (var client = new HttpClient())
{
    var form = new MultipartFormDataContent();
    form.Add(new StringContent(JsonConvert.SerializeObject(test)), "value");

    var compressed = new GzipCompressedContent(form);

    var result = await client.PostAsync(..., compressed);
}

На стороне сервера

Требуется ваш сервер для поддержки сжатого потока.

Например, по умолчанию ASP. NET Core не поддерживает сжатый запрос, если вы отправляете сжатый запрос GZip в приложение ASP. NET Core, вы увидит исключение:

System.IO.IOException: Unexpected end of Stream, the content may have already been read by another component. 
   at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)

Вышеуказанное исключение происходит в конвейере вызова действия до того, как произойдет какое-либо действие контроллера. Таким образом, действия контроллера в этом случае не могут быть достигнуты.

Чтобы исправить это, вам нужно включить поддержку распаковки запросов на стороне сервера.

Если вы используете ASP. NET Core этот пакет Nuget .

0 голосов
/ 26 апреля 2020

Я не уверен, что понял проблему, но если ваш запрос не попадает на сервер, в то время как ваши "нормальные" запросы POST, то я думаю, что нашел вашу проблему.

Я думаю, что проблема в том, что ваш сервер не знает, какой тип контента подходит к нему. Я буквально скопировал ваш код, но добавил

Headers.ContentType = new MediaTypeHeaderValue("application/x-gzip"); 

в GzipMultipartContent.cs ctor.

После того, как я добавил тип, я достиг своей точки останова на сервере localhost.

Источник: Тип содержимого

В запросах (таких как POST или PUT) клиент сообщает серверу, какой тип данных фактически отправляется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...