- 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伺服器開發,請關注我,學習更多騷操作!