20

DartVM服务器开发(第6⃣️天)--利用注解处理请求

 6 years ago
source link: https://studygolang.com/articles/14450?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在之前的文章中,我们学习了如何建立一个DartVM服务器,在我对Flutter群分享时,有些群友会疑问,学习这个还不如学习golang,Dart服务器有什么用....等等,我这里先说明一下,就目前来说确实没什么用,dart服务器运行的是语言VM,而像java服务器运行的是jvm,我们简单来讲一下什么是语言VM跟JVM,语言VM是专门针对某种语言去开发,而JVM是一个字节码VM,这个字节码VM是规定了一个规则,只要是遵守它的规则,无论你是什么语言都可以开发,但前提是根据这个规则转换为字节码,所以说:JVM相对性能没有语言VM要好,但适用范围广泛,语言VM因为根据该语言设计的,所以,是可以通过该语言完全的操控VM,可以两个类似的相比较, JVM跟语言VM就好比万能驱动与原配驱动 ,当然这是类似的比较,不用太较真!好了,巴拉巴拉,说了一大堆,总结: 目前Flutter基于dart语言,学习DartVM开发有助于打好Dart基础,基础打好了,开发Flutter的骚操作也就更多!同样也适合走Dart web开发

下面,如果你跟着仔细操作,你将学会如何使用注解,使用反射获取注解、通过反射调用方法。

yAnuMnu.jpg!web

image

1. 定义注解(dart叫元数据)

在java中,如果自定义一个注解,需要添加 @Target 作用域注解, @Retention 注解类型注解,添加 @interface ,然后定义注解参数,那么现在告诉你,在dart都不用,我们只需要定义实体类一样就可以了,代码如下

class Controller{
  final String path;
//构造方法定义为编译时常量
  const Controller({this.path});
  @override
  String toString() =>'Controller';//这里是区别其它注解
}

要注意的是构造方法需要添加一个const修饰,定义为编译时常量,然后下面就是使用

@Controller(path: '/user')
class UserController {
  
}

然后我们以Controller标识过的作为该Controller的第一路径,如果没有就忽略掉,

接下来我们再定义一个@Request注解

class Request{
  final String path;
  final String method;

  const Request({this.path,this.method});

  @override
  String toString() =>'Request';
}

上面定义了一个第二路径,跟一个请求方法,然后我们根据这个Request再弄Get跟Post的注解

class Get extends Request{
  final String path;

  const Get({this.path}) : super(path : path,method: 'GET');

  @override
  String toString() =>'Get';
}

class Post extends Request{
  final String path;

  const Post({this.path}) : super(path : path, method: 'POST');

  @override
  String toString() =>'Get';

}

可以看到Get跟Post注解都继承了Request,传递了特定的请求方法

现在我们都准备好了这些注解,我们现在用这些注解写一下请求

@Controller(path: '/user')
class UserController{
  @Get(path: '/login')
  void login(HttpRequest request) {
    request.response
      ..statusCode = HttpStatus.ok
      ..writeln('LoginSuccess')
      ..close();
  }

  @Post(path: '/logout')
  void logout(HttpRequest request){
    request.response
      ..statusCode = HttpStatus.ok
      ..writeln('logoutSuccess')
      ..close();
  }

  @Request(path: '/delete', method: 'DELETE')
  void editUser(HttpRequest request){
    request.response
      ..statusCode = HttpStatus.ok
      ..writeln('DeleteSuccess')
      ..close();
  }
}

好了请求写玩了,那么,我们怎么去关联这些注解呢,下面就要用到反射了!

2.使用反射解析注解类

dart里面含有一个镜子包,这个包可以通过传入的类,去解析元数据(即注解),并可以通过镜子传递参数去调用方法,为了统一管理这些Controller,我们定义一个BaseController,让处理请求的Controller都继承这个类

//抽象类
abstract class BaseController{
}

上面这个方法是一个空方法,我们不添加任何东西,然后让UserController继承这个类

@Controller(path: '/user')
class UserController extends BaseController{

  @Get(path: '/login')
  void login(HttpRequest request) {
    request.response
      ..statusCode = HttpStatus.ok
      ..writeln('LoginSuccess')
      ..close();
  }

  @Post(path: '/logout')
  void logout(HttpRequest request){
    request.response
      ..statusCode = HttpStatus.ok
      ..writeln('logoutSuccess')
      ..close();
  }

  @Request(path: '/delete', method: 'DELETE')
  void editUser(HttpRequest request){
    request.response
      ..statusCode = HttpStatus.ok
      ..writeln('DeleteSuccess')
      ..close();
  }
}

下面,我们导入镜子包,新建一个ControllerManager,用来管理这Controller

import 'dart:mirrors';
import 'dart:io';
class ControllerManager{
  static ControllerManager manager=new ControllerManager();

//该list用于判断Controller是否已经被添加
  List<BaseController> controllers=[];
//这是一个map,对应的是请求链接,跟对应的controller信息
  Map<String,ControllerInfo> urlToMirror=new Map();

  //添加控制器
  void addController(BaseController controller){
//判断当前是否已经添加过控制器
    if(!controllers.contains(controller)){
      controllers.add(controller);
//添加map
      urlToMirror.addAll(getRequestInfo(controller));
    }
  }

//该controllerManager处理请求的方法
  void requestServer(HttpRequest request){
    //当前请求的路径
    String path=request.uri.toString();
    //当前请求的方法
    String method=request.method;
   
  //判断map中是否包含该请求地址
    if(urlToMirror.containsKey(path)){
      ControllerInfo info=urlToMirror[path];
//获取到该请求,传递路径、请求方法跟请求
      info.invoke(path, method, request);
    }else{
//没有该地址返回一个404
     request.response
       ..statusCode=HttpStatus.notFound
       ..write('''{
    "code": 404,
    "msg": "链接不存在!"
}''')
     ..close();
    }
  }
}

上面的思路是,在初始化时,将所有的Controller都添加到map中以请求路径为key去查找,当请求时,请求地址在map中查找到,就为它处理请求,如果查找不到,就给它丢一个404的信息,下面是ControllerInfo

class ControllerInfo{
//请求地址对应Controller中的方法,Symbol包含方法标识
  final Map<String,Symbol> urlToMethod;
//该参数包含通过类初始化得到的实例镜子,可以通过该参数调用方法
  final InstanceMirror instanceMirror;
  
//构造方法
  ControllerInfo(this.instanceMirror,this.urlToMethod);

  //调用请求方法
  void invoke(String url,String method,HttpRequest request){
  //判断是否该请求地址是对应的请求方法
    if(urlToMethod.containsKey('$url#$method')){
//调用方法
      instanceMirror.invoke(urlToMethod['$url#$method'], [request]);
    }else {
//请求方法不对,返回一个错误
      request.response
        ..statusCode=HttpStatus.methodNotAllowed
        ..write('''{
    "code": 405,
    "msg": "请求出错!"
}''')
        ..close();
    }
  }
}

主要的是Symbol、InstanceMirror这两个类,通过instanceMirror.invoke(Symbol,[传递的参数]),就能调用对应的方法了

我们在ControllerManager中还有一个添加Controller到map的方法 getRequestInfo(controller) 没有介绍,这个就是获取InstanceMirror跟Symbol的关键,下面介绍获取InstanceMirror跟Symbol

//传递一个Controller进去
Map<String,ControllerInfo> getRequestInfo(BaseController controller) {
// 实际返回的Map
  Map<String,ControllerInfo> info=new Map();
//请求地址对应的方法
  Map<String,Symbol> urlToMethod=new Map();
// 获取Controller实例的镜子
  InstanceMirror im = reflect(controller);
//获取Controller运行时类型的镜子
  ClassMirror classMirror = im.type;
//请求的根路径
  List<String> path = [];
//该Controller的所有接收的请求地址
  List<String> urlList=[];

//获取元数据,就是获取@Controller(path: xxx)中的xxx
  classMirror.metadata.forEach((medate) {
    path.add(medate.reflectee.path);
  });

  //获取该类的所有方法
  classMirror.declarations.forEach((symbol, declarationMirror) {
    //将自身的构造方法剔除
    if (symbol.toString() != classMirror.simpleName.toString()) {
    //获取方法的元数据,就是@Get(path: path)
      declarationMirror.metadata.forEach((medate) {
        //请求的地址
        String requestPath = path.join() + medate.reflectee.path;
        //请求的类型
        String method = medate.reflectee.method;

//        print('请求地址为:$requestPath,请求方法为:$method');
    //添加到请求地址集合
        urlList.add(requestPath);
//添加到请求地址对应方法的集合
        urlToMethod.putIfAbsent('$requestPath#$method', ()=>symbol);
      }
      );
    }
  });

//实例化一个Controller信息
  ControllerInfo controllerInfo=new ControllerInfo(im, urlToMethod);

//循环添加到实际需要的Map,对应请求地址根ControllerInfo信息
  urlList.forEach((url){
    info.putIfAbsent(url, ()=>controllerInfo);
  });
//返回需要的map
  return info;
}

上面的注释已经写的很明细了,如果不清楚,我再讲解下:

  • reflect(controller) >>>> InstanceMirror(含有Controller的实例,可调用方法)
  • InstanceMirror.type >>>> ClassMirror(为了获取类的注解)
  • classMirror. metadata >>>>> 获取类的元数据
  • classMirror.declarations >>>> (symbol, declarationMirror) 获取该类的所有方法名(用于调用方法),镜子(用于获取元数据)
  • declarationMirror.metadata >>>> 获取方法的元数据
    好了,基本上讲完,然后我们去使用该ControllerManager

3. 使用ControllerManager

首先我们需要在运行服务器之前,将我们需要的Controller添加到ControllerManager中(这个比较笨的方法,如果有大佬知道怎么自动去扫描Controller添加到ControllerManager,请告知小弟,谢谢!)

//添加控制器
  ControllerManager.manager.addController(new UserController());

然后将我们之前的 handleMessage(request)方法替换为

//....
          ControllerManager.manager.requestServer(request);
//....

当所有操作完成之后,我们休息一会,然后点击绿色按钮,启动我们的服务器,并输入 http://localhost:8080/user/login

见证奇迹!

V3mEVze.png!web

成功.png

可以看到,我们成功的利用注解处理请求!

今天的内容基本上是这些了,如果你仔细学习了该文章,对于Flutter开发也可以使用注解去登陆,去请求数据,好了,谢谢!我们明天见!

如果想继续学习DartVM服务器开发,请关注我,学习更多骚操作!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK