让WCF支持Http调用

自己有一个项目是使用WCF写的,使用的是wsHttpBind,现在的新需求是要做Web版的,如果再写一套WebApi那工作量有点大了

WCF是可以使用Http进行访问的,只要在配置中开启了

 <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
          <serviceDebug includeExceptionDetailInFaults="true"   httpHelpPageEnabled="true"/>
        </behavior>
      </serviceBehaviors>

  

但因为是使用的wsHttpBind进行绑定,所以有一个问题,http在发送POST或GET请求时,需要将参数封闭成Soap式的XML信封形式,然后写入到请求正文中,大致如下

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<!-- 请求方法名称 --> <Login xmlns="http://tempuri.org/" xmlns:a="http://schemas.datacontract.org/2004/07/PSerivce" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<!-- 请求参数 name --> <name>super</name>
<!-- 请求参数 pwd-->
<pwd>123456</pwd>
<!-- 复杂参数 person-->
<person xmlns:a="http://schemas.datacontract.org/2004/07/PublicModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <a:gender>boy</a:gender>
</person>
</Login>
</s:Body>
</s:Envelope>

  

上面的信封的意思就是我要 访问的方法是Login,有三个参数,name,pwd,以及一个自定义类型person,可以看到

封装成xml的形式很麻烦,特别是针对复杂类型,而wcf返回的正文也是一样的形式,很不好解析,而且在请求标头里还需要加一个自定义的SOAPAction属性,值为 "http://tempuri.org/IPublicSerivce/Login" (注意,引号也是必须的,该值的组成为 "命名空间/契约接口/方法名称"

换另一种更简单的方式

这是配置文件

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5.2" />
    <httpRuntime targetFramework="4.5.2"/>
  </system.web>

  <!--配置WCF-->
  <system.serviceModel>

    <!--配置行为-->
    <behaviors>
      <!--终结点行为-->
      <endpointBehaviors>
        <!--配置一个web行为-->
        <behavior name="WebBehavior">
          <!--以下参数的释义
          1.是否启用帮助页面
          2.参数与响应的封装类型(此处为都封装)
          3.默认的返回主体格式化(此处为返回json格式)
          4.是否自动选择请示正文和响应正文的格式化(如果为true,则会先去找http请示标头上的Accept,
            再找契约方法上有无 RequestFormat/ResponseFormat,如果没有,则找此配置中有无
            defaultOutgoingResponseFormat
          5.启用异常输出
          -->
          <webHttp helpEnabled="true"
                   defaultBodyStyle="Wrapped"
                   defaultOutgoingResponseFormat="Json"
                   automaticFormatSelectionEnabled="true"
                   faultExceptionEnabled="true"
                     />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
          <serviceDebug includeExceptionDetailInFaults="true"   httpHelpPageEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <!--添加绑定-->
    <bindings>
      <!--添加web绑定-->
      <webHttpBinding>
        <!--是否启用跨域-->
        <binding   crossDomainScriptAccessEnabled="true" />
      </webHttpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"  />
    <!-- 我们手动添加 -->
    <services>
      <!--添加一个服务-->
      <service name="PublicSerivce.PSerivce">
        <!--绑定一个用来http访问的web终结点
          注意该终结点的属性 address 因为这里绑了两个终结点
          所以它们的路径不能一致,使用http调用的时候
          此处的地址可能为 http://localhost:56599/pSerivcce.svc/SerivceWeb/请示的方法名称
        -->
        <endpoint address="SerivceWeb"
                  behaviorConfiguration="WebBehavior"
                binding="webHttpBinding"
                contract="PublicInterFace.IPublicSerivce"   />

        <!--绑定第二个终结点-->
        <endpoint binding="basicHttpBinding"
                  contract="PublicInterFace.IPublicSerivce" />
      </service>
    </services>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        若要在调试过程中浏览 Web 应用程序根目录,请将下面的值设置为 True。
        在部署之前将该值设置为 False 可避免泄露 Web 应用程序文件夹信息。
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

  

配置好了文件后,在契约接口中应当打上WebGet或WebInvoke标记

WebGet:允许http以Get方式调用,但要指定UriTemplete(指定参数形式,一般来说不太常用,请参见 https://docs.microsoft.com/zh-cn/dotnet/framework/wcf/feature-details/uritemplate-and-uritemplatetable

WebInovke:默认为POST请求,如果要使用Get,则设置其Method属性

以下为契约接口示例

[ServiceContract(SessionMode = SessionMode.Allowed)]
    public interface IPublicSerivce
    {
     //这个标记还是可以使用以前的访问模式 [OperationContract]
          //下面这个标记是允许使用http方式调用 //因为在配置文件中已经设置了默认的 requestMessageFormat,responseMessageFormat,BodyStyle,所以此处不用设置属性 //而UriTemplate未设置,则直接使用方法名称 (UriTemplate可以给方法指定别名) [WebInvoke] string GetErroMsg(); [OperationContract] [WebInvoke] bool Login(string name1, string pwd, out UserInfoModel userInfo); [OperationContract] [WebInvoke] bool FindPartsForType(string name, out DataTable dt); [OperationContract] [WebInvoke] bool FindTypeForPart(string name, out DataTable dt); }

  

现在就OK了,现在我们就可以使用http调用wcf了,以下为POSTMAN的调用截图

示例为调用 

FindTypeForPart

请求头

 

 

 

请求主体

 

 响应

 

 

可以看到,请求和响应的时候都是json,但out参数datatable类型却输出的是xml,这个暂时我还不知道怎么弄成json的

 

另外特别注意

参数是自定义类型:

 需要完整属性,不能是自动属性,否则返回的json中属性名称可能就不是你的预期了(自己试下就知道了)

 另外如果是DateTime属性,最好换成DateTime?类型,而且此属性一定不能给其赋值 DateTime.MinValue,否则会进入两次调用,而且wcf会关闭连接导致获取不到响应(这个我也不知道为嘛)

 

posted @ 2021-01-31 14:10  人不自在  阅读(1054)  评论(0编辑  收藏  举报