Objective
The ". NET core Practice series of SMS Service-Architecture Design" introduces my architecture design of SMS service, and analyzes my design concept for the scene. This article continues to explain the implementation of API services.
Source Address: Github.com/skychensky/sikiro.sms
This service is built using. NET core Webapi, and the. NET core WEBAPI base prototype is restful, but what is restful?
Rest API Introduction Rest
Representational state Transfer's abbreviation, translated as "presentation level transition", was by Roy Thomas Fieding in his doctoral dissertation architectural Styles and the Design of network-based software architectures, an architectural idea proposed in the paper.
And his thesis puts forward a few constraints that a restful application should have.
- Each resource should have a unique identity
- Each object or resource can be addressed by a unique URI, and the structure of the URI should be simple.
- Use standard methods to change the state of a resource
- GET, POST, PUT, PATCH, DELETE
- Self-description of request and response
- Multiple representations of resources
- Each resource accessed by a URI can be represented in a different form (XML or JSON)
- Non-stateful service
- There is no need to save session state (sessions), the resource itself is a natural state, it needs to be saved.
Restful
When a Web service adheres to the rest of these constraints and principles, then we can call it design style is RESTful.
Three features
Rest has three main features:
- Resources (noun)
- Action (verb)
- Presentation (hypertext)
Resources
The abstract is that he can be audio, can also be video, and can be orders. More lectures is actually the entity, closer to what we normally call "class." In addition, rest emphasizes that the resource has a unique URI. Here's a comparison.
Action
Main actions:
- GET: Retrieves a single resource;
- POST: The main is to create resources, but get parameter length is limited, so can also be used in complex parameters of the retrieval resource scene;
- PUT: Updates all properties of a resource, or it can be called a replacement resource;
- PATCH: Update resource section properties;
- Delete: Deletes the resource;
Expression
For the self-description of request and response, there are many ways to express it: XML, JSON, etc., emphasizing the semantic visibility of HTTP communication.
Compare RPC
Smsapi.com/api/getsms
Smsapi.com/api/createsms
The traditional interface design is process-oriented and each action has a specific URI.
REST
Smsapi.com/api/sms GET
Smsapi.com/api/sms POST
REST API Each resource has only a unique URI, and the resource can have different actions to execute the corresponding interface
RPC is more prone to process-oriented, while restful is designed with object-oriented thinking.
Interface definition
Back to our SMS service, starting with the above three features, SMS does not need to be deleted by external services, modify resources so:
Resources: SMS
Action: GET, POST
How to express: we agree to request, response in JSON format
/// <summary> ///SMS Interface/// </summary>[Route ("Api/[controller]")] [Apicontroller] Public classSmscontroller:controllerbase {Private ReadOnlySMSService _smsservice; Private ReadOnlyIBus _bus; PublicSmscontroller (SMSService smsservice, IBus bus) {_smsservice=SMSService; _bus=bus; } /// <summary> ///get an SMS record/// </summary> /// <param name= "id" >PRIMARY Key</param> /// <returns></returns>[HttpGet ("{ID}")] PublicActionresult<smsmodel> Get (stringID) {if(string. IsNullOrEmpty (ID))returnNotFound (); varSMSService =_smsservice.get (ID); returnsmsservice.sms; } /// <summary> ///Send SMS/// </summary> /// <param name= "model" ></param> /// <returns></returns>[HttpPost] PublicActionResult 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); }); returnOk (); } /// <summary> ///Query SMS Records/// </summary> /// <param name= "model" ></param> /// <returns></returns>[HttpPost ("_search")] PublicActionresult<list<smsmodel>>Post ([frombody] Searchmodel model) {_smsservice.search (model. Mapto<searchmodel, searchsmsmodel>()); return_smsservice.smslist; } }
Function description
Altogether three interfaces defined by the above
- Get http://localhost:port/api/sms/id get an SMS record
- Post Http://localhost:port/api/sms Send SMS
- POST http://localhost:port/api/sms/_search Query SMS Records
Get a text message record doesn't have much to parse
Query SMS Records
I used the post, some people will ask to retrieve resources not with Get? Yes, but the get parameters are limited in the URL, so in the context of complex parameters should be selected post, but I am a complex query to imitate Elasticsearch definition, add one more node/_search declare this URI is to do the query.
Send SMS
This interface implements logic primarily for two things, persisting to MongoDB, filtering out the timely sending of SMS records sent to RABBITMQ.
Before the persistence I did a paging action, we provide out of the interface, the same text message content support n mobile phone number, but the different SMS operators support one-time send mobile phone number is limited.
When I started to implement, I sent paging to the queue consumer service to send SMS logic, but there is a problem, if the page after the partial send success, partial send failed, then this aggregation to fail or success status indicator? In other words, we cannot guarantee the consistency of the data within the aggregation.
So my approach is to prioritize paging into multiple document stores, so you can avoid paging out of the database and causing partial success and failure.
Public voidADD (list<addsmsmodel>smsmodels) {DateTime now=DateTime.Now; varSmsmodel =NewList<smsmodel>(); foreach(varSmsinchsmsmodels) { varMaxCount =_smsfactory.create (SMS. Type). MaxCount; Sms. Mobiles=SMS. Mobiles.distinct (). ToList (); varpage =Getpagecount (SMS. Mobiles.count, MaxCount); varindex =0; Do { varTobesendphones = SMS. Mobiles.skip (Index *maxCount). Take (MaxCount). ToList (); Smsmodel.add (NewSmsmodel {Content=SMS. Content, Createdatetime=Now , mobiles=Tobesendphones, Timesenddatetime=SMS. Timesenddatetime, Type=SMS. Type}); Index++; } while(Index <page); } smslist=Smsmodel; _mongoproxy.batchaddasync (smslist); }
Project-related open source framework
Easynetq
EasyNetQ.DI.Microsoft
Sikiro.Nosql.Mongo
Log4net
Mapster
Easynetq
This open source framework is a package for rabbitmq.client that hides a lot of implementation details and simplifies the way you use it. and provides a variety of IOC injection methods
Source Address: Github.com/easynetq/easynetq
Sikiro.Nosql.Mongo
This is my own. A library of common base operations for MONGO drives
Source Address: Github.com/skychensky/sikiro.nosql.mongo
Mapster
Entity mapping framework, to see the evaluation data than automapper and other such as the efficiency is high, and the ease of use is very high.
Github.com/mapstermapper/mapster
Global Exception Log records
Public classGolbalexceptionattribute:exceptionfilterattribute { Public Override voidonexception (Exceptioncontext context) {if(!context. exceptionhandled) {context. Exception.writetofile (); } Base. Onexception (context); } } Public voidconfigureservices (iservicecollection services) {services. ADDMVC (Option={option. Filters.add<GolbalExceptionAttribute>(); }) . Setcompatibilityversion (Compatibilityversion.version_2_1); }
Loggerhelper
The above writetofile is my extension of the exception method, using the Log4net log framework to record the exception, if necessary can also be written to MongoDB or Elasticsearch
/// <summary> ///Log Helper Class/// </summary> Public Static classLoggerhelper {Private Static ReadOnlyIloggerrepository Repository = Logmanager.createrepository ("netcorerepository"); Public Static ReadOnlyILog Log = Logmanager.getlogger (Repository.name,typeof(Loggerhelper)); StaticLoggerhelper () {xmlconfigurator.configure (Repository,NewFileInfo ("Log4net.config")); } #regionText Log/// <summary> ///Text Log/// </summary> /// <param name= "message" ></param> /// <param name= "ex" ></param> Public Static voidWriteToFile ( ThisException EX,stringMessage =NULL) { if(string. IsNullOrEmpty (message)) message=Ex. Message; LOG.ERROR (message, ex); } #endregion }
Packaging of the tool library
Both the framework and the tool library are available as libraries, and are reusable, but they differ in that the tool library is available out of the box, most of the calls are made in static methods, and only a few or even a few methods are used.
and framework definition, in order to implement a software component specification, provide the basic functions required by the specification of software products, and he is binding, reusable, normative. He is a semi-finished product that can be rewritten.
Therefore, in order to simplify the use of the framework, the common settings, build a combination of encapsulation, as an extension class or helper class to provide, simplified use, increased readability.
Use of swagger
The advantage of the HTTP protocol is the lightweight, cross-platform, so good flexibility that requires an interface to describe external exposure. Swagger is a good choice, no need to write your own documents and provide a background management interface, you can test, simplifying a lot of work.
I chose the Nswag.aspnetcore Open source component, his use is very simple. Only two steps are required:
1. Configure swagger:
Public voidConfigure (Iapplicationbuilder app, Ihostingenvironment env) {if(env. Isdevelopment ()) {app. Usedeveloperexceptionpage (); } app. Useswaggeruiwithapiexplorer (Settings={settings. Generatorsettings.defaultpropertynamehandling=propertynamehandling.camelcase; Settings. PostProcess= Document = ={document.Info.Version="v1"; Document.Info.Title="Sikiro. SMS. API"; Document.Info.Description="SMS Service API"; Document.Info.TermsOfService="None"; }; }); App. Usemvc (); }
2. Setting up site projects
This setting displays the interface, parameter annotations to the Swagger page
Nswag also has multiple versions of the UI selection:
- Useswaggerredoc
- Useswaggerui
- UseSwaggerUi3
Visit Http://localhost:port/swagger to see the API documentation
Deployment
Because my company is still using Windows Server 2008. Therefore, the environment installation package should be prepared before deployment:
. NET Core 2.1.3 Windows-hosting
After the installation is complete, restart the server, publish the files to the server, and edit the application pool to unmanaged code. You will have access to the
End
This article introduces the design and implementation of SIKIRO.SMS.API, and the next chapter encapsulates the SDK for API calls. If you have any suggestions, please comment the feedback below to me.