【问题标题】:Two endpoint (soap ,json) and one service method两个端点(soap,json)和一个服务方法
【发布时间】:2011-09-09 09:42:42
【问题描述】:

我有那个服务

        [OperationContract]
    [WebGet(UriTemplate = "/GetData")]
    List<FieldInfo> GetSerializedData();

和 web.config

<system.web>
    <webServices>
      <protocols>
        <add name="HttpGet" />
        <add name="HttpPost" />
      </protocols>
    </webServices>
    <httpRuntime  executionTimeout="90" maxRequestLength="1048576" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100"/>
    <compilation debug="true" targetFramework="4.0"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="webHttpBindingSettings" maxBufferPoolSize="524288" maxReceivedMessageSize="654321" sendTimeout="00:10:00" closeTimeout="00:01:00" openTimeout="00:10:00" receiveTimeout="00:10:00">
          <security mode="None">
            <transport clientCredentialType="None" />
          </security>
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
        </binding>
      </webHttpBinding>
      <wsHttpBinding>
        <binding name="wsHttpBindingSettings" maxBufferPoolSize="524288" maxReceivedMessageSize="654321" sendTimeout="00:10:00" closeTimeout="00:01:00" openTimeout="00:10:00" receiveTimeout="00:10:00">
          <security mode="None">
            <transport clientCredentialType="None" />
          </security>
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="MetadataBehavior" name="ServiceModel.Service">
        <endpoint name="soap" address="soap" behaviorConfiguration="Default" binding="wsHttpBinding"
          bindingConfiguration="wsHttpBindingSettings" contract="ServiceModel.IService" />
        <endpoint name="Json" address="json" behaviorConfiguration="JSON" binding="webHttpBinding"
        bindingConfiguration="webHttpBindingSettings" contract="ServiceModel.IService" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://service.com/Service.svc/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MetadataBehavior">
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="JSON">
          <webHttp automaticFormatSelectionEnabled="true"/>
          <dataContractSerializer maxItemsInObjectGraph="10000000"/>
        </behavior>
        <behavior name="Default">
          <dataContractSerializer maxItemsInObjectGraph="10000000"/>
        </behavior>
      </endpointBehaviors>
    </behaviors>

为什么在客户端只生成一个端点?

    <client>
  <endpoint address="http://service.renault.com/Service.svc/soap"
    binding="wsHttpBinding" bindingConfiguration="soap" contract="ServiceReference1.IService"
    name="soap" />
</client>

我的意思是从 asp.net 页面代码隐藏执行服务方法,并且 wcf 在soap 或 json 中返回数据取决于 ContentType。但是当它有 text/html 内容时,如何将 asp.net 页面内容类型设置为 application/json。我理解它有问题。

【问题讨论】:

  • 我认为问题出在客户端。您可以发布有关客户电话的详细信息吗?
  • 在客户端只生成一个端点——soap ;/

标签: wcf json soap asp.net-4.0


【解决方案1】:

为什么在客户端只生成一个端点?

因为 WCF 不会为非 SOAP 端点发出元数据。与 SOAP 的 WSDL 和 MEX 不同,“REST”端点没有广泛使用的元数据格式(WADL 是其中之一,但它使用不多,也没有被 WCF 实现),因此添加服务参考(或 svcutil)只会看到元数据中有一个端点,并且只会创建那个端点。

我想使用 WCF 功能,该功能根据请求的 ContentType 选择适当的序列化类型

JSON 与 XML 是一种序列化类型决定; JSON 与 SOAP 不是(SOAP 是一个定义良好的协议,具有请求应该是什么样子的规则) - 请参阅 WCF Dynamic Response Format 的更多信息。您的webHttBinding-endpoint 将执行此操作(根据传入请求返回 JSON 或 XML),因为您启用了自动格式选择,但您使用此服务的方式不需要使用 WCF 客户端 - 使用 @ 987654325@, HttpWebRequest 应该可以正常工作。

【讨论】:

  • 其他问题是即使我在浏览器中测试服务时添加 json 端点到客户端输入 service.renault.com/Service.svc/soap/GetSerializedDataservice.renault.com/Service.svc/json/GetSerializedData.Json 结果总是返回当肥皂没有和 WCF 跟踪返回“有一个问题从网络接收到的 XML。有关更多详细信息,请参阅内部异常。---> System.Xml.XmlException: 无法读取消息正文,因为它是空的"
  • 您不能使用浏览器直接调用 SOAP 端点(即,在地址栏上键入地址)。所有 SOAP 请求都是通过 HTTP POST 发出的,浏览器会发送一个 GET 请求来获取在其栏中输入的地址。
【解决方案2】:

如果可能,尝试像这样设计您的 Visual Studio:

  • 解决方案
    • 有合同的项目(仅限 IXXXXService)
    • 具有实现和所有端点的 Web 项目(参考合同项目)
    • clients 项目不使用 VS 生成的代理,而是可以选择正确端点等协议的工厂。 (参考合同项目)

这是我在类似于您的场景中使用的示例类:

public class ServiceHelper
{

    /// <summary>
    /// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service 
    /// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
    /// </summary>
    /// <typeparam name="TService">The type of the service to use</typeparam>
    /// <param name="action">Lambda of the action to performwith the service</param>
    [System.Diagnostics.DebuggerStepThrough]
    public static void UsingProxy<TService>(Action<TService> action)
        where TService : ICommunicationObject, IDisposable, new()
    {
        var service = new TService();
        bool success = false;
        try
        {
            action(service);
            if (service.State != CommunicationState.Faulted)
            {
                service.Close();
                success = true;
            }
        }
        finally
        {
            if (!success)
            {
                service.Abort();
            }
        }
    }
    /// <summary>
    /// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service 
    /// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
    /// </summary>
    /// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
    /// <param name="action">Action to perform with the client instance.</param>
    /// <remarks>In the configuration, an endpoint with names that maches the <typeparamref name="TIServiceContract"/> name
    /// must exists. Otherwise, use <see cref="UsingContract&lt;TIServiceContract&gt;(string endpointName, Action<TIServiceContract> action)"/>. </remarks>
    [System.Diagnostics.DebuggerStepThrough]
    public static void UsingContract<TIServiceContract>(Action<TIServiceContract> action)
    {
        UsingContract<TIServiceContract>(
            typeof(TIServiceContract).Name,
            action
            );
    }
    /// <summary>
    /// WCF proxys do not clean up properly if they throw an exception. This method ensures that the service 
    /// proxy is handeled correctly. Do not call TService.Close() or TService.Abort() within the action lambda.
    /// </summary>
    /// <typeparam name="TIServiceContract">The type of the service contract to use</typeparam>
    /// <param name="action">Action to perform with the client instance.</param>
    /// <param name="endpointName">Name of the endpoint to use</param>
    [System.Diagnostics.DebuggerStepThrough]
    public static void UsingContract<TIServiceContract>(
          string endpointName,
          Action<TIServiceContract> action)
    {
        var cf = new ChannelFactory<TIServiceContract>(endpointName);
        var channel = cf.CreateChannel();
        var clientChannel = (IClientChannel)channel;

        bool success = false;
        try
        {
            action(channel);
            if (clientChannel.State != CommunicationState.Faulted)
            {
                clientChannel.Close();
                success = true;
            }
        }
        finally
        {
            if (!success) clientChannel.Abort();
        }
    }
}

在客户端配置中,我手动设置了我的引用:

<system.serviceModel>
<client>
  <endpoint address="http://localhost/myapp/myservice.svc/soap"
            binding="wsHttpBinding"
            contract="MyProject.Contracts.IMyService"
            name="IMyServiceSoap"/>
  <endpoint address="http://localhost/myapp/myservice.svc/rest"
            binding="webHttpBinding"
            contract="MyProject.Contracts.IMyService"
            name="IMyServiceRest"/>

</client>
</system.serviceModel>

然后,在您的代码中,您可以简单地调用:

        ServiceHelper.UsingContract<"IMyServiceSoap", MyProject.Contracts.IMyService>(
            svc => svc.DoTheJob()
            );

        ServiceHelper.UsingContract<"IMyServiceRest", MyProject.Contracts.IMyService>(
            svc => svc.DoTheJob()
            );

[edit] 服务器配置与此类似:

<services>
<service name="MyService">
    <endpoint address="soap"
    binding="wsHttpBinding"
                        contract="MyContracts.IMyService"/>
    <endpoint address="rest"
    binding="webHttpBinding"
                        contract="MyContracts.IMyService"/>
    <endpoint address="mex"
                        binding="mexHttpBinding"
                        contract="IMetadataExchange"/>
</service>
</services>

【讨论】:

  • 但在您的解决方案中,soap 或 json 序列化取决于我,但我想使用 WCF 功能,该功能选择正确的序列化类型取决于请求的 ContentType
  • 序列化类型取决于端点(及其绑定)。没有办法改变单端点的序列化类型。我可能遗漏了一些东西,什么决定了请求的内容类型?也就是说,使用soap协议或rest协议的条件是什么?
  • 我的服务是在 asp.net 页面上调用的,并且依赖接受类型(application/json 或 application/soap+xml)应该返回正确的数据。现在我得到 json 但肥皂失败了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
  • 2013-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
相关资源
最近更新 更多