Touch your hands, use Dart language to develop back-end applications, come on!

Touch your hands, use Dart language to develop back-end applications, come on!

Preface

In the past few days, I have posted a few articles about

Dart
The article on developing back-end applications mainly introduces
Dart
Some of its advantages, such as asynchronous tasks, concurrent processing, compilation and deployment, and so on.

As the saying goes, if you just talk about it, don t practice the fake style, today we re here to really start one

Dart
Back-end application.

What application do we want to develop

Suppose we now want to develop a community application, similar to

Nuggets
,
CSDN
Wait, the basic function is for users to post articles and opinions.

Post articles, similar to traditional CMS system

Send opinions, similar to the current Weibo system

Around the core, there are tags, categories, comments, and so on.

What framework do we use

Since I plan to use

Dart
Development, a development framework is still very helpful. however
Dart
There are not many back-end frameworks,
aqueduct
,
jaguar
,
DartMars
Wait, here we use
DartMars
.

The source code is here github.com/tangpanqing...

The document is here tangpanqing.github.io/dart_mars_d...

Open the document homepage, like this

Um, thick

vuepress
taste.

Starting a project is so easy

according to

DartMars
The guidelines in the installation
Dart
After that, we can execute the following command to create the project

# Installation DartMars dart pub global activate --source git https://github.com/tangpanqing/dart_mars.git # Create a project dart pub global run dart_mars --create project_name #Enter the directory cd project_name # Get-dependent dart pub global run dart_mars --get # Start project dart pub global run dart_mars --serve dev Copy code

Touch your hands, let's take it step by step

The first step is to install DartMars

Open the command line tool and execute

dart pub global activate --source git https://github.com/tangpanqing/dart_mars.git copy the code

Thanks to the existence of the wall, I waited for nearly 1 minute and prompted me as follows:

Activated dart_mars 1.0.4 from Git repository "https://github.com/tangpanqing/dart_mars.git " Copy the code

This means that the installation is complete.

The second step is to create the project

Project tentative name

community
Community, execute the following command

dart pub global run dart_mars --create community Copy the code

After the above command,

DartMars
Got a hint

project community has been created you can change dir with command: cd community and then get dependent with command: dart pub global run dart_mars --get and then start it with command: dart pub global run dart_mars --serve dev Copy code

It means that the project has been created, then you need to enter the directory, get the dependencies, and finally execute it.

And the related commands are displayed, isn't it very considerate? When you are in love, you must be a warm man.

The third step is to enter the directory

Excuting an order

cd communityCopy code

The fourth step is to obtain dependencies

Excuting an order

dart pub global run dart_mars --get copy the code

After the above command,

DartMars
Got a hint

Got dependencies! Copy the code

Indicates that the loading dependency is complete

The fifth step, start the project

dart pub global run dart_mars --serve dev copy the code

After the above command,

DartMars
Got a hint

route config file has been updated, see ./lib/config/route.dart $ dart run bin\community.dart --serve dev INFO::2021-07-03 10:14:13.601023::0::Server::Http Server has start, port=80 INFO::2021-07-03 10:14:13.608004::1::Server::Env type is dev INFO::2021-07-03 10:14:13.624571::2::Server::Open browser and vist http://127.0.0.1:80, you can see some info Copy code

The startup is successful. From the above information, we can know:

  1. The routing configuration file has been updated,

  2. HTTP service has started, on port 80, currently using the development environment

Open the browser and visit http://127.0.0.1:80 and we will see the classic

hello worldcopy code

Continue coding step by step

Take a look at the project structure first

bin
The directory is the entry point of the executable file

lib
The directory is the development directory of the entire project

Other directories are auxiliary, as the name suggests. Next, we have to complete the basic functions step by step.

Complete the first one, add, check, modify and delete the user, and make it into a standard for later use.

Create user table

I have prepared the relevant

sql
Statement

CREATE TABLE IF NOT EXISTS ` user ( `id` int ( 11 ) NOT NULL AUTO_INCREMENT, user_id` ` VARCHAR ( 40 ) the COLLATE utf8mb4_general_ci the NOT NULL the DEFAULT '' the COMMENT 'User ID' , user_mobile` ` VARCHAR ( . 11 ) the COLLATE utf8mb4_general_ci the NOT NULL the DEFAULT '' the COMMENT 'mobile phone number' , user_password` ` VARCHAR ( 60 ) the COLLATE utf8mb4_general_ci the NOT NULL the DEFAULT '' the COMMENT 'user password' , user_nickname` ` VARCHAR ( 60 ) the COLLATE utf8mb4_general_ci the NOT NULL the DEFAULT '' the COMMENT 'Nickname' , user_avatar` ` VARCHAR ( 60 ) the COLLATE utf8mb4_general_ci the NOT NULL the DEFAULT '' the COMMENT 'User Profile' , user_description` ` VARCHAR ( 120 ) the COLLATE utf8mb4_general_ci the NOT NULL the DEFAULT '' the COMMENT " user introduction ' , `create_time` bigint ( 20 ) NOT NULL DEFAULT '0' COMMENT'create time' , `update_time` bigint ( 20 ) NOT NULL DEFAULT '0' COMMENT'Update time' , delete_time` ` BIGINT ( 20 is ) the NOT NULL the DEFAULT '0' the COMMENT 'deletion time' , a PRIMARY KEY (` id`), UNIQUE KEY user_id` `(` user_id`), KEY `user_mobile` (`user_mobile`) ) ENGINE = the InnoDB the DEFAULT the CHARSET = utf8mb4 the COLLATE = utf8mb4_general_ci the COMMENT = 'user table' ; duplicated code

Put to

mysql
To execute

Create user model

The user model is used to correspond to the data table to facilitate object-oriented development. In the catalog

lib/extend/model/
Next, create a new model file
User.dart
, Type the following

class User { int id; String userId; String userMobile; String userPassword; String userNickname; String userAvatar; String userDescription; int createTime; int updateTime; int deleteTime; } Copy code

Only the class name and related attributes are defined here, and some methods need to be added. The method of supplementing model classes is a boring thing, and it is recommended to use tools.

If you are using

VSCode
And installed
Dart Data Class Generator
Plug-in, click on the class name at this time, the help will appear, click in the red box in the figure below to complete the code.

We will get the following result

Import 'DART: Convert' ; class User { int id; String userId; String userMobile; String userPassword; String userNickname; String userAvatar; String userDescription; int createTime; int updateTime; int deleteTime; User({ this .id, this .userId, this .userMobile, this .userPassword, this .userNickname, this .userAvatar, this .userDescription, this .createTime, this .updateTime, this .deleteTime, }); Map < String , dynamic > toMap() { return { 'id' : id, 'userId' : userId, 'userMobile' : userMobile, 'userPassword' : userPassword, 'userNickname' : userNickname, 'userAvatar' : userAvatar, 'userDescription ' : userDescription, 'createTime' : createTime, 'updateTime' : updateTime, 'deleteTime' : deleteTime, }; } factory User.fromMap( Map < String , dynamic > map) { return User( id: map[ 'id' ], userId: map[ 'userId' ], userMobile: map[ 'userMobile' ], userPassword: map[ 'userPassword' ], userNickname: map[ 'userNickname' ], userAvatar: map[ 'userAvatar' ], userDescription: map[ 'userDescription' ], createTime: map[ 'createTime' ], updateTime: map[ 'updateTime' ], deleteTime: map[ 'deleteTime' ], ); } String toJson() => json.encode(toMap()); factory User.fromJson( String source) => User.fromMap(json.decode(source)); @override String toString () { return 'the User (ID: $ ID , the userId: $ the userId , userMobile: $ userMobile , the userPassword: $ the userPassword , userNickname: $ userNickname , userAvatar: $ userAvatar , userDescription: $ userDescription , createTime: $ createTime , updateTime: $updateTime , deleteTime: $deleteTime )' ; } } Copy code

After the operation just now, you can see

3.more instantiation functions

User
,
User.fromMap
,
User.fromJson

3.more methods

toMap
,
toJson
,

Why do this, in the final analysis, is because

Dart
Disable reflection. When we get data from other places, it cannot be directly converted into model objects. Can only be converted to
map
,or
json
Strings, and then manually converted into model objects.

It's a little more complicated, but for better performance, it's not a big problem.

Create service

The service is used to process the actual business and is called by the controller.

In the catalog

lib/extend/service/
Next, create a new service file
UserService.dart
, Type the following

Import 'Package: Community/on Bootstrap/DB/Db.dart' ; Import 'Package: Community/on Bootstrap/DB/DbColumn.dart' ; Import 'Package: Community/on Bootstrap/Helper/ConvertHelper.dart' ; Import 'Package: Community/extend/helper/PasswordHelper.dart ' ; Import ' Package: Community/Extend/Helper/TimeHelper.dart ' ; Import ' Package: Community/Extend/Helper/UniqueHelper.dart ' ; Import ' Package: Community/Extend/Model/Page.dart ' ; Import ' Package: Community/Extend/Model/User.dart ' ; class UserService { static String _table = "user" ; ///Paging query static Future<Page<User>> query( List <DbColumn> condition, int pageNum, int pageSize) async { int totalCount = await Db(_table).where(condition).count( '*' ); List < Map < String , dynamic >> mapList = await Db(_table) .where(condition) .page(pageNum, pageSize) .order( "create_time desc" ) .select(); List <User> list = mapList.map((e) => User.fromMap(ConvertHelper.keyToHump(e))).toList(); return Page<User>(totalCount, pageNum, pageSize, list); } ///Query static Future<User> findById( String userId) according to user ID async { List <DbColumn> where = [ DbColumn.fieldToUnderLine( "userId" , "=" , userId), DbColumn.fieldToUnderLine( "deleteTime" , "=" , 0 ), ]; Map < String , dynamic > map = await Db(_table).where(where).find(); if ( null == map) throw "User not found" ; return User.fromMap(ConvertHelper.keyToHump(map)); } ///Add user static Future<User> add( String userMobile, String userPassword, String userNickname, String userAvatar, String userDescription, ) async { Map < String , dynamic > userMap = await _findByMobile(userMobile); IF ( null ! = s userMap) the throw 'the phone number already exists' ; User user = User( userId: UniqueHelper.userId(), userMobile: userMobile, userPassword: PasswordHelper.password(userPassword), createTime: TimeHelper.timestamp(), userNickname: userNickname, userAvatar: userAvatar, userDescription: userDescription, updateTime: 0 , deleteTime: 0 ); user.id = await Db(_table).insert(ConvertHelper.keyToUnderLine(user.toMap())); return user; } ///Modify user nickname static Future<User> updateNickname( String userId, String userNickname) async { User user = await findById(userId); user.userNickname = userNickname; await _updateField(user.toMap(), 'userId' , [ 'userNickname' ]); return user; } ///Delete according to user ID, soft delete static Future<User> delete( String userId) async { User user = await findById(userId); user.deleteTime = TimeHelper.timestamp(); await _updateField(user.toMap(), 'userId' , [ 'deleteTime' ]); return user; } ///Query static Future< Map < String , dynamic >> _findByMobile( String userMobile) async { List <DbColumn> condition = [ DbColumn.fieldToUnderLine( "userMobile" , "=" , userMobile), DbColumn.fieldToUnderLine( "deleteTime" , "=" , 0 ), ]; Map < String , dynamic > map = await Db(_table).where(condition).find(); return map; } ///Update table field static Future< int > _updateField( Map < String , dynamic > map, String keyName, List < String > fieldList) async { List <DbColumn> condition = [ DbColumn.fieldToUnderLine(keyName, '=' , map[keyName]) ]; Map < String , dynamic > updateMap = {}; fieldList.forEach((fieldName) { updateMap[fieldName] = map[fieldName]; }); return await Db(_table) .where(condition) .update(ConvertHelper.keyToUnderLine(updateMap)); } } Copy code

The above code is the addition, modification, and deletion of data. It is similar to the code in other languages. Some places that are easy to confuse, please explain a little bit.

In pagination query

List <User> list = mapList.map((e) => User.fromMap(ConvertHelper.keyToHump(e))).toList(); Copy code

The main role here is to

mapList
This list of key-value pairs is converted to
User
List of objects.

In addition, because the field names of our database are in underscore format, and the attributes of the model class are in camel case, a conversion process is required.

ConvertHelper.keyToHump
The effect is to name the key
Underline format
Key-value pairs, converted into key names
Camel case
The key-value pair.

Create controller

The controller is used to receive user request parameters, call the service to process the business, and finally return information

In the catalog

lib/app/controller/
Next, create a new model file
UserController.dart
, Type the following

Import 'Package: Community/on Bootstrap/Context.dart' ; Import 'Package: Community/on Bootstrap/DB/DbColumn.dart' ; Import 'Package: Community/on Bootstrap/DB/DbTrans.dart' ; Import 'Package: Community/on Bootstrap/helper/VerifyHelper.dart ' ; Import ' Package: Community/on Bootstrap/Meta/RouteMeta.dart ' ; Import ' Package: Community/Extend/Model/Page.dart ' ; Import ' Package: Community/Extend/Model/the User. DART ' ; Import ' Package: Community/Extend/-Service/UserService.dart ' ; class UserController { @RouteMeta ( '/home/user/query' , 'GET|POST' ) static void query(Context ctx) async { int pageNum = ctx.getPositiveInt( 'pageNum' , def: 1 ); int pageSize = ctx .getPositiveInt( 'pageSize' , def: 20 ); await DbTrans.simple(ctx, () async { List <DbColumn> condition = []; Page<User> res = await UserService.query(condition, pageNum, pageSize); ctx.showSuccess( 'obtained' , res.toMap()); }); } @RouteMeta ( '/home/user/findById' , 'GET|POST' ) static void findById(Context ctx) async { String userId = ctx.getString( 'userId' ); if (VerifyHelper.empty(userId)) return ctx .showError( 'User ID cannot be empty' ); await DbTrans.simple(ctx, () async { User res = await UserService.findById(userId); ctx.showSuccess( 'obtained' , res.toMap()); }); } @RouteMeta ( '/home/user/add' , 'GET|POST' ) static void add(Context ctx) async { String userMobile = ctx.getString( 'userMobile' ); String userPassword = ctx.getString( 'userPassword' ) ; String userNickname = ctx.getString( 'userNickname' ); String userAvatar = ctx.getString( 'userAvatar' ); String userDescription = ctx.getString( 'userDescription' ); if (VerifyHelper.empty(userMobile)) return ctx.showError( 'User mobile phone number cannot be empty' ); if (VerifyHelper.empty(userPassword)) return ctx.showError( 'User password cannot be empty' ); if (VerifyHelper .empty(userNickname)) return ctx.showError( 'User nickname cannot be empty' ); if (VerifyHelper.empty(userAvatar)) return ctx.showError( 'User avatar cannot be empty' ); if (VerifyHelper.empty(userDescription )) return ctx.showError( 'User description cannot be empty' ); await DbTrans.simple(ctx, () async { User res = await UserService.add( userMobile, userPassword, userNickname, userAvatar, userDescription); ctx.showSuccess( 'Added' , res.toMap()); }); } @RouteMeta ( '/home/user/updateNickname' , 'GET|POST' ) static void updateNickname(Context ctx) async { String userId = ctx.getString( 'userId' ); String userNickname = ctx.getString( 'userNickname' ) ; if (VerifyHelper.empty(userId)) return ctx.showError( 'User ID cannot be empty' ); if (VerifyHelper.empty(userNickname)) return ctx.showError( 'User nickname cannot be empty' ); await DbTrans.simple(ctx, () async { User res = await UserService.updateNickname(userId, userNickname); ctx.showSuccess( 'Changed' , res.toMap()); }); } @RouteMeta ( '/home/user/delete' , 'GET|POST' ) static void delete(Context ctx) async { String userId = ctx.getString( 'userId' ); if (VerifyHelper.empty(userId)) return ctx .showError( 'User ID cannot be empty' ); await DbTrans.simple(ctx, () async { User res = await UserService.delete(userId); ctx.showSuccess( 'deleted' , res.toMap()); }); } } Copy code

It is necessary to explain:

RouteMeta
Yes
DartMars
Defined routing metadata, similar to
java
Notes in the.

The same effect is that the code can be described, so that the developer knows the function of the code described.

The difference is because

DartMars
There is no reflection, so the program cannot obtain metadata or annotation information when it is running, and it cannot be similar to
java
The function of generating code is annotated here.

Of course, since the code cannot be generated when it is running, we can find another diagram and generate it before compiling.

Automatically update routing configuration

Next, we start the project and execute the following commands:

dart pub global run dart_mars --serve dev copy the code

Please note that there is a sentence printed on the console

route config file has been updated, see ./lib/config/route.dart copy the code

Said that the routing configuration file has been updated and the address is

./lib/config/route.dart
, Let's take a look

import '../bootstrap/helper/RouteHelper.dart' ; import '../app/controller/HomeController.dart' as app_controller_HomeController; import '../app/controller/UserController.dart' as app_controller_UserController; /// ///don't modify this file yourself, this file content will be replace by DartMars /// ///for more infomation , see doc about Route/// ///last replace time 2021-07-03 14:53:51.588722 /// void configRoute(){ RouteHelper.add( 'GET' , '/' , app_controller_HomeController.HomeController.index); RouteHelper.add( 'GET' , '/user' , app_controller_HomeController.HomeController.user); RouteHelper.add( 'GET' , '/city/:cityName' , app_controller_HomeController.HomeController.city); RouteHelper.add( 'GET|POST' , '/home/user/query' , app_controller_UserController.UserController.query); RouteHelper.add( 'GET|POST' , '/home/user/findById' , app_controller_UserController.UserController.findById); RouteHelper.add( 'GET|POST' , '/home/user/add' , app_controller_UserController.UserController.add); RouteHelper.add( 'GET|POST' , '/home/user/updateNickname' , app_controller_UserController.UserController.updateNickname); RouteHelper.add( 'GET|POST' , '/home/user/delete' , app_controller_UserController.UserController.delete); } Copy code

Sure enough, added at the end

5
Routing rules, and the
UserController
The same as defined in.

In addition, as the file prompts, do not manually change this file, when you run

--serve
When ordering,
DartMars
Will be updated automatically.

Test interface

The work of the test interface is very simple. You can use professional tools or directly in the browser. The article is limited, so I will test

2
1. other interfaces, interested students come by themselves.

Test add user interface

http://127.0.0.1/home/user/add?userMobile=18512345679&userPassword=123456&userNickname=tang&userAvatar=http://www.test.com/1.jpg&userDescription=test copy the code

Returns as follows

{ "code" : 200 , "msg" : "Added" , "data" : { "id" : 2 , "userId" : "1625295731292004882" , "userMobile" : "18512345679" , "userPassword" : "4616221982a9d1759d1d0cec7249a6d71da960d3" , "userNickname" : "tang" , "userAvatar" : "http://www.test.com/1.jpg" , "userDescription" : "test" , "createTime" :1625295731, "updateTime" : 0 , "deleteTime" : 0 } } Copy code

Everything is normal and great.

Test and query a single user interface

http://127.0.0.1/home/user/findById?userId=1625295731292004882Copy code

Returns as follows

{ "Code" : 200 is , "MSG" : "acquired" , "Data" : { "ID" : 2 , "the userId" : "1,625,295,731,292,004,882" , "userMobile" : "18,512,345,679" , "the userPassword" : "4616221982a9d1759d1d0cec7249a6d71da960d3" , "userNickname" : "tang" , "userAvatar" : "http://www.test.com/1.jpg" , "userDescription" : "test" , "createTime" :1625295731, "updateTime" : 0 , "deleteTime" : 0 } } Copy code

Everything is normal and great.

summary

The classmates who can see here must be true love.

From the above process, it can be seen that using

Dart
There is not much difference between the development of back-end applications and the development of other languages. Also explain one thing, developers of other languages want to switch to
Dart
It is easy to develop back-end applications.

Plus

Dart
Successful in the field of client development, one language to complete the client and server is definitely no longer a dream.

That's All, Enjoy.