C/S程序,发布Web接口(WCF Rest)

前言:本人在某家企业的职位为后端工程师、C#桌面应用程序开发工程师。前段时间收到产品经理的新需求,想要在公司的客户服务器上部署的Windows服务上公开一系列Web接口,供第三方外包公司调用。

刚收到这个需求时,简直是慌,因为之前做的事情,要不就是做个完整的Windows服务,要不就是做个完成的桌面应用程序,再接触过一个全栈的MVC项目。现在要在后端程序上提供Web接口?(what?一堆堆的黑人问号脸),这这这。。。。还有这种做法的吗?(当然,从理论上来讲,肯定是可以实现的,只不过之前没做过,不会做而已)后来经过做前端的同事一番指导,果不其然的完成了该需求,Windows服务启动后,提供的http接口,Web程序完美调用(撒花),此文章纪念一下自己在“攻城师”的道路上又前进了一步,记录下,以供将来自己再使用的时候做参考。

接口程序已经完成了一段时间了,第三方公司没反馈有任何异常,今天抽空把之前写的接口程序重新读了一遍,对比之前看过了WebApi,恍然大悟,原来就那么一回事,没当时编码的时候想的那么复杂,接下来,文章中讲述以后端程序为宿主公开Web接口。

材料:VS2015,框架:.NET FrameWork 4.5.2,接口调试工具:PostMan,数据传输格式:Json

方式:WCF Rest(应该是这么叫吧?是本人在做该项目的时候,经前端开发指导写的,技术名称网上查了一下,好像这么叫的,如果不对,希望知道的网友在底下留言指出,谢谢)

如下图为该项目下本人写的类库分层(简单的三层设计架构,因为是要正式使用的程序,所以写的要尽量规范),主程序为Windows服务程序(读者测试的话,可以直接使用控制台程序进行测试)

首先,先来看看如何在后端程序中启动这种公开Web接口的服务:代码如下

        private void StartRestService()
        {
            try
            {
                IPAddress localIp = null;
                IPAddress[] ipArray;
                ipArray = Dns.GetHostAddresses(Dns.GetHostName());
                localIp = ipArray.First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
                if (localIp == null)
                {
                    localIp = IPAddress.Parse("127.0.0.1");
                }
                string s = string.Format("http://{0}:33333", localIp);
                Uri baseAddress = new Uri(string.Format("http://{0}:33333",localIp));
                _serviceHost = new WebServiceHost(typeof(Realize), baseAddress);
                _serviceHost.Opened += delegate
                {
                    Global.mylog.WriteLine("RestService已开启..."); //本人的日志打印,读者可略过
                };
                _serviceHost.Open();
            }
            catch (Exception ex)
            {
                Global.mylog.WriteLine(ex.ToString());
            }
        }

代码很简单,我甚至觉得比开启一个TCP/IP还要简单。说的粗糙一些就是,有一个叫 WebServiceHost 的类,将这个类实例化。该类的构造函数中有两个参数,入代码所示,第一个参数的注释解释为叫服务类型,其实就是将之后实现接口的类,入下图所示。第二参数需要传入一个叫 Uri 的对象,其实就是这个WEB接口的IP地址和端口号,这个IP和端口号供日后前端调用时使用,本人代码中获取的是本地的IP地址,端口号固定为33333。OK了,WebServiceHost被成功实例,接下来只需要 Open 就可以了,代码中,本人调用的是Opened,目的是为了Open成功后调用日志打印,因为这是本人项目中要正式使用的代码,加入了日志方便日后调试,读者此处可以直接 _serviceHost.Open() 即可,效果是一样的,都是启动了 WebServiceHost 服务。

接触下来看接口,文件夹打开后,底下其实就一个接口代码:如图

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace KJTX.Analyzer.DealWithWeb
{
    [ServiceContract(Name = "Realize")]
    public interface IInterFace
    {
        //一、人员信息接口API
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "api/GetEmployee?employeeNum={employeeNum}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string GetEmployee(int employeeNum);
        //二、基站基础信息接口API
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "api/GetWorkSitesInfo", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string GetWorkSitesInfo();
        //三、基站三维坐标接口API
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "api/SetWorkSiteInfo?workSiteNum={workSiteNum}&x={x}&y={y}&z={z}&orientation={orientation}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string SetWorkSiteInfo(int workSiteNum, float x, float y, float z,string orientation);
        //四、出井信息接口API
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "api/GetOutMineEmployees", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string GetOutMineEmployees(OutMineModelArr outMineModelArr);
        //五、考勤记录查询接口API
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "api/GetAttendance?employeeNum={employeeNum}&startTime={startTime}&endTime={endTime}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string GetAttendance(string employeeNum, string startTime, string endTime);
        //六、轨迹记录查询接口API
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "api/GetTrack?employeeNum={employeeNum}&startTime={startTime}&endTime={endTime}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string GetTrack(string employeeNum, string startTime, string endTime);
        //七、实时位置信息API
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "api/GetRealTimeLocation", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        string GetRealTimeLocation();
    }
}

以上所有接口中均规定采用POST请求的方式调用,代码中的 ServiceContract 代表一种契约关系,括号中的Name为接口实现类的类名。在所有接口中可以看到其实有三类接口(本人自己总结的),以下对这三类接口进行一些解释(假设本程序部署的IP地址为192.168.8.24,端口号为33333):

第一类:例如:api/GetWorkSitesInfo、api/GetRealTimeLocation。

此类接口是最简单的接口,作用是,前端只需要输入正确的URL,无需传入任何参数,就能取到相关的内容信息。前端调用示例如:http://192.168.8.24:33333/api/GetWorkSitesInfo,这样就能获取到该接口提供的信息。

第二类:例如:api/GetEmployee?employeeNum={employeeNum}、api/GetAttendance?employeeNum={employeeNum}&startTime={startTime}&endTime={endTime}。

此类接口为,前端在调用的时候在Url上提供明文参数,就能获取到接口的相关信息,其中得注意的是等号(=)左边参数的名称一定要与对应的方法中的参数的名称相同(映射关系),不然接口对不上。前端调用示例如:http://192.168.8.24:33333/api/GetEmployee?employeeCardNum=100001,这样就能获取到该接口提供的信息。

第三类:例如:api/GetOutMineEmployees。

此类接口中,后端代码接收的参数为一个实体,前端调用示例为http://192.168.8.24:33333/api/GetOutMineEmployees,除次之外还得传入参数,且参数必须为Json格式(因为在接口中已经规定好了,看代码),参数示例为:{"OutMineModelArrObj":[{"EmployeeNum":100001},{"EmployeeNum":100002}]},其中需要注意的是,参数中的 OutMineModelArrObj,必须是这个名称,别问为什么,还是映射关系(我觉得前后端交互麻烦就是这样,各种映射关系),至于和谁映射,读者请先记住,参数类型是 OutMineModelArr,下面看这个参数的实体类代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;

namespace KJTX.Analyzer.DealWithWeb
{
    public class OutMineModel
    {
        public int EmployeeNum { get; set; }
    }
    [DataContract]
    public class OutMineModelArr
    {
        [DataMember]
        public OutMineModel[] OutMineModelArrObj { get; set; }
    }
}

[DataContract](数据契约):定义了远程访问对象和可供调用的方法,数据契约则是服务端和客户端之间要传送的自定义数据类型。一旦声明一个类型为DataContract,那么该类型就可以被序列化在服务端和客户端之间传送。只有声明为DataContract的类型的对象可以被传送,且只有成员属性会被传递,成员方法不会被传递。WCF对声明为DataContract的类型提供更加细节的控制,可以把一个成员排除在序列化范围以外,也就是说,客户端程序不会获得被排除在外的成员的任何信息,包括定义和数据。默认情况下,所有的成员属性都被排除在外,因此需要把每一个要传送的成员声明为DataMember。(本段解释来自于:DataContract和DataMember的作用 - 大海的泡沫 - 博客园

所以,实体类中 OutMineModelArrObj 已经标记为了前后端之间传送的数据类型,这也可以解释为什么,后端接收的实体参数,对象名称必须为 OutMineModelArrObj。

温馨提示:读者可以在途中看到,本人在Model的文件夹下有好多个实体类,这些类都是项目中前后端交互是实体模型,为了安全起见,入下图,凡是与前端有染的事宜模型,我都加入了 [DataContract] 和 [DataMember],防止之后调试的时候没出现我们想要的数据,那时候就头疼了。(其实,本人测试了一下,好像只要不是作为接收实体(例如上面的 OutMineModelArr实体),其实不需要做[DataContract] 和 [DataMember] 的声明)

       

接下来看一下接口实现类的代码:因为代码比较多,本人在此就只贴了上面叙述的三类接口中各一个方法作为代表

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Text;
using System.Threading.Tasks;
namespace KJTX.Analyzer.DealWithWeb
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, IncludeExceptionDetailInFaults = true)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Realize : IInterFace
    {
        /// <summary>
        /// 人员信息接口实现
        /// </summary>
        /// <returns></returns>
        public string GetEmployee(int employeeNum)
        {
            return DataBusiness.Instance().GetEmployeeJson(employeeNum);
        }
        /// <summary>
        /// 基站信息接口实现
        /// </summary>
        /// <returns></returns>
        public string GetWorkSitesInfo()
        {
            return DataBusiness.Instance().GetWorkSitesInfoJson();
        }
        /// <summary>
        /// 出井查询接口实现
        /// </summary>
        /// <returns></returns>
        public string GetOutMineEmployees(OutMineModelArr outMineModelArr)
        {
            return DataBusiness.Instance().GetOutMineEmployeesJson(outMineModelArr);
        }
    }
}

最终的接收到的数据都是下一层(业务层:DataBusiness)返回的Json格式的字符串,然后把这些字符串返回给上层(前端)。代码中的 [ServiceBehavior] 和 [AspNetCompatibilityRequirements] 各位读者如果想理解,可以自行到网上搜索,这里本人理解不足,光知道需要这么做,就不做解释,避免误导。

代码看到这里,基本上后段的Web接口服务就已经写完了,底层的业务层(BLL => DataBusiness)和数据层(DAL => DataService)的代码就不贴了,就分别是业务处理和数据库交互的一些代码。

完成了代码编写后,我们现在看看调用,本人使用的测试工具是 PostMan 比较推荐,这个软件我也没怎么学,拿来直接就用上了,很简单。

先看调用第一类接口的结果:如下图

调用第二类接口的结果:如下图(跟上图一样的解释就不多做赘述)

调用第三类接口:如下图(跟上图一样的解释就不多做赘述)

至此,整个项目就完成了。。

本文章作者纯手码字,引用部分已在文章中注明出处地址,如有侵权,请及时联系作者删除。如有雷同,万分荣幸。如果错误或者疑问,请在留言区留言,本人看到后会及时回复。

感谢阅读!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值