Как переопределить WebServiceHostFactory MaxReceivedMessageSize? - PullRequest
7 голосов
/ 06 декабря 2011

Существует много подобных вопросов, но я попробовал каждое решение в каждом из них, но безрезультатно.

У нас есть веб-сервис, который инициализируется с помощью WebServiceHostFactory, но если он больше 64kбросается на это, мы получаем «400 плохих запросов».Обычно это можно решить путем увеличения MaxReceivedMessageSize, MaxBufferSize и MaxBufferPoolSize.Проблема заключается в том, что при использовании WebServiceHostFactory объект Web.Config полностью игнорируется.Никакие изменения, которые я делаю в разделе ServiceModel, не отражаются в сервисе вообще.

Было бы неплохо просто полностью отказаться от WebServiceHostFactory и настроить web.config с нуля, но наш сервис не будет работать без него.Один из методов имеет параметр потока, а также некоторые другие строковые параметры.Без фабрики мы получаем

System.InvalidOperationException: For request in operation Test to be a stream the operation must have a single parameter whose type is Stream

Так что убрать фабрику невозможно.Я не могу точно понять, что делает фабрика, которая исправляет эту ошибку, но я потратил 4 дня на нее и нигде не получил.

Я также попытался программно переопределить MaxReceivedMessageSize, с некоторыми примерами, которые я нашел на околоместо:

protected override void OnOpening()
        {
            base.OnOpening();
            foreach (var endpoint in Description.Endpoints)
            {

                //var binding = endpoint.Binding as WebHttpBinding;
                //if (binding != null)
                //{
                //    binding.MaxReceivedMessageSize = 20000000;
                //    binding.MaxBufferSize = 20000000;
                //    binding.MaxBufferPoolSize = 20000000;
                //    binding.ReaderQuotas.MaxArrayLength = 200000000;
                //    binding.ReaderQuotas.MaxStringContentLength = 200000000;
                //    binding.ReaderQuotas.MaxDepth = 32;
                //}


                //var transport = endpoint.Binding.CreateBindingElements().Find<HttpTransportBindingElement>();
                //if (transport != null)
                //{
                //    transport.MaxReceivedMessageSize = 20000000;
                //    transport.MaxBufferPoolSize = 20000000;
                //}


                var newTransport = new HttpTransportBindingElement();
                newTransport.MaxReceivedMessageSize = 20000000;
                newTransport.MaxBufferPoolSize = 20000000;
                endpoint.Binding.CreateBindingElements().Add(newTransport);
            }
        }

Первое не работает, поскольку фабрика создает CustomBinding, который не может быть приведен к WebHttpBinding.Второе не работает, так как кажется, что обязательные элементы доступны только для чтения - независимо от того, на что я установил элементы, ничего не меняется, что я проверял, читая значения обратно после их «изменения».Третья была последней попыткой бросить туда новый связующий элемент, но, конечно, это тоже не удалось.

Теперь мы полностью в замешательстве.Как мы можем заставить эту вещь работать?Вы думаете, это было бы так просто!

Спасибо, ребята

Web.Config

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <compilation targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="rest" maxReceivedMessageSize="500000000" />
      </webHttpBinding>
    </bindings>
    <services>
      <service name="SCAPIService" behaviorConfiguration="ServiceBehaviour">
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="rest" contract="ISCAPIService" behaviorConfiguration="web">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviour">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
  </system.webServer>
</configuration>

Редактировать, используя предлагаемое исправление, которое до сих пор не удалось:

public class MyServiceHost : WebServiceHost
    {
        public MyServiceHost()
        {
        }

        public MyServiceHost(object singletonInstance, params Uri[] baseAddresses)
            : base(singletonInstance, baseAddresses)
        {
        }

        public MyServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
        }

        protected override void ApplyConfiguration()
        {
            base.ApplyConfiguration();
            APIUsers.TestString += "here" + Description.Endpoints.Count.ToString();
            foreach (var endpoint in this.Description.Endpoints)
            {
                var binding = endpoint.Binding;
                APIUsers.TestString += binding.GetType().ToString();
                if (binding is WebHttpBinding)
                {
                    var web = binding as WebHttpBinding;
                    web.MaxBufferSize = 2000000;
                    web.MaxBufferPoolSize = 2000000;
                    web.MaxReceivedMessageSize = 2000000;
                }
                var myReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas();
                myReaderQuotas.MaxStringContentLength = 2000000;
                myReaderQuotas.MaxArrayLength = 2000000;
                myReaderQuotas.MaxDepth = 32;
                binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null);
            }
        }
    }

    class MyWebServiceHostFactory : WebServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new MyServiceHost(serviceType, baseAddresses);
        }
    }

Ответы [ 2 ]

7 голосов
/ 11 января 2012

В интересах других, приведенный выше ответ идет по правильному пути, но содержит много ошибок и не будет работать без изменений (следовательно, OP не смог заставить решение работать).Используйте следующее, и оно будет работать для вас «из коробки».

Примечание - Это чрезвычайно важно предоставить все перечисленные ниже конструкторы и использовать конструкторПерегрузка продемонстрирована на заводе, в противном случае вы получите следующее сообщение об ошибке:

InitializeRuntime требует инициализации свойства Description.Либо предоставьте допустимую ServiceDescription в методе CreateDescription, либо переопределите метод InitializeRuntime, чтобы предоставить альтернативную реализацию

Пользовательская реализация ServiceHost:

public class CustomServiceHost : ServiceHost
{
    public CustomServiceHost()
    {
    }

    public CustomServiceHost(object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses)
    {
    }

    public CustomServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)
    {
    }

    protected override void  OnOpening()
    {
        base.OnOpening();

        foreach (var endpoint in this.Description.Endpoints)
        {
            var binding = endpoint.Binding;
            var web = binding as WebHttpBinding;

            if (web != null)
            {
                web.MaxBufferSize = 2147483647;
                web.MaxReceivedMessageSize = 2147483647;
            }

            var myReaderQuotas = new XmlDictionaryReaderQuotas { MaxStringContentLength = 2147483647 };

            binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null);
        }
    }
}

Пользовательская реализация ServiceHostFactory:

public sealed class CustomServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new CustomServiceHost(serviceType, baseAddresses);
    }
}

Приведенная выше разметка Service1.svc будет работать до тех пор, пока тип указан правильно.Я фактически использовал эту технику, чтобы переопределить WebServiceHost и разрешить больший размер ответов JSON, но все принципы должны также применяться к ServiceHost.

3 голосов
/ 06 декабря 2011

Если вы реализуете свой собственный хост настраиваемой службы, вы должны иметь возможность переопределить метод под названием «ApplyConfiguration», где вы можете связать все свойства конфигурации, необходимые для вашей привязки.Пример кода, показанный ниже:

РЕДАКТИРОВАТЬ: Добавление реализации моей фабрики сервисных хостов

public class MyServiceHost : System.ServiceModel.ServiceHost
{
    public MyServiceHost () { }

    public MyServiceHost (Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)
    {

    }

    public MyServiceHost (object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses)
    {

    }

protected override void ApplyConfiguration()
            {
                Console.WriteLine("ApplyConfiguration (thread {0})", System.Threading.Thread.CurrentThread.ManagedThreadId);
                base.ApplyConfiguration();
                foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
                {
                    Binding binding = endpoint.Binding;
                    var binding = endpoint.Binding;
                    if(binding is WebHttpBinding)
                    {
                        var web = binding as WebHttpBinding;
                        web.MaxBufferSize = 2000000;
                        web.MaxReceivedMessageSize = 2000000;
                    }
                    var myReaderQuotas = new XmlDictionaryReaderQuotas();
                    myReaderQuotas.MaxStringContentLength = 5242880;
                    binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null); 
                }
            }

}

Вышеприведенное переопределяет вашу конфигурацию каждой привязки и устанавливает MaxStringContentLength.

public sealed class MyServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
    {
        public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            return base.CreateServiceHost(constructorString, baseAddresses);
        }

        protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new MyServiceHost(serviceType, baseAddresses);
        }
    }

Теперь моя разметка файла Service.svc имеет следующее:

<%@ ServiceHost Language="C#" Debug="true" Factory="Sample.MyServiceHostFactory" Service="Sample.ReaderQuotasService" CodeBehind="ReaderQuotasService.svc.cs" %>
...