.net core实践系列之短信服务-Sikiro.SMS.Api服务的实现(一)

本文涉及的产品
短信服务,100条 3个月
短信服务,200条 3个月
数字短信套餐包(仅限零售电商行业),100条 12个月
简介: .net core实践系列之短信服务-Sikiro.SMS.Api服务的实现(一)

前言


上篇《.net core实践系列之短信服务-架构设计》介绍了我对短信服务的架构设计,同时针对场景解析了我的设计理念。本篇继续讲解Api服务的实现过程。


源码地址:https://github.com/SkyChenSky/Sikiro.SMS


此服务会使用.NET Core WebApi进行搭建,.NET Core WebApi基础原型就是RESTful风格,然而什么叫RESTful呢。


REST API简介


REST


Representational State Transfer的缩写,翻译为“表现层状态转化”,是由Roy Thomas Fieding在他的博士论文《Architectural Styles and the Design of Network-based Software Architectures》中提出的一种架构思想。


而他的论文中提出了一个RESTful应用应该具备的几点约束。


  • 每个资源都应该有一个唯一的标识
  • 每一个对象或资源都可以通过一个唯一的URI进行寻址,URI的结构应该是简单的。
  • 使用标准的方法来更改资源的状态
  • GET、POST、PUT、PATCH、DELETE
  • Request和Response的自描述
  • 资源多重表述
  • URI所访问的每个资源都可以使用不同的形式加以表示(XML或JSON)
  • 无状态的服务
  • 不需要保存会话状态(SESSION),资源本身就是天然的状态,是需要被保存的。


RESTful


当某Web服务遵守了REST这些约束条件和原则,那么我们可以称它设计风格就是 RESTful。


三特点


REST有三大特点:

  • 资源(名词)
  • 动作(动词)
  • 表述(超文本)



1650718264(1).png


资源


抽象的说他可以是音频、也可以是视频,更可以是订单。更俗讲其实就是实体,更接*我们*常说的“类(class)”。另外REST强调资源有唯一的URI。下面有对比


动作


主要动作:

  •  GET:检索单个资源;
  •  POST:主要是创建资源,但是GET的参数长度受限,因此也可以用在复杂参数的检索资源场景;
  •  PUT:更新资源所有属性,也可以称为替换资源;
  •  PATCH:更新资源部分属性;
  •  DELETE:删除资源;


表述


对于Request与Response的自描述,而表述方式有多种:XML、JSON等,强调HTTP通信的语义可见性。


对比


RPC


SMSApi.com/api/GetSMS


SMSApi.com/api/CreateSMS


传统的接口设计面向过程的,每个动作有特定的URI。


REST


SMSApi.com/api/SMS  GET


SMSApi.com/api/SMS  POST


REST API每个资源只有唯一的URI,而资源可以有不同的动作执行相应的接口


RPC的更加倾向于面向过程,而RESTful则以面向对象的思想进行设计。


接口定义


回到我们的短信服务,以上面的三特点进行出发,SMS不需要由外部服务进行删除、修改资源因此:


资源:SMS

动作:GET、POST

表述方式:我们约定Request、Response为JSON格式


/// <summary>
    /// 短信接口
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class SmsController : ControllerBase
    {
        private readonly SmsService _smsService;
        private readonly IBus _bus;
        public SmsController(SmsService smsService, IBus bus)
        {
            _smsService = smsService;
            _bus = bus;
        }
        /// <summary>
        /// 获取一条短信记录
        /// </summary>
        /// <param name="id">主键</param>
        /// <returns></returns>
        [HttpGet("{id}")]
        public ActionResult<SmsModel> Get(string id)
        {
            if (string.IsNullOrEmpty(id))
                return NotFound();
            var smsService = _smsService.Get(id);
            return smsService.Sms;
        }
        /// <summary>
        /// 发送短信
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Post([FromBody] List<PostModel> model)
        {
            _smsService.Add(model.MapTo<List<PostModel>, List<AddSmsModel>>());
            _smsService.SmsList.Where(a => a.TimeSendDateTime == null)
                .ToList().MapTo<List<SmsModel>, List<SmsQueueModel>>().ForEach(item =>
                {
                    _bus.Publish(item);
                });
            return Ok();
        }
        /// <summary>
        /// 查询短信记录
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost("_search")]
        public ActionResult<List<SmsModel>> Post([FromBody] SearchModel model)
        {
            _smsService.Search(model.MapTo<SearchModel, SearchSmsModel>());
            return _smsService.SmsList;
        }
    }


功能描述


由上可见一共定义了三个接口


  • GET   http://localhost:port/api/sms/id 获取一条短信记录
  • POST http://localhost:port/api/sms 发送短信
  • POST http://localhost:port/api/sms/_search 查询短信记录


获取一条短信记录就不多解析了


查询短信记录


动作我使用了POST,有人会问检索资源不是用GET么?对,但是GET的参数在URL里是受限的,因此在复杂参数的场景下应该选择POST,然而我是模仿elasticsearch的复杂查询时定义,添加多一个节点/_search申明此URI是做查询的。


发送短信


此接口的实现逻辑主要两件事,持久化到MongoDB,过滤出及时发送的短信记录发送到RabbitMQ。


在持久化之前我做了一个分页的动作,我们提供出去的接口,同一条短信内容支持N个手机号,但是不同的短信运营商的所支持一次性发送的手机数量是有限的。


开始实现时,我把分页发送写到队列消费服务的发送短信逻辑里,但是这里有个问题,如果分页后部分发送成功,部分发送失败,那么这个聚合究竟以失败还是成功的状态标示呢?换句话来说我们无法保证聚合内的数据一致性。


因此我的做法就是优先在分页成多个文档存储,那么就可以避免从数据库取出后分页导致部分成功、失败。


public void Add(List<AddSmsModel> smsModels)
        {
            DateTime now = DateTime.Now;
            var smsModel = new List<SmsModel>();
            foreach (var sms in smsModels)
            {
                var maxCount = _smsFactory.Create(sms.Type).MaxCount;
                sms.Mobiles = sms.Mobiles.Distinct().ToList();
                var page = GetPageCount(sms.Mobiles.Count, maxCount);
                var index = 0;
                do
                {
                    var toBeSendPhones = sms.Mobiles.Skip(index * maxCount).Take(maxCount).ToList();
                    smsModel.Add(new SmsModel
                    {
                        Content = sms.Content,
                        CreateDateTime = now,
                        Mobiles = toBeSendPhones,
                        TimeSendDateTime = sms.TimeSendDateTime,
                        Type = sms.Type
                    });
                    index++;
                } while (index < page);
            }
            SmsList = smsModel;
            _mongoProxy.BatchAddAsync(SmsList);
        }


目录
相关文章
|
1月前
|
API
阿里云短信服务文档与实际API不符
阿里云短信服务文档与实际API不符
|
1月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
24 1
|
1月前
|
API
阿里云短信平台API错误码提示错误天级流控显示小时级错误码
阿里云短信平台API错误码提示错误天级流控显示小时级错误码
|
1月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
96 9
|
1月前
|
存储 开发框架 .NET
.NET 8 实现无实体库表 API 部署服务
【10月更文挑战第12天】在.NET 8中,可通过以下步骤实现无实体库表的API部署:首先安装.NET 8 SDK及开发工具,并选用轻量级Web API框架如ASP.NET Core;接着创建新项目并设计API,利用内存数据结构模拟数据存储;最后配置项目设置并进行测试与部署。此方法适用于小型项目或临时解决方案,但对于大规模应用仍需考虑持久化存储以确保数据可靠性与可扩展性。
|
2月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
1月前
|
监控 安全 API
Docker + .NET API:简化部署和扩展
Docker + .NET API:简化部署和扩展
39 0
|
1月前
|
监控 安全 API
最完美的扩展Docker + .NET API:简化部署和扩展
最完美的扩展Docker + .NET API:简化部署和扩展
81 0
|
1月前
|
API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
28 0
|
3月前
|
存储 NoSQL Redis
【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)
【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)
【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)
下一篇
无影云桌面