Может ли RestSharp отправлять двоичные данные без использования составного типа контента?

Я использовал AddParameter для включения тел XML в свои HTTP-запросы:

request.AddParameter(contentType, body, ParameterType.RequestBody);

Однако, похоже, это не работает для неструнных тел. (По какой-то причине Http.RequestBody в RestSharp является строкой.) Я пытался использовать AddFile(), но не могу найти способа избежать кодирования «файла» как multipart/form, даже если я предоставил только единый объект.

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

Изменить: что касается запросов, которые я пытаюсь отправить, они выглядят так:

PUT ... HTTP/1.1
Accept: application/vnd...
Authorization: Basic ...
Content-Type: application/octet-stream

<arbitrary bytes>

В идеале я хотел бы использовать те же вызовы для отправки контента другого типа:

PUT ... HTTP/1.1
Accept: application/vnd...
Authorization: Basic ...
Content-Type: application/vnd...

<other arbitrary bytes>

person ladenedge    schedule 15.04.2012    source источник
comment
Не совсем уверен, что понимаю проблему здесь; все http-запросы являются текстовыми; если вы отправляете двоичный файл, то он закодирован как base64 (обычно), который по-прежнему является текстом, хотя и нечитаемым текстом.   -  person Russ Clarke    schedule 15.04.2012
comment
чтобы квалифицировать обычный бит, существуют другие схемы, такие как uuencode, которые обеспечивают лучшее сжатие, но в конечном итоге это то же самое - используйте кодировку, которая позволяет представить полное значение байта в диапазоне печатных символов, которые поддерживает HTTP.   -  person Russ Clarke    schedule 15.04.2012
comment
Можете ли вы опубликовать пример необработанного http-запроса, который вы пытаетесь воспроизвести?   -  person John Sheehan    schedule 15.04.2012
comment
@RussC: тела HTTP могут содержать любое значение байта . Может быть, я не понимаю, что вы имеете в виду?   -  person ladenedge    schedule 15.04.2012
comment
@JohnSheehan: я обновил вопрос несколькими примерами. Являются ли они достаточно подробными? (Я пропустил некоторые автоматические поля.)   -  person ladenedge    schedule 15.04.2012
comment
@ladenedge это не значение байта как таковое, это: OCTET = «любая 8-битная последовательность данных». Проблема в том, что вам нужно представить более 8 бит. Если вы отправляете 8-байтовое число (64-битное число), то, если получатель интерпретирует это 8-байтовое значение как 8 различных байтов, тогда он испортится. Другими словами, технически это байт, но сервер не может предположить, что это байт, поэтому у нас есть кодировка для представления значений, которые больше, чем могут быть представлены в 8 битах.   -  person Russ Clarke    schedule 15.04.2012
comment
@RussC: в этом случае я устанавливаю тип содержимого на «application/octet-stream» (или какой-либо другой тип двоичного содержимого (например, изображение/jpeg)), чтобы сервер мог фактически предположить, что тело представляет собой последовательность байтов. без структуры.   -  person ladenedge    schedule 15.04.2012
comment
Мы все еще смотрим на это; одна вещь, которая только что пришла мне в голову, но это может не иметь значения, ссылка, которую вы дали выше в отношении HTTP-тел, на самом деле была о «телах сущностей», но запросы, которые вы пытаетесь отправить, указывают на то, что вы пытаетесь кодировать ваш двоичный файл в тело сообщения без использования заголовка Entity?   -  person Russ Clarke    schedule 16.04.2012
comment
Возвращаясь к вашему первоначальному вопросу, в настоящее время это не поддерживается, и это чрезвычайно редкий запрос, поэтому я вряд ли добавлю его сам. Я бы рекомендовал использовать HttpWebRequest напрямую.   -  person John Sheehan    schedule 16.04.2012
comment
у меня было похожее требование: мне нужно опубликовать multipart/form-data только с параметрами и без файлов. сделал запрос на вытягивание на github.com/restsharp/RestSharp/pull/385, но сделал не привлекать внимания   -  person Martin Meixger    schedule 22.08.2013


Ответы (6)


В последнюю версию были внесены некоторые изменения, которые позволяют использовать один файл без создания многостраничного запроса формы. Вот суть, которая показывает и пример:

https://gist.github.com/hallem/5faaa6bebde50641e928

person Michael    schedule 16.01.2015
comment
К вашему сведению для тех, кто, как и я, пропустил этот ответ, потому что он говорит файл: это не обязательно должен быть файл. Ключевая особенность заключается в том, что если вы добавите тело с request.AddParameter("application/pdf", documentBytes, ParameterType.RequestBody), а documentBytes будет byte[], то байты будут отправлены напрямую как указанное Content-Type (здесь application/pdf). - person meustrus; 10.02.2016

Я столкнулся с той же проблемой. Мне пришлось загрузить ровно один файл и использовать определенный тип контента для связи с интерфейсом REST. Вы можете изменить Http.RequestBody на byte[] (и все зависимости от этого), но я думаю, что так проще:

Я изменил RestSharp, чтобы он использовал Multipart Encoding только тогда, когда количество файлов> 1 или количество файлов = 1, а также есть тело или другой набор данных поста.

Вы должны изменить Http.cs в строке 288 из

if(HasFiles)

to

if(Files.Count > 1 || (Files.Count == 1 && (HasBody || Parameters.Any())))

Для Http.Sync.cs измените PreparePostData из

private void PreparePostData(HttpWebRequest webRequest)
{
    if (HasFiles)
    {
        webRequest.ContentType = GetMultipartFormContentType();
        using (var requestStream = webRequest.GetRequestStream())
        {
            WriteMultipartFormData(requestStream);
        }
    }

    PreparePostBody(webRequest);
}

to

private void PreparePostData(HttpWebRequest webRequest)
{
    // Multiple Files or 1 file and body and / or parameters
    if (Files.Count > 1 || (Files.Count == 1 && (HasBody || Parameters.Any())))
    {
        webRequest.ContentType = GetMultipartFormContentType();
        using (var requestStream = webRequest.GetRequestStream())
        {
            WriteMultipartFormData(requestStream);
        }
    }
    else if (Files.Count == 1)
    {
        using (var requestStream = webRequest.GetRequestStream())
        {
            Files.Single().Writer(requestStream);
        }
    }

    PreparePostBody(webRequest);
}

Если вы используете асинхронную версию, вам необходимо изменить код, аналогичный приведенному выше, в Http.Async.cs.

Теперь вы можете использовать RestSharp вот так

IRestRequest request = new RestRequest("urlpath", Method.PUT);
request.AddHeader("Content-Type", "application/zip");
request.AddFile("Testfile", "C:\\File.zip");

Client.Execute(request);

AddFile также предоставляет перегрузку для установки прямых данных byte[] вместо пути. Надеюсь, это поможет.

person Dresel    schedule 09.08.2012
comment
Между прочим, альтернативной модификацией RestSharp будет изменение кодировки по умолчанию на Windows-1252 и передача в строке, закодированной как таковая в AddBody. Оказывается, строка в кодировке 1252 эквивалентна двоичному коду. - person ladenedge; 03.09.2012

В последней версии RestSharp на момент написания (версия 104) модификация должна быть в Http.Sync.cs, метод PreparePostData, который должен читаться как:

    private void PreparePostData(HttpWebRequest webRequest)
    {

        // Multiple Files or 1 file and body and / or parameters
        if (Files.Count > 1 || (Files.Count == 1 && (HasBody || Parameters.Count>0)))
        {
            webRequest.ContentType = GetMultipartFormContentType();
            using (var requestStream = webRequest.GetRequestStream())
            {
                WriteMultipartFormData(requestStream);
            }
        }
        else if (Files.Count == 1)
        {
            using (var requestStream = webRequest.GetRequestStream())
            {
                Files[0].Writer(requestStream);
            }
        }
        PreparePostBody(webRequest);
    }
person Vincent Sos    schedule 19.09.2012

У меня была та же проблема, но мне не хотелось разветвлять код, и мне не понравилась альтернатива, предложенная Майклом, поскольку в документации говорится: «RequestBody: используется AddBody () (не рекомендуется использовать напрямую)».

Вместо этого я заменил RestClient.HttpFactory своим собственным:

RestClient client = GetClient();

var bytes = await GetBytes();
client.HttpFactory = new FactoryWithContent { GetBytes = () => new Bytes(bytes, "application/zip") };

var request = new RestRequest();
return await client.ExecutePostTaskAsync(request);

Где Bytes и FactoryWithContent выглядят так:

public class Bytes
{
    public Bytes(byte[] value, string type)
    {
        Value = value;
        Type = type;
    }

    public byte[] Value { get; private set; }
    public string Type { get; private set; }
}

public class FactoryWithContent : IHttpFactory
{
    public IHttp Create()
    {
        var http = new Http();

        var getBytes = GetBytes;
        if (getBytes != null)
        {
            var bs = getBytes();
            http.RequestBodyBytes = bs.Value;
            http.RequestContentType = bs.Type;
        }

        return http;
    }

    public Func<Bytes> GetBytes { get; set; }
}
person andygjp    schedule 09.01.2015
comment
поэтому я ненавижу RestSharp - person hellboy; 22.06.2015

Я была такая же проблема. Оказалось, что RestSharp ведет себя немного странно.

НЕ РАБОТАЕТ:

request.Parameters.Add(new Parameter() {
  ContentType = "application/x-www-form-urlencoded",
  Type = ParameterType.RequestBody,
  Value = bytes
});

РАБОТАЕТ (добавьте тип контента в качестве имени):

request.Parameters.Add(new Parameter() {
  Name = "application/x-www-form-urlencoded", // This is the 'funny' part
  ContentType = "application/x-www-form-urlencoded",
  Type = ParameterType.RequestBody,
  Value = bytes
});

Я попробовал это решение на основе комментария здесь: https://github.com/restsharp/RestSharp/issues/901

в котором указано "... значение имени будет использоваться в качестве заголовка Content-Type, а значение contentType будет проигнорировано."

Вам также не нужно добавлять значение в качестве параметра Content-Type, но я боюсь, что в будущем исправление ошибки может изменить поведение, а затем потребовать использования Content-Type вместо имени.

person Stephan Møller    schedule 24.04.2018

Изменения в Http.Async.cs также необходимы для метода RequestStreamCallback. На самом деле я работаю над тем, чтобы внести это исправление в репозиторий и опубликовать его в Nuget, поскольку сейчас я помогаю поддерживать проект. Вот ссылка на созданную для этого задачу: https://github.com/restsharp/RestSharp/issues/583

person Michael    schedule 17.10.2014