DartVM伺服器開發(第6⃣️天)--利用註解處理請求

來源:互聯網
上載者:User
  • DartVM伺服器開發(第️天)--Hello World!
  • DartVM伺服器開發(第️天)--處理請求
  • DartVM伺服器開發(第️天)--pub管理器、返回html頁面
  • DartVM伺服器開發(第️天)--代碼最佳化
  • DartVM伺服器開發(第️天)--日誌工具

在之前的文章中,我們學習了如何建立一個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開發

下面,如果你跟著仔細操作,你將學會如何使用註解,使用反射擷取註解、通過反射調用方法。


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
見證奇蹟!

成功.png
可以看到,我們成功的利用註解處理請求!
今天的內容基本上是這些了,如果你仔細學習了該文章,對於Flutter開發也可以使用註解去登陸,去請求資料,好了,謝謝!我們明天見!

如果想繼續學習DartVM伺服器開發,請關注我,學習更多騷操作!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.