Как переопределить WebServiceHostFactory MaxReceivedMessageSize?

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

У нас есть веб-сервис, который инициализируется с помощью WebServiceHostFactory, но если на него выдается больше 64 КБ, мы получаем "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 ответа

Решение

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

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

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" %>

В интересах других, приведенный выше ответ идет по правильному пути, но содержит много ошибок и не будет работать без изменений (следовательно, 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.

Другие вопросы по тегам