如何覆盖 WebServiceHostFactory 的 MaxReceivedMessageSize?

8

有很多类似的问题,但我已经尝试了所有的解决方案都没有成功。

我们有一个使用WebServiceHostFactory初始化的Web服务,但如果超过64k,就会出现“400 Bad Request”的错误。通常,这只需要增加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个回答

9
为了方便其他人,上面的答案走了正确的道路,但其中有许多错误,并且需要更改才能正常使用(因此OP无法使解决方案起作用)。 使用以下内容将起到“开箱即用”的效果。

注意 - 提供所有下面的构造函数并在工厂中使用构造函数重载是非常重要的,否则您将收到以下错误消息:

InitializeRuntime 要求初始化 Description 属性。 在 CreateDescription 方法中提供有效的 ServiceDescription,或者覆盖 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。


我并不想覆盖任何绑定属性,但是对于构造函数和InitializeRuntime错误的注释给予+1。这正是我所需要的! - AdmSteck
在VB .Net中,我应该使用.New()关键字来创建构造函数吗? - Thomas
非常有帮助。我一直在尝试使用ApplyConfiguration()来添加终结点行为,但是WebServiceHost直到OnOpening()时才创建其终结点,所以这个方法帮助了我。 - kenchilada

5
如果您正在实现自定义服务主机,您应该能够覆盖名为“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" %>

谢谢Rajesh。然而,这对我来说仍然不太起作用——由于某种原因,在调用ApplyConfiguration时没有端点,因此它甚至无法进入foreach循环。由于工厂正在执行所有的描述创建操作,您将如何让它在触发ApplyConfiguration之前创建端点? - James R
您是否可以发布您的工厂代码,以便提供更多帮助? - Rajesh
当然,我已经更新了问题的代码。声明看起来像这样:<%@ ServiceHost Language="C#" Debug="true" Service="APINamespace.Service" CodeBehind="Service.svc.cs" Factory="WcfService1.MyWebServiceHostFactory" %>在我的代码中,APIUsers.TestString被打印出来,只是为了确保这段代码正在运行。结果是here0(0 = 端点计数)。 - James R
我已经更新了我的Web.config,但答案是肯定的。我在某个地方读到,当你使用webservicehostfactory时,servicemodel部分基本上会被忽略。还要注意声明现在是 <%@ ServiceHost Language="C#" Debug="true" Service="APINamespace.SCAPIService" CodeBehind="SCAPIService.svc.cs" Factory="WcfService1.MyWebServiceHostFactory" %>。 - James R
我已经尝试了上面的代码,使用了设置为webHttpBinding的服务,并像上面所说,在ApplyConfiguration方法中获取了端点。此外,我还添加了如何使用上述服务主机工厂代码的说明。 - Rajesh
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接