Customize WCF Envelope and Namespace Prefix
10749 단어 namespace
Posted on January 8th 2014 by Vana Genica Cosmin
Introduction
WCF allows you to customize the response using DataContract, MessageContract or XmlSerializer. With DataContract you have access only to the body of the message while MessageContract will let you control the headers of the message too. However, you don't have any control over namespace prefixes or on the soap envelope tag from the response message.
When generating the response, WCF uses some default namespaces prefixes and we cannot control this from configuration. For example,
Real world example
Recently, I was asked to rewrite an old service using .NET and WCF, keeping compatibility with existing clients. The response from WCF looked like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetCardInfoResponse xmlns="https://vanacosmin.ro/WebService/soap/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<control_area>
<source>OVF</source>
<ns1:source_send_date>2014-01-06T14:15:37.1505943+01:00</ns1:source_send_date>
<api_key/>
<message_id>27970411614463393270</message_id>
<correlation_id>1</correlation_id>
</control_area>
<chip_uid>1111</chip_uid>
<tls_engraved_id>************1111</tls_engraved_id>
<reference_id/>
<is_blocked>false</is_blocked>
<is_useable>false</is_useable>
<registration_date>2013-12-13T13:06:39.75</registration_date>
<last_modification>2013-12-20T15:48:52.307</last_modification>
</GetCardInfoResponse>
</s:Body>
</s:Envelope>
The expected response for existing clients was:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"xmlns:ns1="https://vanacosmin.ro/WebService/soap/">
<SOAP-ENV:Body>
<ns1:GetCardInfoResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ns1:control_area>
<ns1:source>OVF</ns1:source>
<ns1:source_send_date>2014-01-06T14:15:37.1505943+01:00</ns1:source_send_date>
<ns1:api_key/>
<ns1:message_id>27970411614463393270</ns1:message_id>
<ns1:correlation_id>1</ns1:correlation_id>
</ns1:control_area>
<ns1:chip_uid>1111</ns1:chip_uid>
<ns1:tls_engraved_id>************1111</ns1:tls_engraved_id>
<ns1:reference_id/>
<ns1:is_blocked>false</ns1:is_blocked>
<ns1:is_useable>false</ns1:is_useable>
<ns1:registration_date>2013-12-13T13:06:39.75</ns1:registration_date>
<ns1:last_modification>2013-12-20T15:48:52.307</ns1:last_modification>
</ns1:GetCardInfoResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The two messages are equivalent from SOAP or XML perspective. What I needed to do in order to obtain the expected result was:
Possible solutions
There are multiple extension points where the message can be altered
Custom MessageEncoder
You can alter the xml output using a MessageEncoder. A good example about this can be found here .
This approach has several disadvantages, as the author also pointed out in the end of the article:
Custom MessageFormatter and a derived Message class
The MessageFormatter is used to transform the result of your method into an instance of Message class. This instance is then passed in the WCF pipeline (message inspectors, channels, encoders). This is the right place to transform your message because all the other extension points will work with the exact same message that you are sending to your clients
The following diagram shows how the message is sent accross different layers in WCF pipeline. You can see that the MessageFormatter is just before the MessageInspector when you send a message from server to client, while the MessageEncoder is a side component which is activated right before the transport layer.
Additional information about the diagram can be found here
Creating a custom MessageFormatter
First, you need to create a IDispatchMessageFormatter class. This is the message formatter. The SerializeReply method will return an instance of your custom Message class.
public class MyCustomMessageFormatter : IDispatchMessageFormatter {
private readonly IDispatchMessageFormatter formatter;
public MyCustomMessageFormatter(IDispatchMessageFormatter formatter)
{
this.formatter= formatter;
}
public void DeserializeRequest(Message message, object[]parameters)
{
this.formatter.DeserializeRequest(message,parameters);
}
public Message SerializeReply(MessageVersion messageVersion, object[]parameters, object result)
{
var message = this.formatter.SerializeReply(messageVersion, parameters, result);
return new MyCustomMessage(message);
}
}
Inherit from Message class
Custom message class
This is the class that will allow you to alter the output of your service.
public class MyCustomMessage : Message
{
private readonly Message message;
public MyCustomMessage(Message message)
{
this.message= message;
}
public override MessageHeaders Headers
{
get { return this.message.Headers;}
}
public override MessageProperties Properties
{
get { return this.message.Properties;}
}
public override MessageVersion Version
{
get { return this.message.Version;}
}
protected override void OnWriteStartBody(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
this.message.WriteBodyContents(writer);
}
protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
{
writer.WriteStartElement("SOAP-ENV", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
writer.WriteAttributeString("xmlns", "ns1", null,"https://vanacosmin.ro/WebService/soap/");
}
}
Custom message class explained
The derived Message class has several overrides that you can use to obtain the required XML:
Activate the MessageFormatter
To activate the MessageFormatter we will create an OperationBehavior attribute that must be applied to the methods (on the interface) that we want to use this MessageFormatter.
[AttributeUsage(AttributeTargets.Method)]
public class MobilityProviderFormatMessageAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
var serializerBehavior =operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
MINCS (Mini Container Shellscripts) 설명네임스페이스의 잘라내기, overlayfs에 의한 계층적 루트 파일 시스템의 준비,/proc나/dev의 준비, chroot에 의한 컨테이너내에의 마이그레이션등이 주된 기능입니다. Docker like인 이미지의 관리...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.