声明:本文首发于微信订阅号:Dart客栈,微信后台回复05166获取本篇源码
文章为原创,如需转载请注明出处,并告知作者,谢谢!
Dart-Aqueduct框架开发目录
- Dart-Aqueduct框架开发(一)
- Dart-Aqueduct框架开发(二)
- Dart-Aqueduct框架开发(三)
- Dart-Aqueduct框架开发(四)
- Dart-Aqueduct框架开发(五)
- Dart-Aqueduct框架开发(六)
- Dart-Aqueduct框架开发(七)
- Dart-Aqueduct框架开发(八)
- 未完待续…
1.介绍
这一节我们来学习一下资源控制器ResourceController
2. 什么是资源控制器?
可以从名字看出,这个控制器是用来管理资源的,也就是上一节中介绍了控制器B,也可以称为端点的控制器,可以用以下图来描述:
从上图可以得到的信息为,控制器可以处理多个请求路径,并可返回不同的内容,那么为什么它可以处理这么多个请求呢?如果多次请求让Controller处理,那么会不会出现该控制器会不断的实例化,导致性能出现问题呢?
3. Recyclable
我们在查看源码的时候,可以看到ResourceController
继承Controller
并实现了Recyclable
,这个Recyclable
就是能让Controller
实现在处理完请求后,进行回收,等待下次请求时不用重新实例化,它主要实现了recycledState
和restore
从源码中可以看出,它保存了类名为BoundController
的一个实体,那么这个实体哪个地方耗时,导致需要循环再用呢?我们进一步看源码
可以看到,它的构造方法一开始就调用了反射,大家应该都知道,反射是比较耗时的,如果每次请求一下都要反射一次的话,那这个性能太低了,所以需要循环使用,而归功于上面的反射,可以使用元数据(注解)进行对请求的处理
4.请求方法
目前Aqueduct
框架支持下面的注解请求方法,并且支持0-4
的请求路径
Operation('请求方法')
Operation.get
Operation.put
Operation.post
Operation.delete
我们来把之前的获取文章接口使用ResourceController
实现看看,新建文件article_controller
,然后添加以下代码
class ArticleController extends ResourceController {
ArticleController(this.context);
final ManagedContext context;
@Operation.get() //获取文章列表
FutureOr<Response> getArticle() async {
//查询文章,并根据createDate进行排序
final query = Query<Article>(context)
..sortBy((e) => e.createData, QuerySortOrder.ascending);
final List<Article> articles = await query.fetch();
return Result.data(articles);
}
}
然后把ArticleController链接到Router里面
@override
Controller get entryPoint {
//定义路由、请求链接等,在启动期间调用
return Router(notFoundHandler: (request) async {
//当出现请求不到接口的时候,返回下面内容给客户端
await request.respond(Response.notFound()
..contentType = ContentType.json
..body = {
'code': 404,
'msg': 'not found',
"data": null,
});
//打印日志
logger.warning("${request.toDebugString()}");
})
//new
..route('/article/[:id([0-9]+)]').link(
() => ArticleController(context),
);
//new
上面代码就完成了请求文章列表的接口,让我们来请求一下http://localhost:8080/article
可以看到,我们成功的拿到了数据,下面,我们继续添加文章的增删改的接口
class ArticleController extends ResourceController {
ArticleController(this.context);
final ManagedContext context;
@Operation.get() //获取文章列表
FutureOr<Response> getArticle() async {
//查询文章,并根据createDate进行排序
final query = Query<Article>(context)
..sortBy((e) => e.createDate, QuerySortOrder.ascending);
final List<Article> articles = await query.fetch();
return Result.data(articles);
}
@Operation.post()//添加一篇文章
FutureOr<Response> insertArticle(
@Bind.body(ignore: ["createData"]) Article article) async {
article.createDate = DateTime.now();
//插入一条数据
final result = await context.insertObject<Article>(article);
return Result.data(result);
}
@Operation.get('id')//查询单个文章
Future<Response> getArticleById(@Bind.path('id') int id) async {
//根据id查询一条数据
final query = Query<Article>(context)..where((a) => a.id).equalTo(id);
final article = await query.fetchOne();
if (article != null) {
return Result.data(article);
} else {
return Result.successMsg();
}
}
@Operation.put()//修改一篇文章
Future<Response> updateArticleById(
@Bind.body(ignore: ["createData"]) Article article) async {
final query = Query<Article>(context)
..values.content = article.content
..where((a) => a.id).equalTo(article.id);
//更新一条数据
final result = await query.updateOne();
// final article = await query.fetchOne();
if (result != null) {
return Result.data(result);
} else {
return Result.errorMsg("更新失败,数据不存在");
}
}
@Operation.delete('id')//删除一篇文章
Future<Response> deleteArticleById(@Bind.path('id') int id) async {
final query = Query<Article>(context)..where((a) => a.id).equalTo(id);
//删除一条数据
final result = await query.delete();
if (result != null && result == 1) {
return Result.successMsg("删除成功");
} else {
return Result.errorMsg("删除失败,数据不存在");
}
}
}
以上就是文章的增删查改,有兴趣的朋友可以CV看看效果,下面我们来学习一下在代码中出现的@Bind
5.请求绑定
目前Aqueduct
框架支持下面的注解请求绑定
Bind.path
绑定路径
具体使用:
@Operation.get('id')//查询单个文章
Future<Response> getArticleById(@Bind.path('id') int id) async {
//根据id查询一条数据
final query = Query<Article>(context)..where((a) => a.id).equalTo(id);
final article = await query.fetchOne();
if (article != null) {
return Result.data(article);
} else {
return Result.successMsg();
}
}
对应请求:http://localhost:8080/article/1
Bind.query
绑定Url查询参数
具体使用:
@Operation.get() //获取文章列表或一篇文章
FutureOr<Response> getArticle({@Bind.query('id') int id}) async {//使用中括号表示参数可选
if (id != null) {
//查询一篇文章
final query = Query<Article>(context)..where((a) => a.id).equalTo(id);
final article = await query.fetchOne();
if (article != null) {
return Result.data(article);
} else {
return Result.successMsg();
}
} else {
//查询文章,并根据createDate进行排序
final query = Query<Article>(context)
..sortBy((e) => e.createDate, QuerySortOrder.ascending);
final List<Article> articles = await query.fetch();
return Result.data(articles);
}
}
对应请求:http://localhost:8080/article?id=1
Bind.header
绑定请求头
具体使用:
class ArticleController extends ResourceController {
ArticleController(this.context);
final ManagedContext context;
@Bind.header("token")
String token;//@Bind注解可以在局部变量使用,根据传入的key获取对应的值
//...
}
Bind.body
绑定请求体(需要注意,获取的内容为json
形式传递的数据)
具体使用:
@Operation.post() //添加一篇文章
FutureOr<Response> insertArticle(
@Bind.body(ignore: ["createData"]) Article article) async {
//这里可以直接转为实体,但需要注意的是@Bind.body里的参数含义如下
//ignore表示忽略哪些字段
//reject表示拒绝接收哪些字段
//require表示哪些字段必须有
//啥都不填表示参数如果不传则为空
article.createDate = DateTime.now();
//插入一条数据
final result = await context.insertObject<Article>(article);
return Result.data(result);
}
6.ResourceController全局参数
当某个控制器需要获取和设置全局参数时,框架提供一下内容:
Request request
可获取请求信息Map<String, String> get pathVariables
可获取路径List<ContentType> acceptedContentTypes
可设置接收的内容类型ContentType responseContentType
可设置响应的内容类型
7.生命周期
很多时候,一个请求的到来,通知伴随者控制器的生命周期,下面是ResourceController
的生命周期
FutureOr<RequestOrResponse> willProcessRequest(Request req)
//在处理请求之前调用,如果返回Response类型,则终止后续处理
void willDecodeRequestBody(RequestBody body)
//在解码请求体之前调用
void didDecodeRequestBody(RequestBody body)
//在解码请求体之后调用,如果没有请求体,则不执行
FutureOr<RequestOrResponse> handle(Request request)
//该方法继承自Controller,无需处理
以上就是这一节的所有内容,如果小伙伴们觉得有收获,不妨点一下点个赞,让我能看到你跟我一起学习Dart服务器,也是对我写作的一种肯定🙏!