Dart-Aqueduct框架开发(六)

上一篇

声明:本文首发于微信订阅号:Dart客栈,微信后台回复05166获取本篇源码
文章为原创,如需转载请注明出处,并告知作者,谢谢!

1.介绍

这一节我们来学习一下资源控制器ResourceController

2. 什么是资源控制器?

可以从名字看出,这个控制器是用来管理资源的,也就是上一节中介绍了控制器B,也可以称为端点的控制器,可以用以下图来描述:



从上图可以得到的信息为,控制器可以处理多个请求路径,并可返回不同的内容,那么为什么它可以处理这么多个请求呢?如果多次请求让Controller处理,那么会不会出现该控制器会不断的实例化,导致性能出现问题呢?

3. Recyclable

我们在查看源码的时候,可以看到ResourceController继承Controller并实现了Recyclable,这个Recyclable就是能让Controller实现在处理完请求后,进行回收,等待下次请求时不用重新实例化,它主要实现了recycledStaterestore



从源码中可以看出,它保存了类名为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获取对应的值

//...
}
image.png
  • 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服务器,也是对我写作的一种肯定🙏!

下一篇

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274