使用HttpClient设置带有Content-Type边界的请求头信息

9

我正在使用JavaScript与第三方服务进行通信。作为认证流程的一部分,他们需要对包含图像的“multipart/form”POST消息体进行md5加密,并将其添加到包括日期和其他几个内容的字符串中,然后在其上运行HMAc/SHA1。因此最终,他们会获得多部分正文、日期和身份验证哈希值以便进行身份验证并读取图片。

这对于除了Windows Phone以外的所有移动设备都有效(我知道,这是IE的问题...谁能想到呢?)。他们的httpwebrequest没有包括'日期'头,因此无法进行身份验证。这意味着我必须在Windows Phone上转而使用C#中新发布的httpclient代码。现在我对C#一窍不通,因此这可能是简单解决方法所在。通过将几乎所有内容传递给C#并仅使用C#进行POST操作,我已经成功进行了身份验证,但是他们无法读取正文,因为我发现唯一发送边界的方法是在将内容定义为multipartformDatacontent并以这种方式发送内容时,会更改正文,从而导致身份验证失败。

我的JavaScript大致如下:

var boundary = "------------ThIs_Is_tHe_bouNdaRY_";
var part1Array = [];
var part1 = "--"+boundary + "\r\n"+
    "Content-Disposition: form-data; name=\"image\"\r\n"+
    "Content-Type: image/jpg\r\n"+
    "\r\n";
var part3Array = [];
var part3 = "\r\n" + boundary +"--";
for(var p1=0; p1<part1.length; p1++){
    part1Array.push(part1.charCodeAt(p1));
}
for(var p3=0; p3<part3.length; p3++){
    part3Array.push(part3.charCodeAt(p3));
}
var bodyContent = part1Array.concat(imageArray,part3Array);

//hash this

var authMessage = bodyContentHash +"\n"+ contentType +"\n"+ todayString +"\n"+ pathandstuff;
// -hmac -sha1 -base64

而 C# 是:

HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, path);

request.Headers.Date = DateTime.ParseExact(todaydate, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'", new CultureInfo("en-US"), DateTimeStyles.AssumeUniversal);
request.Headers.Add("Accept", "application/json; charset=utf-8");
request.Headers.Add("Authorization", auth);

byte[] body = Convert.FromBase64String(bodyData);
request.Content = new ByteArrayContent(body);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data");
request.Content.Headers.Add("boundary", "------------ThIs_Is_tHe_bouNdaRY_");

HttpResponseMessage response = client.SendAsync(request).Result;
string resultCode = response.StatusCode.ToString();
string responseBodyAsText = await response.Content.ReadAsStringAsync();

这基本上可以工作了..正文内容和标题都是正确的..除了内容类型标头应该是:

request.Content.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data, boundary=------------ThIs_Is_tHe_bouNdaRY_");

但是,这会抛出一个System.FormatException错误。

3个回答

16
我们通过手动清除并重新添加内容类型而解决了这个问题,没有进行验证。似乎 MediaTypeHeaderValue() 类不喜欢边界标签。
而不是使用:
content.Headers.ContentType = new MediaTypeHeaderValue("multipart/form-data; boundary=----FLICKR_MIME_20140415120129--");

我们做了以下操作:

content.Headers.Remove("Content-Type");
content.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=----FLICKR_MIME_20140415120129--");

我们做了这个改变后,一切都正常工作了。

(请注意,这是在WinRT上进行的,如果有任何区别)


6
使用HttpClient
 request.Content.Headers.Add("ContentType", "multipart/form-data, boundary=------------ThIs_Is_tHe_bouNdaRY_");

您可以使用HttpWebRequest。

 myHttpWebRequest.Date = DateTime.Now;
 myHttpWebRequest.ContentType = "multipart/form-data, boundary=------------ThIs_Is_tHe_bouNdaRY_";

谢谢,我刚刚找到了相同的内容.headers.add并使其工作。不幸的是,我不能使用httpwebrequest解决方案,因为据我所读,这是不会为wp8填充日期标头的部分。谢谢。 - user2548513
1
我不熟悉wp8开发。httpwebrequest可以添加自定义标头myHttpWebRequest.Headers.Add("name", "value"); - user553838
是的,据我所知,这是特定于WP8而不是一般的C#,在WindowsPhone8版本的HttpWebRequest中没有“Date”属性。所有其他标头似乎都可以正常工作,但“Date”标头从未包含在您的请求中,这就是为什么我不得不使用HttpClient的原因。 - user2548513
httpClient请求.Content.Headers的解决方案完美地运行了 :) 再次感谢。 - user2548513

1
我在这里发布了我的代码,希望能帮助像我一样遇到问题的人。在我的情况下,这段代码可以将文件上传到我的账户(不是银行,而是一个安全的云文件产品)。
 public  string UploadFile(string endpointUrl, string filePath, string accessToken)
    {

        FileStream fs = null;
        Stream rs = null;
        string result = "";
        try
        {

            string uploadFileName = System.IO.Path.GetFileName(filePath);

            fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);

            var request = (HttpWebRequest)WebRequest.Create(endpointUrl);
            request.Method = WebRequestMethods.Http.Post;
            request.AllowWriteStreamBuffering = false;
            request.SendChunked = true;
            String CRLF = "\r\n"; // Line separator required by multipart/form-data.        
            long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();

            string boundary = timestamp.ToString("x");
            request.ContentType = "multipart/form-data; boundary=" + boundary;

            request.Headers.Add("Authorization", "Bearer " + accessToken);                             

            long bytesAvailable = fs.Length;
            long maxBufferSize = 1 * 1024 * 1024;


            rs = request.GetRequestStream();
            byte[] buffer = new byte[50];
            int read = 0;

            byte[] buf = Encoding.UTF8.GetBytes("--" + boundary + CRLF);
            rs.Write(buf, 0, buf.Length);

            buf = Encoding.UTF8.GetBytes("Content-Disposition: form-data; name=\"body\"; filename=\"" + uploadFileName + "\"" + CRLF);                
            rs.Write(buf, 0,buf.Length);

            buf = Encoding.UTF8.GetBytes("Content-Type: application/octet-stream;" + CRLF);
            rs.Write(buf, 0, buf.Length);

            buf = Encoding.UTF8.GetBytes(CRLF);
            //writer.append("Content-Type: application/octet-stream;").append(CRLF);
            rs.Write(buf, 0, buf.Length);
            rs.Flush();


            long bufferSize = Math.Min(bytesAvailable, maxBufferSize);
            buffer = new byte[bufferSize];
            while ((read = fs.Read(buffer, 0, buffer.Length)) != 0)
            {
                rs.Write(buffer, 0, read);
            }
            buf = Encoding.UTF8.GetBytes(CRLF);                
            rs.Write(buf, 0, buf.Length);
            rs.Flush();


            // End of multipart/form-data.
            buffer = Encoding.UTF8.GetBytes("--" + boundary + "--" + CRLF);
            rs.Write(buffer, 0, buffer.Length);

            using (var response = request.GetResponse())
            using (var responseStream = response.GetResponseStream())
            using (var reader = new StreamReader(responseStream))
            {

                result = reader.ReadToEnd();
            }
        }
        catch (Exception e)
        {
            result = e.InnerException != null ? e.InnerException.Message : e.Message;  
        }

        return result;
    }

直接使用,所以我必须点赞(:, 添加函数GetResponseWithTimeout或编辑它。 - Yakir Manor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接