WCF MTOM附件如何设置content-type
WCF MTOM attachment how to set content-type
我需要为 MTOM 配置的绑定中的二进制部分设置特定的内容类型 (application/pdf)。
我可以在配置或代码中设置内容类型吗?对于文件部分,它总是 "Content-Type: application/octet-stream"
配置:
<binding name="FileTransferServicesBinding"
closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00"
messageEncoding="Mtom" transferMode="Buffered"
maxBufferSize="67108864" maxReceivedMessageSize="67108864">
<security mode="Transport" />
</binding>
当前发送到服务的内容。文件的多部分 header 应该是 application/pdf 而不是 application/octetstream
POST https://somewebservice HTTP/1.1
MIME-Version: 1.0
Authorization: Basic authhash
SOAPAction: ""
Host: webservicehost
Content-Length: 24517
Expect: 100-continue
Accept-Encoding: gzip, deflate
--uuid:b8366a06-3ecc-4bc4-9809-8c87ad459981+id=1
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Header>[The soapmessageitself]</s:Envelope>
--uuid:b8366a06-3ecc-4bc4-9809-8c87ad459981+id=1
Content-ID: <http://tempuri.org/1/636787311873257476>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream // This must be application/pdf
%PDF-1.4
%
1 0 obj
<</Type /Catalog/Pages 2 0 R>> ...
我不得不猜测您正在对请求的响应进行编码。如果是这样,您只需将内容类型 header 添加到您的外发消息中:
OperationContext
.Current
.OutgoingMessageHeaders
.Add ("Content-Type", "application/pdf" );
不能 100% 确定,但您可能需要先清除任何现有的传出 Content-Type header。我认为您的消息是 MTOM-encoded 并不重要。理论上是透明的。
另外...只是问...当流式消息传输占用的内存少得多时,为什么允许 64MB 的缓冲消息...而且可能同样快或更快?您几乎可以保证在重负载服务中获得更好的性能。
更新:
如果您有幸使用网络 api-type 绑定 (WebHttpBinding)...您可以非常轻松地流式传输您的内容,指定内容类型。这与我在上面的回答中所写的并没有什么不同,但是正如您所回答的那样,那些 header 去错了地方。
相反,它应该是:
WebOperationContext
.Current
.OutgoingResponse
.ContentType = "application/pdf";
有一个很棒的 article/example here 可以充分发展这个想法...以符合您的目标的方式。
如果您绑定到旧的 soap 绑定,那么您可能需要编写自定义消息编码器。该技术足够简单(如果乏味)。基本思想是您可以控制以适合您需要的方式编写消息的 body。
编码器本身很简单。围绕它的样板将是乏味的部分。自定义编码器源自抽象 MessageEncoder。要做的重要事情是覆盖 ContentType
、MediaType
、MessageVersion
属性以及 ReadMessage
和 WriteMessage
方法。 ReadMessage
和 WriteMessage
方法有多种变体用于处理流式和缓冲消息。
丑陋的部分是您必须编写部署编码器的样板。您已经创建了一个编码器工厂和一个自定义绑定元素。不可怕,但很吵。示例代码包中有几个完整的蜡球示例(包括工厂和绑定元素支持)here...可以节省您一些时间。
随着时间的推移,我通过手动构建 soap 调用解决了这个问题。由于这个问题有赏金,我将标记做相同但使用 WCF 库作为正确答案的答案。下面的代码示例 "byteContent.Headers.Add("Content-Type", "application/pdf; name=test.pdf");"我需要用 WCF
做什么
static HttpClient client;
static object mutex = new object();
static HttpClient Client
{
get
{
if (client == null)
{
lock (mutex)
{
if (client == null)
{
client = new HttpClient();
client.DefaultRequestHeaders.Add("MIME-Version", "1.0");
client.DefaultRequestHeaders.Add("SOAPAction", "\"\"");
client.DefaultRequestHeaders.ConnectionClose = true;
client.DefaultRequestHeaders.ExpectContinue = false;
client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(Username + ":" + Password)));
}
}
}
return client;
}
}
public static TResponse Send<TResponse>(string endpoint, string payload, byte[] file)
{
string boundary = Guid.NewGuid().ToString("N");
MultipartContent multipart = new MultipartContent("related", boundary);
multipart.Headers.Remove("Content-Type");
multipart.Headers.TryAddWithoutValidation("Content-Type", "multipart/related; boundary=\"" + boundary + "\"");
multipart.Add(new StringContent(payload, Encoding.UTF8, "text/xml"));
ByteArrayContent byteContent = new ByteArrayContent(file);
byteContent.Headers.Remove("Content-Type");
byteContent.Headers.Add("Content-Type", "application/pdf; name=test.pdf");
byteContent.Headers.Add("Content-Transfer-Encoding", "binary");
byteContent.Headers.Add("Content-ID", "<test.pdf>");
byteContent.Headers.Add("Content-Disposition", "attachment; name=\"test.pdf\"; filename=\"test.pdf\"");
multipart.Add(byteContent);
var result = Client.PostAsync(endpoint, multipart).Result;
result.EnsureSuccessStatusCode();
var resultString = result.Content.ReadAsStringAsync().Result;
TResponse resultTyped = XmlHelper.ToObject<TResponse>(resultString);
return resultTyped;
}
我需要为 MTOM 配置的绑定中的二进制部分设置特定的内容类型 (application/pdf)。
我可以在配置或代码中设置内容类型吗?对于文件部分,它总是 "Content-Type: application/octet-stream"
配置:
<binding name="FileTransferServicesBinding"
closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00"
messageEncoding="Mtom" transferMode="Buffered"
maxBufferSize="67108864" maxReceivedMessageSize="67108864">
<security mode="Transport" />
</binding>
当前发送到服务的内容。文件的多部分 header 应该是 application/pdf 而不是 application/octetstream
POST https://somewebservice HTTP/1.1
MIME-Version: 1.0
Authorization: Basic authhash
SOAPAction: ""
Host: webservicehost
Content-Length: 24517
Expect: 100-continue
Accept-Encoding: gzip, deflate
--uuid:b8366a06-3ecc-4bc4-9809-8c87ad459981+id=1
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Header>[The soapmessageitself]</s:Envelope>
--uuid:b8366a06-3ecc-4bc4-9809-8c87ad459981+id=1
Content-ID: <http://tempuri.org/1/636787311873257476>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream // This must be application/pdf
%PDF-1.4
%
1 0 obj
<</Type /Catalog/Pages 2 0 R>> ...
我不得不猜测您正在对请求的响应进行编码。如果是这样,您只需将内容类型 header 添加到您的外发消息中:
OperationContext
.Current
.OutgoingMessageHeaders
.Add ("Content-Type", "application/pdf" );
不能 100% 确定,但您可能需要先清除任何现有的传出 Content-Type header。我认为您的消息是 MTOM-encoded 并不重要。理论上是透明的。
另外...只是问...当流式消息传输占用的内存少得多时,为什么允许 64MB 的缓冲消息...而且可能同样快或更快?您几乎可以保证在重负载服务中获得更好的性能。
更新:
如果您有幸使用网络 api-type 绑定 (WebHttpBinding)...您可以非常轻松地流式传输您的内容,指定内容类型。这与我在上面的回答中所写的并没有什么不同,但是正如您所回答的那样,那些 header 去错了地方。
相反,它应该是:
WebOperationContext
.Current
.OutgoingResponse
.ContentType = "application/pdf";
有一个很棒的 article/example here 可以充分发展这个想法...以符合您的目标的方式。
如果您绑定到旧的 soap 绑定,那么您可能需要编写自定义消息编码器。该技术足够简单(如果乏味)。基本思想是您可以控制以适合您需要的方式编写消息的 body。
编码器本身很简单。围绕它的样板将是乏味的部分。自定义编码器源自抽象 MessageEncoder。要做的重要事情是覆盖 ContentType
、MediaType
、MessageVersion
属性以及 ReadMessage
和 WriteMessage
方法。 ReadMessage
和 WriteMessage
方法有多种变体用于处理流式和缓冲消息。
丑陋的部分是您必须编写部署编码器的样板。您已经创建了一个编码器工厂和一个自定义绑定元素。不可怕,但很吵。示例代码包中有几个完整的蜡球示例(包括工厂和绑定元素支持)here...可以节省您一些时间。
随着时间的推移,我通过手动构建 soap 调用解决了这个问题。由于这个问题有赏金,我将标记做相同但使用 WCF 库作为正确答案的答案。下面的代码示例 "byteContent.Headers.Add("Content-Type", "application/pdf; name=test.pdf");"我需要用 WCF
做什么static HttpClient client;
static object mutex = new object();
static HttpClient Client
{
get
{
if (client == null)
{
lock (mutex)
{
if (client == null)
{
client = new HttpClient();
client.DefaultRequestHeaders.Add("MIME-Version", "1.0");
client.DefaultRequestHeaders.Add("SOAPAction", "\"\"");
client.DefaultRequestHeaders.ConnectionClose = true;
client.DefaultRequestHeaders.ExpectContinue = false;
client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(Username + ":" + Password)));
}
}
}
return client;
}
}
public static TResponse Send<TResponse>(string endpoint, string payload, byte[] file)
{
string boundary = Guid.NewGuid().ToString("N");
MultipartContent multipart = new MultipartContent("related", boundary);
multipart.Headers.Remove("Content-Type");
multipart.Headers.TryAddWithoutValidation("Content-Type", "multipart/related; boundary=\"" + boundary + "\"");
multipart.Add(new StringContent(payload, Encoding.UTF8, "text/xml"));
ByteArrayContent byteContent = new ByteArrayContent(file);
byteContent.Headers.Remove("Content-Type");
byteContent.Headers.Add("Content-Type", "application/pdf; name=test.pdf");
byteContent.Headers.Add("Content-Transfer-Encoding", "binary");
byteContent.Headers.Add("Content-ID", "<test.pdf>");
byteContent.Headers.Add("Content-Disposition", "attachment; name=\"test.pdf\"; filename=\"test.pdf\"");
multipart.Add(byteContent);
var result = Client.PostAsync(endpoint, multipart).Result;
result.EnsureSuccessStatusCode();
var resultString = result.Content.ReadAsStringAsync().Result;
TResponse resultTyped = XmlHelper.ToObject<TResponse>(resultString);
return resultTyped;
}