Can RestSharp send binary data without using a multipart content type?

こ雲淡風輕ζ 提交于 2020-01-09 17:43:15

问题


I have been using AddParameter to include XML bodies in my HTTP requests:

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

However, this does not seem to work for non-string bodies. (RestSharp's Http.RequestBody is a string for some reason.) I tried using AddFile(), but I can't find any way to avoid encoding the "file" as multipart/form, even if I've only supplied a single object.

I'm not at all opposed to underhanded reflection to solve this problem, but I'm hoping to avoid modifying the source just to send arbitrary data in an HTTP request.

Edit: regarding the requests I'm trying to send, they just look like this:

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

<arbitrary bytes>

Ideally, I'd like to use the same calls to send a different content type:

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

<other arbitrary bytes>

回答1:


There have been some modifications made in the latest version that allow a single file to be used without creating a multipart form request. Here is a gist that shows and example:

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




回答2:


I ran into the same issue. I had to upload exactly one file and use a specific content type for communicating with an REST Interface. You could modify Http.RequestBody to byte[] (and all dependencies on that), but i think its easier this way:

I modified RestSharp, so that it only use Multipart Encoding when number of Files > 1 or number of Files = 1 and there is also body or other post data set.

You have to modify Http.cs on Line 288 from

if(HasFiles)

to

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

For Http.Sync.cs modify PreparePostData from

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);
}

If you use the async version, you have to modify the code similar to the above in Http.Async.cs.

Now u can use RestSharp like this

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

Client.Execute(request);

AddFile also provides an overload for setting direct byte[] data instead of a path. Hope that helps.




回答3:


In the latest version of RestSharp at the time of writing (version 104), the modification needs to be in Http.Sync.cs , method PreparePostData, which should read as:

    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);
    }



回答4:


I had the same problem, but I didn't fancy forking the code and I didn't like the alternative suggested by Michael as the documentation says "RequestBody: Used by AddBody() (not recommended to use directly)".

Instead I replaced the RestClient.HttpFactory with my own:

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);

Where Bytes and FactoryWithContent look like:

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; }
}



回答5:


I had the same issue. Turned out that RestSharp behaves in a little odd way.

NOT WORKING:

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

WORKING (Add content-type as name):

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
});

I tried this solution based on a comment here: https://github.com/restsharp/RestSharp/issues/901

which states "...name value will be used as Content-Type Header and contentType value will be ignored."

You dont have to add the value as the Content-Type parameter as well, but I fear that a future bug-fix might change the behaviour and then requiring the Content-Type to be used instead of name.




回答6:


Modifications to Http.Async.cs are also necessary for the RequestStreamCallback method. I'm actually working on getting this fix into the repo and published to Nuget as I'm helping to maintain the project now. Here's a link to the issue that's been created for this: https://github.com/restsharp/RestSharp/issues/583



来源:https://stackoverflow.com/questions/10158977/can-restsharp-send-binary-data-without-using-a-multipart-content-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!