Preface
In the past few days, I have published several articles about Dart
developing back-end applications, mainly introducing Dart
, such as asynchronous tasks, concurrent processing, compilation and deployment, and so on.
As the saying goes, if you just Dart
fakes, let’s start a 060e80910864a8 back-end application today.
What application do we want to develop
Suppose we now want to develop a community application, similar to Nuggets,
CSDN
etc. The basic function is for users to post articles and post 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 we plan to use Dart
development, a development framework is still very helpful. However, Dart
are not many back-end frameworks for aqueduct
, jaguar
, DartMars
, 060e8091086536, etc., here, we use DartMars
.
The source code is here https://github.com/tangpanqing/dart_mars
The document is here https://tangpanqing.github.io/dart_mars_docs/zh/
Open the document homepage, like this
vuepress
, the strong flavor of 060e809108659e.
Starting a project is so easy
According to the guidelines of DartMars
Dart
, we can execute the following command to create the project
# 安装DartMars
dart pub global activate --source git https://github.com/tangpanqing/dart_mars.git
# 创建项目
dart pub global run dart_mars --create project_name
# 进入目录
cd project_name
# 获取依赖
dart pub global run dart_mars --get
# 启动项目
dart pub global run dart_mars --serve dev
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
Thanks for 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"
This means that the installation is complete.
The second step is to create the project
The tentative name of the project is community
community, and the following orders are executed
dart pub global run dart_mars --create community
After the above command, DartMars
a prompt
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
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 community
The fourth step is to obtain dependencies
Excuting an order
dart pub global run dart_mars --get
After the above command, DartMars
a prompt
Got dependencies!
Indicates that the loading dependency is complete
The fifth step, start the project
dart pub global run dart_mars --serve dev
After the above command, DartMars
a prompt
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
The startup is successful. From the above information, we can know:
- The routing configuration file has been updated,
- HTTP service has started, on port 80, currently using the development environment
Open the browser, visit http://127.0.0.1:80 we can see the classic
hello world
Continue coding step by step
Take a look at the project structure first
bin
directory is the entrance to the executable file
lib
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.
First complete the first one, add, check, modify, delete, and make a standard for later use.
Create user table
I have prepared the relevant sql
statement in advance
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(40) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户ID',
`user_mobile` varchar(11) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户手机号',
`user_password` varchar(60) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
`user_nickname` varchar(60) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户昵称',
`user_avatar` varchar(60) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户头像',
`user_description` varchar(120) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户介绍',
`create_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '更新时间',
`delete_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '删除时间',
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`),
KEY `user_mobile` (`user_mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
mysql
to 060e8091086871 to execute
Create user model
The user model is used to correspond to the data table to facilitate object-oriented development.
In the directory lib/extend/model/
, create a new model file User.dart
, and 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;
}
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 the Dart Data Class Generator
plug-in, click the class name at this time, and the help will appear. Click inside 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 'User(id: $id, userId: $userId, userMobile: $userMobile, userPassword: $userPassword, userNickname: $userNickname, userAvatar: $userAvatar, userDescription: $userDescription, createTime: $createTime, updateTime: $updateTime, deleteTime: $deleteTime)';
}
}
After the operation just now, you can see
Three more instantiated functions User
, User.fromMap
, User.fromJson
Three more methods toMap
, toJson
, toString
Why do this, in the final analysis, is because Dart
disables reflection. When we get data from other places, it cannot be directly converted into model objects. It can only be converted into a map
or json
string first, and then manually converted into a model object.
It's a little more complicated. 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 directory lib/extend/service/
, create a new service file UserService.dart
, type the following
import 'package:community/bootstrap/db/Db.dart';
import 'package:community/bootstrap/db/DbColumn.dart';
import 'package:community/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";
/// 分页查询
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);
}
/// 根据用户ID查询
static Future<User> findById(String userId) 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 "没有找到用户";
return User.fromMap(ConvertHelper.keyToHump(map));
}
/// 添加用户
static Future<User> add(
String userMobile,
String userPassword,
String userNickname,
String userAvatar,
String userDescription,
) async {
Map<String, dynamic> userMap = await _findByMobile(userMobile);
if (null != userMap) throw '该手机号已存在';
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;
}
/// 修改用户昵称
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;
}
/// 根据用户ID删除,软删除
static Future<User> delete(String userId) async {
User user = await findById(userId);
user.deleteTime = TimeHelper.timestamp();
await _updateField(user.toMap(), 'userId', ['deleteTime']);
return user;
}
/// 根据用户手机号查询
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;
}
/// 更新表字段
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));
}
}
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();
The main role here is to mapList
this list of key-value pairs, converted into 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
is to convert the key- underlined format into the key-value pair with the key name
camel case format.
Create controller
The controller is used to receive user request parameters, call the service to process the business, and finally return information
In the directory lib/app/controller/
, create a new model file UserController.dart
, and type the following
import 'package:community/bootstrap/Context.dart';
import 'package:community/bootstrap/db/DbColumn.dart';
import 'package:community/bootstrap/db/DbTrans.dart';
import 'package:community/bootstrap/helper/VerifyHelper.dart';
import 'package:community/bootstrap/meta/RouteMeta.dart';
import 'package:community/extend/model/Page.dart';
import 'package:community/extend/model/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('已获取', 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('用户ID不能为空');
await DbTrans.simple(ctx, () async {
User res = await UserService.findById(userId);
ctx.showSuccess('已获取', 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('用户手机号不能为空');
if (VerifyHelper.empty(userPassword)) return ctx.showError('用户密码不能为空');
if (VerifyHelper.empty(userNickname)) return ctx.showError('用户昵称不能为空');
if (VerifyHelper.empty(userAvatar)) return ctx.showError('用户头像不能为空');
if (VerifyHelper.empty(userDescription)) return ctx.showError('用户描述不能为空');
await DbTrans.simple(ctx, () async {
User res = await UserService.add(
userMobile, userPassword, userNickname, userAvatar, userDescription);
ctx.showSuccess('已添加', 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('用户ID不能为空');
if (VerifyHelper.empty(userNickname)) return ctx.showError('用户昵称不能为空');
await DbTrans.simple(ctx, () async {
User res = await UserService.updateNickname(userId, userNickname);
ctx.showSuccess('已更改', 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('用户ID不能为空');
await DbTrans.simple(ctx, () async {
User res = await UserService.delete(userId);
ctx.showSuccess('已删除', res.toMap());
});
}
}
It is necessary to explain:
RouteMeta
is the routing metadata defined by DartMars
, similar to the annotations in java
The same effect is that the code can be described, so that the developer knows the function of the code described.
The difference is that because DartMars
does not have reflection, the program cannot obtain metadata or annotated information when it is running, and it cannot complete the function similar to the annotation generated code in java
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
Please note that there is a sentence printed on the console
route config file has been updated, see ./lib/config/route.dart
Said that the routing configuration file has been updated, 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);
}
Sure enough, the 5
routing rule was UserController
, which is the same as the one we just defined in 060e8091086df0.
In addition, as the file prompts, do not manually change this file. When you run the --serve
command, 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 length of the article is limited, so I will test 2
, 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
Returns as follows
{
"code": 200,
"msg": "已添加",
"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
}
}
Everything is normal and great.
Test and query a single user interface
http://127.0.0.1/home/user/findById?userId=1625295731292004882
Returns as follows
{
"code": 200,
"msg": "已获取",
"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
}
}
Everything is normal and great.
to sum up
The classmates who can see here must be true love.
From the above process, it can be seen that the Dart
is not much different from other languages. It also shows that it is very easy for developers of other languages to switch to Dart
Coupled with the Dart
in the field of client development, it is definitely no longer a dream to complete the client and server in one language.
That's All, Enjoy.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。