Restful API 服务端编写和连接Sql Server数据库实现数据持久化(一)

1.项目准备

一.开发环境 vs2015

二.net framework版本4.5.2     

三.Nuget安装SqlSugar(第三方中间件,非常好用)  官网:https://www.donet5.com/Home/Doc?typeId=1226

四.接口要求:请求和响应的主体是JSON结构

五.实例实现功能:添加 (POST),其他功能大同小异

Restful表达行为

  • GET(SELECT):从服务器取出资源(一项或多项)
  • POST(CREATE):在服务器新建一个资源
  • PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)
  • PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)
  • DELETE(DELETE):从服务器删除资源

2.接下来开始写Restful 服务端,如何实现一个Rest风格的web服务供外部调用。

主要包括以下4点:

  • 定义service的契约
  • 定义URL Routing
  • 实现service
  • 为服务编写宿主程序

1、定义service的契约和URL Routing

  先定义服务契约,这里我介绍最常见的两种方式,分别采用GET和POST方式访问,使用VS2015创建一个新的控制台工程,命名为RestFulService。

并为该工程添加引用System.ServiceModel 和System.ServiceModel.Web

一,编写接口ITaskInfoQuery
 

 创建一个接口类文件,命名为ITaskInfoQuery。为了让.Net FrameWork识别这是一个service接口,我们需要给接口添加上ServiceContract特性,并且给接口定义的方法添加OperationContract特性。这里我介绍最常见的两种方式,分别采用GETPOST方式访问。我们可以看到,与普通WCF服务契约不同的是,需要额外用WebGet或者WebInvoke指定REST访问的方式。另外还要指定消息包装样式和消息格式,默认的消息请求和响应格式为XML,若选择JSON需要显式声明。 UriTemplate用来将方法映射到具体的Uri上,但如果不指定映射,将映射到默认的Uri。

 

namespace RestFulService
{
    [ServiceContract(Name = "TaskInfoQueryServices")]
    public interface ITaskInfoQuery
    {
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "TaskInfoInsert/Task", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        MessagesInfo InstrtInfo(Task task);
    }
}

特性说明

ServiceContract 特性里面的Name 填写接口实现类型的名称

UriTemplate 里面相当于是访问路由 (例如,如果InstrtInfo是一个插入的方法,规定接口服务地址是http://127.0.0.1:7788/ 路由定义的访问名称是:TaskInfoInsert/Task)

                    那么最后第三方就可以外部调用:http://127.0.0.1:7788/TaskInfoInsert/Task  来进行数据插入操作。
WebGet默认请求是GET方式。
WebInvoke请求方式有POST、PUT、DELETE等,所以需要明确指定Method是哪种请求的。
RequestFormat规定客户端必须是什么数据格式请求的(JSon或者XML),不设置默认为XML
ResponseFormat规定服务端返回给客户端是以是什么数据格返回的(JSon或者XML)

MessagesInfo 实体类说明。这个类主要是用来外部调用接口时反馈自定义状态。例子中,当外部调用该接口时,将反馈回任务ID,自定义Status,和消息说明Message

  [DataContract]

    public class MessagesInfo
    {
        [DataMember]
        public long? Id { get; set; }

        [DataMember]
        public int Status { get; set; }
        [DataMember]
        public string Message { get; set; }
    }

 反馈例子:

{
"Id": 789456139,
"Message": "成功", //执行失败,备注具体失败原因。
"Status": 1
}

 Task 实体类说明。这个类的字段跟数据库表结构字段一样,也就是当外部调用接口时,是需要他们往数据库表里面插入一个ID或者是其他一些信息(根据需求定)。

     就比如当前我这当表只有两个字段,一个是任务ID,一个是任务类型。那么我就这样定义

  [SugarTable("Transport")]
    [DataContract]
    public class Task
    {
        [DataMember]
        public long? Id { get; set; }

        [DataMember]
        public int TaskType { get; set; }

       
    }

 SugarTable("Transport") 说明:当数据库表明跟实体类定义不一样时,可以用过SugarTable特性来设置。

例如:我的数据库表名是Transport 但是为了安全或者是出于其他考滤,建立的实体类名是Task,那么就可以使用  [SugarTable("Transport")] 进行映射 。

        这个特性来自第三方中间件ORM.  官网:https://www.donet5.com/Home/Doc?typeId=1182

      因为使用到第三方ORM进行数据库CRUD(增删改查),所以这里需要这样配置。

2、实现service

    二.实现service就是实现定义ITaskInfoQuery 接口

上面说到,ServiceContract特性里面的Name 填写接口实现类型的名称,那就建立一个实现类,名称就叫 TaskInfoQueryServices

  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, IncludeExceptionDetailInFaults = true)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class TaskInfoQueryServices : ITaskInfoQuery
    {
        private SqlSugarClient db;
        public TaskInfoQueryServices()
        {
            db = Singleton.GetInstance();
        }
        public MessagesInfo InstrtInfo(Task task)
        {
            bool flag = db.Queryable<Task>().Where(it => it.Id == task.Id).Any();
            if (flag) return new MessagesInfo() { Id = task.Id, Status = 0, Message = "存在重复ID" };
            if (db.Insertable(task).UseSqlServer().ExecuteBlueCopy() > 0) return new MessagesInfo() { Id = task.Id, Status = 1, Message = "成功" };
            return new MessagesInfo() { Id = task.Id, Status = 0, Message = "插入数据到数据库操作失败" };
        }
    }
    /// <summary>
    /// 单例模式的实现
    /// </summary>
    public class Singleton
    {
        // 定义一个静态变量来保存类的实例
        private static SqlSugarClient uniqueInstance;

        // 定义一个标识确保线程同步
        private static readonly object locker = new object();

        // 定义私有构造函数,使外界不能创建该类实例
        private Singleton()
        {

        }
        /// <summary>
        /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
        /// </summary>
        /// <returns></returns>
        public static SqlSugarClient GetInstance()
        {
            // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
            // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
            // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
            // 双重锁定只需要一句判断就可以了
            if (uniqueInstance == null)
            {
                lock (locker)
                {
                    // 如果类的实例不存在则创建,否则直接返回
                    if (uniqueInstance == null)
                    {
                        uniqueInstance = new SqlSugarClient(new ConnectionConfig()
                        {
                            ConnectionString = "Data Source=.;Initial Catalog=TestDemo;User ID=sa;Password=123",
                            // ConnectionString = ConfigurationManager.AppSettings["ConnectionString"],
                            DbType = DbType.SqlServer,
                            IsAutoCloseConnection = true
                        });
                    }
                }
            }
            return uniqueInstance;
        }
    }

  说明:

  1.数据库连接使用的单例模式是参考网上的实现,如果有侵权,删除 。

3、服务编写宿主程序

  上面我们定义了Service接口,并实现了Service方法,现在我们需要将编写宿主程序,以便能够使客户端通过GET或者POST方法进行请求。

class Program
    {
        #region 禁用关闭按键


        private const int MF_BYCOMMAND = 0x00000000;
        public const int SC_CLOSE = 0xF060;

        [DllImport("user32.dll")]
        public static extern int DeleteMenu(IntPtr hMenu, int nPosition, int wFlags);// 删除菜单

        [DllImport("user32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);// 获取系统菜单句柄

        [DllImport("kernel32.dll", ExactSpelling = true)]
        private static extern IntPtr GetConsoleWindow();// 获取控制台窗口句柄
        #endregion
        static void Main(string[] args)
        {
            try
            {
                Console.Title = "RestFulService";
                WebServiceHost _serviceHost = new WebServiceHost(typeof(TaskInfoQueryServices), new Uri("http://127.0.0.1:7788/"));
                _serviceHost.Open();
                Console.ForegroundColor = ConsoleColor.DarkCyan;
                Console.WriteLine("Web服务已开启...");
                Console.ForegroundColor = ConsoleColor.DarkCyan;
                Console.WriteLine("输入指令关闭服务程序:");
                #region 禁用关闭按键
                DeleteMenu(GetSystemMenu(GetConsoleWindow(), false), SC_CLOSE, MF_BYCOMMAND);
                //使用命令关闭
                while (true)
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    if (Console.ReadLine().ToLower() == "exit")
                    {
                        _serviceHost.Close();
                        return;
                    }
                    Console.WriteLine("指令不正确!请重新输入");
                    Console.ForegroundColor = ConsoleColor.Red;
                }
                #endregion
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Web服务开启失败:{0}\r\n{1}", ex.Message, ex.StackTrace);
            }

        }

    }
}

 为了防止控制台关闭,参考了网上实现,禁用控制台程序关闭按钮。链接:https://www.cnblogs.com/xinjian/archive/2010/07/23/1783358.html

Restful 服务端参考链接:https://www.cnblogs.com/xuliangxing/p/8735552.html

上面的实现结合网上例子和个人总结,如有侵权。删除!

接下来第二讲,进行测试Postman测试和客户端调用测试 https://blog.csdn.net/weixin_39237340/article/details/117031046?spm=1001.2014.3001.5502

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小丫头呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值