WCF Protobuf端点400错误请求

时间:2019-04-30 11:13:25

标签: c# wcf protocol-buffers

我有带有DataContract json序列化的WCF服务。我想添加服务端点以使用Protobuf数据消息。

我尝试使用掘金包ProtoBuf.Services.WCF。 通过web.config配置添加了端点。但是,protobuf端点上地址为“ proto”的每个请求都返回400错误请求。 Web.config示例编写如下。具有默认地址“”的端点可以正常工作。

获取方法:

HTTP 200 OK http://localhost:65460/BeaconService.svc/GetData

HTTP 400错误请求:http://localhost:65460/BeaconService.svc/proto/GetData

<system.serviceModel>
   <bindings>
      <webHttpBinding>
         <binding transferMode="Streamed">
            <security mode="None" />
         </binding>
      </webHttpBinding>
      <basicHttpBinding>
         <binding messageEncoding="Mtom">
            <security mode="None" />
         </binding>
      </basicHttpBinding>
   </bindings>
   <extensions>
      <behaviorExtensions>
         <add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net" />
      </behaviorExtensions>
   </extensions>
   <services>
      <service behaviorConfiguration="DefaultServiceBehavior" name="Services.BeaconService">
         <endpoint address="" behaviorConfiguration="httpBehavior" binding="webHttpBinding" contract="Services.IBeaconService" />
         <endpoint address="proto" behaviorConfiguration="protoBehavior" binding="basicHttpBinding" contract="Services.IBeaconService" />
      </service>
   </services>
   <behaviors>
      <endpointBehaviors>
         <behavior name="protoBehavior">
            <protobuf />
         </behavior>
         <behavior name="httpBehavior">
            <webHttp />
         </behavior>
      </endpointBehaviors>
   </system.serviceModel>

请,配置的哪一部分有缺陷。最终,在“原始” WCF端点上调用Get方法以避免HTTP 400错误请求消息的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

不幸的是,我未能实现ProtoBuf.Services.WCF,因此决定使用另一种方法。通常,WCF默认情况下使用DataContractSerializer

阅读此article之后,我意识到可以用另一个序列号,例如library中的protobuf序列化程序。因此,我创建了行为扩展,用我的自定义ProtobufSerializer代替了DataContractSerializer。在配置中添加了另一个端点,该端点已将行为扩展设置为使用我的自定义ProtobufSerializer。

WebHttpBehavior:

public class ProtobufBehavior : WebHttpBehavior
    {
        protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
        {
            return new ProtobufDispatchFormatter(operationDescription);
        }

        protected override IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
        {
            return new ProtobufDispatchFormatter(operationDescription);
        }
    }

调度格式化程序:

namespace Services.Extension.ProtobufSerializationExtension
{
    public class ProtobufDispatchFormatter : IDispatchMessageFormatter
    {
        OperationDescription operation;
        bool isVoidInput;
        bool isVoidOutput;

        public ProtobufDispatchFormatter(OperationDescription operation)
        {
            this.operation = operation;
            this.isVoidInput = operation.Messages[0].Body.Parts.Count == 0;
            this.isVoidOutput = operation.Messages.Count == 1 || operation.Messages[1].Body.ReturnValue.Type == typeof(void);
        }

        public void DeserializeRequest(Message message, object[] parameters)
        {
            if (!message.IsEmpty)
            {
                XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
                bodyReader.ReadStartElement("Binary");
                byte[] rawBody = bodyReader.ReadContentAsBase64();
                MemoryStream ms = new MemoryStream(rawBody);

                using (StreamReader sr = new StreamReader(ms))
                    for (int i = 0; i < parameters.Length; i++)
                        parameters[i] = Serializer.Deserialize(operation.Messages[i].Body.Parts[i].Type, sr.BaseStream);
            }
        }

        public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
        {
            byte[] body;

            using (MemoryStream ms = new MemoryStream())
            using (StreamWriter sw = new StreamWriter(ms))
            {
                Serializer.Serialize(sw.BaseStream, result);
                sw.Flush();
                body = ms.ToArray();
            }

            Message replyMessage = Message.CreateMessage(messageVersion, operation.Messages[1].Action, new RawBodyWriter(body));
            replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
            return replyMessage;
        }

        class RawBodyWriter : BodyWriter
        {
            internal static readonly byte[] EmptyByteArray = new byte[0];
            byte[] content;
            public RawBodyWriter(byte[] content)
              : base(true)
            {
                this.content = content;
            }

            protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
            {
                writer.WriteStartElement("Binary");
                writer.WriteBase64(content, 0, content.Length);
                writer.WriteEndElement();
            }
        }
    }
}

扩展元素:

namespace Services.Extension.ProtobufSerializationExtension
{
    public class ProtobufSerializationServiceElement : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof(ProtobufBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new ProtobufBehavior();
        }
    }
}

Web配置:

<system.serviceModel>
   <bindings>
      <webHttpBinding>
         <binding transferMode="Streamed">
            <security mode="None" />
         </binding>
      </webHttpBinding>
   </bindings>
   <extensions>
      <behaviorExtensions>
          <add name="protobufExtension" type="Services.Extension.ProtobufSerializationExtension.ProtobufSerializationServiceElement, Services" />
      </behaviorExtensions>
   </extensions>
   <services>
      <service behaviorConfiguration="DefaultServiceBehavior" name="Services.BeaconService">
         <endpoint address="" behaviorConfiguration="httpBehavior" binding="webHttpBinding" contract="Services.IBeaconService" />
         <endpoint address="proto" behaviorConfiguration="protoBehavior" binding="webHttpBinding" contract="Services.IBeaconService" />
      </service>
   </services>
   <behaviors>
      <endpointBehaviors>
         <behavior name="protoBehavior">
            <webHttp/>
            <protobufExtension/>
         </behavior>
         <behavior name="httpBehavior">
            <webHttp />
         </behavior>
      </endpointBehaviors>
   </system.serviceModel>

Services.Extension.ProtobufSerializationExtension是我在应用程序结构中的自定义名称空间的名称。希望这对某人有帮助。