<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                下面的例子比較復雜,但是更加令人滿意。假裝建立一個基本的Wiki應用:一個網站內容可以由用戶快速直接在瀏覽器中修改。wiki 這個單詞來源于 夏威夷Wiki - 這個表述,意味著快速。Wiki的功能希望是: * 每個頁面都可以通過點擊 編輯鏈接立即修改。編輯頁面是由一個簡單的表單構成,允許對內容的改變。 * 新的頁面可以被添加,通過導航到偽裝的位置,并點擊編輯按鈕。 * 頁面內容的語法是一個標準的HTML片段(僅有可以被包含到body的元素被允許)。 * 支持多層樹結構。每個頁面都可以有無限的子頁面。 * 當前的面包屑自動生成。在web開發中,一個面包屑表示當前頁面在一個復雜結構中的位置。當使用面包屑時,可以從當前頁面導航到父級容器。 * 每個頁面的標題來自它的URI 。 * 使用文本文件存儲創建的頁面。 像之前做過的,可以準備一個項目目錄 wiki 。在其中創建 web, src 和pages 目錄。pages 目錄將包含動態創建的Wiki內容。鑒于此,確保這個目錄被正確的允許Neko的讀寫權限。 整個使應用運行的代碼在 src 目錄。 入口類是 WikiApplication ,它定義在 WikiAppllication.hx 文件: ~~~ import WikiController; class WikiApplication { public static function main() { var uri = neko.Sys.getUri(); var repositoryPath = neko.Sys.getCwd() + "../pages"; var params = neko.Web.getParams(); var action = switch( params.get("action") ) { case "edit": Edit; case "save": Save( params.get("content") ); default: View; } var controller = new WikiController(uri, repositoryPath, action); controller.execute(); } } ~~~ 類包含了main方法,在每次web服務器調用 Wiki應用的時候都會被調用。要生成一個有效的響應,一些信息必須獲得;首先,當前請求頁面的URL必須知道,所以它可以加載匹配的內容。記住所有請求都會重定義到你的WikiApplication 。然后這個部分,它會解釋如何通知web服務器重定義所有的頁面調用到同樣的執行單元。neko.Web.getUri() 方法返回相對當前頁面的一個相對路徑,所以如果調用URI是: ~~~ http://localhost:2000/mydirectory/mypage?param=value ~~~ 返回的值將是: ~~~ /mydirectory/mypage ~~~ 如果地址是Web服務器的基礎地址 http://localhost:20000/ 返回值將是一個簡單的斜線 / 。 應用需要的其他信息是實際內容所在的目錄。現在路徑相對于當前的工作目錄,但是可以簡單的改變它符合你的需求,也許從一個配置文件加載這個值。 最終,應用必須了解與Web服務器所做的請求關聯的每一個參數。neko.Web.getParams() 返回一個map對象包括所有的 GET和POST變量。需要處理請求的參數是執行動作的一種(查看,編輯或者保存),要保存內容那么操作就是 save 。當一個請求被做出,而沒有action參數,或者帶有一個不可用的值,默認則使用 view 操作。 三個動作非常直觀:view顯示頁面內容,edit會展示一個表單來編輯頁面內容,而save則保存內容并顯示新修改的內容,帶有一個save 確認信息。 當使用一個請求時,總是假定發送的信息可以被惡意操作;傳輸輸入參數到一個 enum 是一個確定的方式來阻止錯誤和意外結果。 WikiController.hx,位于 src目錄下,包括WikiController類的定義,和RequestAction的枚舉。每個文本內容被分配到一個私有靜態變量。這是一個好的實踐,使得開發人員更加容易,當他需要隨后去改變一些值的時候,因為不需要他滾動整個代碼區尋找可能需要改變的位置。 在類的構造函數中,函數參數被存儲在實例變量供后面使用。本例中的URI,值被修改為特定的值 /root 當請求的URI是基本的根地址的時候。通過這個方式,你得到一個引用名稱 root 作為主頁,同樣可以用于任何動態創建的頁面。 你可能注意到,在WikiApplication類main方法中,WikiController是被實例化然后方法 execute()被調用。在這個方法中真正的操作發生。execute 方法決定哪個視圖被渲染并實例化相應的類。所有實際生成輸出的類,HTML代碼,都是抽象類 Page的子類,而且,因此,他們都共享相同的方法 render(),最終生成所需的結果。結果被發送到請求它的用戶代理,通過使用 neko.Lib.print()方法。在save動作情況下,execute() 方法也可以用于調用savePage()或者removePage()方法。removePage()方法當用戶發送一個空的內容時發生。文件被移除而不是簡單的留空內容,避免代碼庫生成垃圾文件。 getPageConent() 方法是一個公共方法,用在Page 類來獲得頁面的內容。默認的值可能被提供為方法的參數;這在請求的頁面不存在時使用。getTitle和getBreadcrumbLinks()方法也用在再Page類,他們返回頁面的標題,用它的URI分隔,和一個對象列表包含關于當前頁面和他的祖先的鏈接信息。其他的私有方法支持前面描述的操作,而且是自解釋的。 ~~~ import haxe.Stack; import neko.FileSystem; import neko.io.File; import neko.io.FileOutput; import neko.Lib; import Page; class WikiController { private static var FILE_EXTENSION = ".wiki"; private static var ROOT_PAGE = "/root"; private static var ROOT_URI = "/"; private static var DEFAULT_EDIT_TEXT = ""; private static var DEFAULT_VIEW_TEXT = "不存在的頁面,點擊編輯創建頁面"; private static var SAVE_MESSAGE = "頁面內容保存成功"; private static var HOME_TITLE = "主頁"; public var uri(default, null):String; private var dir:String; private var action : RequestAction; public function new(uri:String, dir:String, action:RequestAction) { if(uri == ROOT_URI) uri = ROOT_PAGE; if(uri.substr(uri.length - ROOT_URI.length) == ROOT_URI) uri = uri.substr(0, uri.length - ROOT_URI.length); this.uri = uri; this.dir = dir; this.action = action; } public function execute():Void { var page:Page; switch (action) { case Edit: page = new PageEdit(this, DEFAULT_EDIT_TEXT); case View: page = new PageView(this, DEFAULT_VIEW_TEXT); case Save(content): if(content == "") removePage(); else savePage(content); page = new PageView(this,null,SAVE_MESSAGE); } Lib.print(page.render()); } public function getPageContent(alternative:String):String { if(pageExists()) return neko.io.File.getContent(getPageFile()); else return alternative; } public function getTitle():String { if(uri==ROOT_PAGE) return HOME_TITLE; else return StringTools.urlDecode(uri.substr(uri.lastIndexOf("/",0) + 1)); } public function getBreadcrumbLinks() { var list = new Array<LinkItem>(); if(uri != ROOT_PAGE) { var path = getPageFile(); while(path.length > dir.length) { if(FileSystem.exists(path)) list.unshift( {title:titleFromPath(path),uri:uriFromPath(path)} ); else list.unshift( {title:titleFromPath(path), uri:null} ); path = path.substr(0, path.lastIndexOf("/")); if(path == dir) break; path += FILE_EXTENSION; } } list.unshift({title:HOME_TITLE, uri: ROOT_URI}); return list; } private function getPageFile():String { return dir + uri + FILE_EXTENSION; } private function getPageDirectory():String { return dir + getPageNamespace(); } private function getPageNamespace():String { return uri.substr(0, uri.lastIndexOf("/")); } private function pageExists():Bool { return neko.FileSystem.exists(getPageFile()); } private function uriFromPath(path:String) { var relative = path.substr(dir.length); return relative.substr(0, relative.length - FILE_EXTENSION.length); } private function titleFromPath(path:String) { var file = StringTools.urlDecode(path.substr(path.lastIndexOf("/") + 1)); return if(file.substr(file.length - FILE_EXTENSION.length) == FILE_EXTENSION) file.substr(0, file.length - FILE_EXTENSION.length); else file; } private function savePage(content:String) { ensureDirectoryExists(getPageDirectory); var out = File.write(getPageFile(), true); out.write(content); out.close(); } private function removePage() { FileSystem.deleteFile(getPageFile); if(uri != ROOT_PAGE) removeEmptyDirectories(getPageDirectory, dir); } private static function ensureDirectoryExists(dir:String) { var base = if(dir.substr(0,2)=="//") "//" else dir.substr(0, dir.indexOf("\\")+1); var path = dir.substr(base.length); var parts = (~/[\/\\]/g).split(path); for(part in parts) { base += '/' + part; if(!FileSystem.exists(base)) FileSystem.createDirectory(base); } } private static function removeEmptyDirectories(dir:String, root:String) { var d = dir; while( d!=root && FileSystem.exists(d) && FileSystem.readDirectory(d).length==0 ) { FileSystem.deleteDirectory(d); d=d.substr(0, d.lastIndexOf("/")); } } } enum RequestAction { Edit; View; Save(content:String); } ~~~ 要使示例簡短,一些安全處理的實踐被省略了,但是小心畸形的URI。例如,取決于你如何處理它們,一個URI可能包含序列 %00 ,會阻止進一步的添加擴展,或者 ../ 會導航潛在的文件系統跳出公共可訪問web目錄。好的方式是使用正則匹配URI的正確性,詳細在第8章介紹。 src 目錄還包含 Page.hx ,里面包括了抽象類Page的定義,PageView.hx和PageEdit.hx文件包含雙關的類定義。麻煩一點的在于在Page類,而其他兩個只是添加一些針對查看和編輯特別的內容。頁面構造函數包括一個數組的鏈接,被顯示到頁面頭部。存在的值可以被修改同時新的頁面可以被添加來適應所需。 render 方法只是從其他的 renderX 方法組合輸出。這些方法被單獨定義所以派生類可以分別重載它們,避免代碼重復。 ~~~ class Page { private static var WIKI_HOME_PAGE = ‘Wiki - Home Page’; private static var LOGO_PATH = ‘/assets/logo.png’; private static var LOGO_ALT = ‘logo wiki’; private static var BREADCRUMBS_TEXT = ‘Where am I?’; var controller : WikiController; var altcontent : String; var mainlinks : Array < LinkItem > ; private function new(controller : WikiController, altcontent : String) { this.controller = controller; this.altcontent = altcontent; mainlinks = new Array(); mainlinks.push( {title : “title”, uri : “/uri”} ); } public function render() : String { return renderHeader() + renderContent() + renderFooter(); } private function renderHeader() : String { var b = new StringBuf(); b.add(‘ < !DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”\n’); b.add(‘ “http://www.w3.org/TR/html4/strict.dtd”> \n’); b.add(‘ < html > \n’); b.add(‘ < head > \n’); b.add(‘ < title > ’ + getTitle() + ‘ < /title > \n’); b.add(‘ < link href=”/assets/main.css” type=”text/css” rel=”stylesheet” /> \n’); b.add(‘ < /head > \n’); b.add(‘ < body > \n’); b.add(‘ < div id=”header” > \n’); b.add(‘ < div id=”wiki-header” > < a href=”/” title=”’ + WIKI_HOME_PAGE + ‘” > ’); b.add(‘ < img src=”’ + LOGO_PATH + ‘” alt=”’ + LOGO_ALT + ‘” /> ’); b.add(‘ < /a > < /div > \n’); b.add(renderMainLinks()); b.add(‘ < /div > \n’); b.add(‘ < div id=”main” > \n’); b.add(renderBreadCrumbs()); b.add(‘ < div id=”content” > \n’); return b.toString(); } private function renderContent() : String { return controller.getPageContent(altcontent); } private function renderFooter() : String { var b = new StringBuf(); b.add(‘\n < /div > \n’); b.add(‘ < /div > \n’); b.add(‘ < /body > \n’); b.add(‘ < /html > ’); return b.toString(); } private function renderBreadCrumbs() : String { var b : StringBuf = new StringBuf(); b.add(‘ < div id=\”breadcrumbs\” > ’ + BREADCRUMBS_TEXT + ‘ \n’); b.add(‘ < ul > \n’); var list = controller.getBreadcrumbLinks(); for(i in 0 ... list.length) { if(i == list.length -1) b.add(‘ < li > ’ + list[i].title + “ < /li > \n”); else if(list[i].uri == null) b.add(‘ < li > ’ + list[i].title + “ ? < /li > \n”); else b.add(‘ < li > < a href=”’ + list[i].uri + ‘” > ’ + list[i].title + “ < /a > ?< /li > \n”); } b.add(‘ < /ul > \n’); b.add(‘ < /div > \n’); return b.toString(); } private function renderMainLinks() : String { var b = new StringBuf(); b.add(‘ < div id=”main-links” > \n < ul > \n’); for(item in mainlinks) b.add(‘ < li > < a href=”’ + item.uri + ‘” > ’ + item.title + ‘ < /a > < /li > \n’); b.add(‘ < /ul > \n’); b.add(‘ < /div > \n’); return b.toString(); } private function getTitle() : String { return controller.getTitle(); } } typedef LinkItem ={ title : String, uri : String } ~~~ PageView.hx 文件只是添加了一個盒子到內容區域,用來顯示當一個信息傳遞到構造器。這個消息用來傳遞保存確認消息。 ~~~ class PageView extends Page { private var message : String; public function new(controller : WikiController, altcontent : String, ?message : String) { super(controller, altcontent); this.message = message; } private override function renderContent() { var result : String = ‘’; if(message != ‘’ & & message != null) { result += ‘ < div class=”message” > ’ + message + ‘ < /div > ’; } return result + super.renderContent(); } private override function renderFooter() { var b = new StringBuf(); b.add(‘\n < /div > \n’); b.add(‘ < div id=”page-links” > \n’); b.add(‘ < a href=”’ + controller.uri + ‘?action=edit” > edit < /a > ’); b.add(super.renderFooter()); return b.toString(); } } ~~~ PageEdit.hx 文件添加一個包裝和一些控件來跟頁面內容交互。 ~~~ class PageEdit extends Page { private static var EDIT_TITLE_PREFIX = ‘Edit: ‘; private static var CONTENT_LABEL = ‘The page content goes here:’; public function new(controller : WikiController, altcontent : String) { super(controller, altcontent); } private override function getTitle() { return EDIT_TITLE_PREFIX + super.getTitle(); } private override function renderHeader() { var b = new StringBuf(); b.add(super.renderHeader()); b.add(‘ < form action=”’ + controller.uri + ‘?action=save” ‘); b.add(‘method=”post” > \n’); b.add(‘ < div class=”control” > \n’); b.add(‘ < label for=”content” > ’ + CONTENT_LABEL + ‘ < /label > \n’); b.add(‘ < textarea name=”content” > ’); return b.toString(); } private override function renderFooter() { var b = new StringBuf(); b.add(‘ < /textarea > \n’); b.add(‘ < /div > \n’); b.add(‘ < div class=”control” > \n’); b.add(‘ < input type=”button” ‘); b.add(‘onclick=”window.location=\’’ + controller.uri + ‘\’” ‘); b.add(‘name=”cancel” value=”Cancel” / > \n’); b.add(‘ < input type=”submit” name=”submit” value=”Save” /> \n’); b.add(‘ < /div > \n’); b.add(‘ < /form > ’); b.add(super.renderFooter()); return b.toString(); } } ~~~ Wiki應用的整個代碼都寫好了。現在是時候編譯查看結果了。 添加 Wiki.hxml 文件到項目目錄。內容和上一個例子中的十分相似: ~~~ -cp src -neko web/index.n -main WikiApplication ~~~ 這次 main 類是 WikiApplication ,編譯單元為 index.n 。注意,無論何時一個HTTP相對的目錄被調用,Web服務器(NekoTools服務器默認,mod_neko如果正確配置也是)會尋找一個 index.n 文件在目錄中,并執行它。因為這個理由,你可以訪問 index.n 不用指定整個的文件名,但是只有web服務器執行點是web目錄的時候才可以使用。 ~~~ http://localhost:2000/ ~~~ 現在你可以看到主頁的內容,實際上是一個沒有內容存在的頁面。點擊編輯按鈕,但是等待,PAGE NOT FOUND?為什么吶?這是因為,如前面觀察到的,web服務器必須被指示,所有的調用直接訪問你的 index.n 文件。要做到這點,必須添加一個新的文件 .htaccess 到web 目錄。內容必須是: ~~~ < FilesMatch “^([_a-z0-9A-Z-])+$” > RewriteEngine On RewriteRule (.*) /index.n < /FilesMatch > ~~~ 這個簡單的文件表示web服務器使每個URI不匹配一個存在的文件都會跳轉到 idnex.n 文件。這個功能在Apache中需要啟動 mod_rewrite ,或者在Neko服務器需要開啟 -rewrite 開關。因此,你需要停止你的Neko服務器然后重啟它: ~~~ nekotools server -rewrite ~~~ 現在你可以刷新編輯頁面,插入一些內容到提供的表單并確認提交。這個頁面內容現在被修改保存和可視化。視覺的記過不是很好但是你可以通過添加一個樣式表和一些小的logo圖片迅速改善它。這些文件的引用已經在Page類產生的代碼中了。添加一個目錄 assets 到 web 目錄下,并創建一個圖片,名字是 logo.png(可以使用你的圖片編輯器作圖)。對于樣式表,添加文件 main.css ,樣式如下: ~~~ * { margin: 0; padding: 0; font-size: 9pt; font-family: Verdana, sans-serif; } img { border: 0; } div.message { margin: 10px 0; padding: 4px 4px 4px 32px; font-weight: bold; border: 1px solid; } div.message { background-color: #d5ffaa; border-color: #4a9500; } #breadcrumbs { border-bottom: 1px dashed #ccc; padding: 0 0 4px; margin: 0 0 16px; } #breadcrumbs ul { display: inline; } #breadcrumbs li { display: inline; margin-right: 4px; font-weight: bold; } #main { padding: 20px; } #main-links { padding: 10px; text-align: right; background-color: #f3f3f3; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; } #main-links li { display: inline; } #main-links a { border: 1px solid #999; text-decoration: none; padding: 2px 6px; background-color: #fff; color: #000; } #main-links a:hover { background-color: #aaa; color: #fff; } #page-links { margin-top: 60px; border-top: 1px solid #ccc; padding: 4px 0 0; } label { display: block; margin: 0 0 8px; } textarea { width: 98%; height: 240px; padding: 8px; font-family: monospace; } div.control { border: 1px solid #ccc; margin: 0 0 12px; padding: 8px; text-align: center; background-color: #eee; } h1 { font-size: 1.5em; margin-bottom: 1em; } h2 { font-size: 1.2em; margin: 0.5em 0; } h3 { font-size: 1.1em; margin: 0.5em 0; } p { margin-bottom: 0.5em; } pre{ background-color: #eeeeee; padding: 1em; font-family: monospace; } ~~~ Wiki應用可以通過添加功能大大的改進,例如: * 支持文檔歷史版本 * 限制用戶認證來添加編輯Wiki * 解決安全問題 * Wiki文本語法 這些功能的實現和其他的,留給你作為練習。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看