WCF protobuf endpoint 400 bad request

我的未来我决定 提交于 2021-01-29 07:05:23

问题


I have WCF service with DataContract json serialization. I would like to add service endpoints to consume Protobuf data messages.

I tried to use nugget package ProtoBuf.Services.WCF. Added endpoint via web.config configuration. However, every request on protobuf endpoint with address "proto" returns 400 Bad request. Web.config sample is written bellow. Endpoint with default address "" works properly.

Get method:

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

HTTP 400 BAD REQUEST: 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>

Please, which part of configuration is defective. Eventualy, what is proper way to call Get method on "proto" WCF endpoint to avoid HTTP 400 Bad request message?


回答1:


Unfortunately, I have failed to implement ProtoBuf.Services.WCF and decided to use another approach. In general, WCF by default uses DataContractSerializer.

After reading this article, I realized It is possible to replace this serializer with another one, f.e. protobuf serializer in this library. So I created behavior extension, which replaces DataContractSerializer with my custom ProtobufSerializer. In configuration added another endpoint, which has set behavior extension to use my custom 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);
        }
    }

Dispatch formatter:

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

Extension element:

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

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

Web config:

<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 is name of my custom namespace inside application structure. Hope this helps someone.



来源:https://stackoverflow.com/questions/55919691/wcf-protobuf-endpoint-400-bad-request

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