C#桌面程序(winform)如何一步步集成内置WebApi(owin技术),解耦IIS,并将Api接收信息推给桌面窗体控件展示

最近工厂有个需求,服务器上部署了一个服务,此服务要把信息推送给现场多台工控机上的CS上位机程序。由于涉及到多个软件之间的通信,做架构时,首先排除掉中间表形式,从效率和稳定性上也排除掉了Socket,最后采用了WebApi接口形式来做通信。但是有个问题,上位机程序都为运行稳定的CS架构程序,当时并没有考虑后台服务WebApi的需求。如果现在给每个上位机程序再做个后端WebApi部署在IIS上,那势必会造成很多开发资源浪费(制造业IT嘛,本身开发就那么几个,呵呵~)。
所以后来思考,能不能做个内置WebApi的dll包,方法事件都写好,上位机软件只要加载这个dll包就能在本地上位机上跑WebApi服务,接收并处理WebApi接收到的消息,大大减小大家的开发工作量。结果还真有办法,就是使用owin技术。owin是什么,大家可以百度下,大致意思就是它允许你在本地运行WebService,从而解耦web服务器IIS。
那怎么集成owin呢,方法如下:

一、创建dll项目及导入所需依赖库

打开VS,建立class library项目,命名为OWINServer
建立项目
打开Nuget,导入下列所需依赖库
所需依赖包

二、建立Startup类

在OWINServer项目中,创建第一个类Startup类,用以启动owin

    public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            try
            {
                HttpConfiguration config = new HttpConfiguration();
                config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
                config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }

                );
                appBuilder.UseWebApi(config);
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }
    }

三、建立事件类TriggerEvent

在OWINServer项目中,建立第二个类OWINTriggerEvent事件类,用来保存接口收到的信息,并用事件触发的方式将收到的接口信息通知桌面应用。

    public class OWINTriggerEvent
    {
        public delegate void GetInfoEventHandler(object sender, EventArgs e);
        public event GetInfoEventHandler infoEvent;

        //存储信息变量
        public string Message = "";

        //编写引发事件的函数(在程序任意域使用)
        public void OnMessage()
        {
            if (infoEvent != null)
            {
                //发送信息
                infoEvent(this, new EventArgs());
            }
        }

    }

四、建立ApiController接口类

在OWINServer项目中,建立第三个类OWINApiController,用以实现被外部调用的接口。当收到信息后触发OWINTriggerEvent事件,将消息传给桌面前端应用。

  public class OWINApiController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<string> Get()
        {
            return new string[] { "Success"};
        }

        // GET api/<controller>/5
        public string Get(int id)
        {
            return string.Format("owin {0} by:linezero", id);
        }

        // POST api/<controller>
        public string Post([FromBody] string value)
        {
            //将接收到的信息赋给事件类中Message属性
            OWINStart.TriggerEvent.Message = value;
            //触发事件类中事件,将消息传递给桌面程序绑定的方法
            OWINStart.TriggerEvent.OnMessage();
            return "Success";
        }

        // PUT api/<controller>/5
        public string Put([FromBody] string value)
        {
            return "Success";
        }

        // DELETE api/<controller>/5
        public string Delete([FromBody] string value)
        {
            return "Success";
        }
    }

这里只在Post接口里,写了简单接收消息和触发事件的代码,各位可根据需要在里面写其他逻辑代码,但还是建议里面不多写逻辑,功能界限只作为消息传递,将消息传递给桌面前端后,由前端对消息进行分析处理。

五、建立OWINStart类

上面3个工具类写完后,再写个集成类OWINStart,用以对以上3个类集成,作为对外的唯一调用类,方便第三方软件对OWINServer.dll的使用。

    public class OWINStart
    {
        public static OWINTriggerEvent TriggerEvent { get; set; }
        public static OWINTriggerEvent Start(string ServerUrl)
        {
            try
            {
                //调用Startup启动owin,url需要调用方传入
                WebApp.Start<Startup>(url: ServerUrl);

                //新建OWINTriggerEvent类实例,并返回被调用方使用获取里面的message和infoEvent事件
                var triggerEvent = new OWINTriggerEvent();
                TriggerEvent = triggerEvent;

                HttpClient client = new HttpClient();
                //通过get请求数据,测试owin服务是否正常开启
                var response = client.GetAsync(ServerUrl+"api/owinapi").Result;
                if (response.IsSuccessStatusCode)
                {
                    return triggerEvent;
                }
                else
                {
                    throw new Exception("Owin loacal server start failed!");
                }


            }
            catch (Exception)
            {
                throw;
            }

        }
    }

六、编译OWINServer项目,获得dll文件

项目结构如下,直接编译项目
项目结构

获得如下dll文件
编译文件
OWINServer.dll包通过前面6步就已经生成了,下面就是创建个桌面引用,集成并测试一下它好不好用了~

七、新建桌面项目,导入OWINServer.dll

新建一个owin的桌面项目,将OWINServer.dll包导入
导入包
简单画几个桌面控件,按钮用来启动owin,一个输入框用来输入开启的owin服务的url地址,另一个用来显示接口接到的消息。
在这里插入图片描述

八、启动owin服务,接收并显示消息

在winform后端写如下代码,调用OWINStart.Start启用owin服务,并给infoEvent事件绑定处理方法GetInfo()。

    public partial class Form1 : Form
    {
        private OWINTriggerEvent _triggerEvent;
        public Form1()
        {
            InitializeComponent();
        }

        private void Start_button_Click(object sender, EventArgs e)
        {
            try
            {
                if (textBox2.Text != "")
                {
             ;
                    string baseAddress = "http://" + textBox2.Text + ":9121/";
                    _triggerEvent = OWINStart.Start(baseAddress);
                    _triggerEvent.infoEvent += GetInfo;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void GetInfo(object sender, EventArgs e)
        {
            try
            {
           		//将接口收到的消息传给textbox控件
                Action actionDelegate = () => { this.textBox1.Text = _triggerEvent.Message; };
                this.textBox1.Invoke(actionDelegate);
            }
            catch (Exception ex)
            {

                throw;
            }

        }
    }

写完后,运行winform,点击Start按钮,随后打开postman进行接口调试。
在这里插入图片描述
可以发现postman成功的将信息通过调用owin接口,将信息传给了桌面的textbox控件。

九、注意事项

(1)调用OWINServer.dll可能会报找不到依赖包的错误,请检查第6步中的dll包是否都放在你的项目dll文件夹中和OWINServer.dll文件在一起。
如果还是报Could not load file or assembly ‘Microsoft.Owin, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)":"Microsoft.Owin, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35错误
请在app.config中加如以下指向代码:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.2.9.0" newVersion="5.2.9.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.2.9.0" newVersion="5.2.9.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

(2)最近在调试中又出现一个新的问题,发现在调试程序的时候WebApp.Start(url: ServerUrl)可以成功执行,并开启服务。但是跑生成的执行程序时就会报异常错误,啥异常也没说明,只是报内部异常。尝试了N多次,加了N个log也没找到问题,最后查阅资料,发现这原来是缺少管理员权限,而引起了一个被拒绝的内部异常。用管理员权限打开执行程序可以正常开启服务并打开程序。

结语

以上就实现了桌面程序内置WebApi,可以用来进行软件之间的通信。当有软件需要使用此webApi功能时,只要导入这个OWINServer.dll包即可。
dll包下载地址:https://download.csdn.net/download/weixin_44239774/87155725
由于个人不经常上CSDN,需要源码的同学不要留言了,直接关注微信公众号:小默快跑,关注后可以直接私信,或用微信扫描下面二维码直接关注,博主看到私信后会回复的,有时间还可以一起交流下编码过程中的问题。
在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐