让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会关闭连接导致获取不到响应(这个我也不知道为嘛)