顯示具有 網際網路 標籤的文章。 顯示所有文章
顯示具有 網際網路 標籤的文章。 顯示所有文章

2021/10/15

JS DataSnap framework in Webpack

 


Webpack 5 初探筆記】這篇記錄了 Webpack 三項重點:

  • JS 入口
  • HTML Page 建立及嵌入 JS 設定。
  • 除錯模式

你以為已經很多了?不,還有線上除錯服務器 (Webpack devServer) 還沒用到,該篇文章僅就編譯功能處理,編譯完成後再到實機裡除錯。

所以本篇就來研究如何讓 Webpack devServer 和後端 WebAPI 結合,本篇文章會講到兩個重點:

  • 使用 devServer Proxy 來解決【跨來源資源共用(CORS)】問題。
  • JS DataSnap framework 修改呼叫 DataSnap API 的方式。

使用 devServer Proxy 來解決【跨來源資源共用(CORS)】問題

devServer 的基本功能是提供網頁服務,熱編譯重讀 (hot reload) 功能則是在前端網頁開發時省時好幫手,檔案一儲存畫面就立即更新,這能省下許多重新編譯的時間,所以這次就把使用它為目標。

webpack 設定 devServer 非常簡單,內容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
module.exports = {
  ...
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'),
        },
        hot: true,
        port: 9000,
    }
  ...
}

接著在 index.js 裡寫上範例程式碼:

1
2
3
setConnection("localhost", "8080", "")
var oldExecutor = new ServerFunctionExecutor("TServerMethods1", connectionInfo);
console.log("EchoString : ", oldExecutor.executeMethod("EchoString", "GET", ["A B C"]))

網頁在啟動後會出現錯誤:


已封鎖跨來源請求: 同源政策不允許讀取】這句話的翻譯就是:後端網頁伺服器未對 CORS 做出對應的處理,在這裡指的便是 DataSnap 伺服器。

Eden 的【Delphi in Depth DataSnap 網站應用程式全端開發】一書中所提到的 CORS 解決方案其實就是要解決前端伺服器呼叫 DataSnap 伺服器上的 API 所產生的問題,有興趣的開發者邀請你購買起來看,保證物超所值!

devServer 對 CORS 的處理方式:Proxy

來看看 Webpack 官網對 Proxy 是怎麼寫的:

当拥有单独的 API 后端开发服务器并且希望在同一域上发送 API 请求时,代理某些 URL 可能会很有用。

「擁有單獨的 API 後端開發服務器」以本篇指的就是 DataSnap 伺服器,所以按照官網的範例調整後,webpack.config.js 內容為:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'),
        },
        hot: true,
        port: 9000,
        proxy:{
            "/datasnap/rest":"http://localhost:8080"
        }
    },

現在,http://localhost:9000/datasnap/rest 的網址就會對應到 http://localhost:8080/datasnap/rest,完成後就來試一下:


設定成功,現在可以順利在開發時期輕鬆呼叫 DataSnap API! 😉


JS DataSnap framework 修改呼叫 DataSnap API 的方式

我在【JavaScript ES6 call DataSnap API with Promise Fetch】寫到使用 fetch 來取代 ServerFunctionExecutor.executeMethodUrl 裡的 XMLHttpRequest,但為了避免回呼地獄 (callback hell) 而放棄 ServerFunctionExecutor 整個類別,必須要說,我真的很喜歡 EMBT 寫的 ServerFunctionExecutor 類別,它不僅是我學習 JavaScript 的啟蒙,而且對瀏覽器相容性也達到 100%,連 IE6 都相容,我真的愛死 ServerFunctionExecutor 類別了!

修改 ServerFunctionExecutor.js 原始碼是一種方法,但每一版的 Delphi 對 ServerFunctionExecutor 類別都有或多或少的修改,我認為最好的方案就是【繼承】,既然要用 fetch,也表示要放棄對 IE 的相容性,那採用 JavaScript ES6/7 新標準也是可以的,程式碼如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import "./dsjs/connection"
import "./dsjs/ServerFunctionExecutor"

class DSFunctionExecutor extends ServerFunctionExecutor {
    constructor(className, connectionInfo, owner) {
        super(className, connectionInfo, owner)
    }

    /**
     * This function executes the given method with the specified parameters and then
     * notifies the Promise when a response is received.
     * @param url the url to invoke
     * @param contentParam the parameter to pass through the content of the request (or null)
     * @param requestType must be one of: GET, POST, PUT, DELETE
     * @param hasResult true if a result from the server call is expected, false to ignore any result returned.
     *                  This is an optional parameter and defaults to 'true'
     * @param accept The string value to set for the Accept header of the HTTP request, or null to set as application/json
     * @return This function will return the result that would have otherwise been passed to the Promise.
     */
    async #fetchMethodURL(url, contentParam, requestType, hasResult, accept) {
        if (hasResult == null) hasResult = true;

        requestType = validateRequestType(requestType);

        const fetchHeaders = new Headers();
        fetchHeaders.append("Accept", (accept == null ? "application/json" : accept));
        fetchHeaders.append("Content-Type", "text/plain;charset=UTF-8");
        fetchHeaders.append("If-Modified-Since", "Mon, 1 Oct 1990 05:00:00 GMT");
        const sessId = getSessionID();
        if (sessId != null)
            fetchHeaders.append("Pragma", "dssession=" + sessId);
        if (this.authentication != null)
            fetchHeaders.append("Authorization", "Basic " + this.authentication);

        const fetchParams = {
            method: requestType,
            body: contentParam,
            headers: fetchHeaders,
        }
        try {
            const response = await fetch(url, fetchParams)
            this.#parseFetchSessionID(response);
            const responseText = await response.text();
            let JSONResultWrapper = null;
            try {
                JSONResultWrapper = JSON.parse(responseText);
            }
            catch (e) {
                JSONResultWrapper = responseText;
            }
            if (response.status == 403) {
                if (JSONResultWrapper != null && JSONResultWrapper.SessionExpired != null) {
                    //the session is no longer valid, so clear the stored session ID
                    //a new session will be creates the next time the user invokes a server function
                    setSessionData(null, null);
                }
            }
            //all other results (including other errors)
            //return JSONResultWrapper;
            const returnObject = JSONResultWrapper;
            if (returnObject != null && returnObject.result != null && Array.isArray(returnObject.result)) {
                return returnObject.result[0];
            }
            return returnObject;
        }
        catch (err) {
            console.error('Error:', err)
            return err
        }
    };

    /**
     * This function executes the given method with the specified parameters and then
     * notifies the callback when a response is received.
     * @param methodName the name of the method in the class to invoke
     * @param requestType must be one of: GET, POST, PUT, DELETE
     * @param params an array of parameter values to pass into the method, or a single parameter value
     * @param hasResult true if a result from the server call is expected, false to ignore any result returned.
     *                  This is an optional parameter and defaults to 'true'
     * @param requestFilters JSON Object containing pairs of key/value filters to add to the request (filters such as ss.r, for example.)
     * @param accept The string value to set for the Accept header of the HTTP request, or null to set application/json
     * @return This function will return the result that would have otherwise been passed to the Promise.
     */
    async fetchMethod(methodName, requestType, params, hasResult, requestFilters, accept) {
        const url = this.getMethodURL(methodName, requestType, params, requestFilters);
        return await this.#fetchMethodURL(url[0], url[1], requestType, hasResult, accept);
    };

    /**
     * Tries to get the session ID from the Pragma header field of the given request/response object
     * If successful, will set the value of the $$SessionID$$ and $$SessionExpires$$ variables accordingly.
     * @param response the response from the http request
     */
    #parseFetchSessionID(response) {
        if (response != null) {
            //pragma may store the Session ID value to use in future calls
            var pragmaStr = response.headers.get("Pragma");

            if (pragmaStr != null) {
                //Header looks like this, if set: Pragma: dssession=$$SessionID$$,dssessionexpires=$$SessionExpires$$
                var sessKey = "dssession=";
                var expireKey = "dssessionexpires=";
                var sessIndx = pragmaStr.indexOf("dssession=");

                if (sessIndx > -1) {
                    var commaIndx = pragmaStr.indexOf(",", sessIndx);
                    commaIndx = commaIndx < 0 ? pragmaStr.length : commaIndx;
                    sessIndx = sessIndx + sessKey.length;
                    var sessionId = pragmaStr.substr(sessIndx, (commaIndx - sessIndx));

                    var sessionExpires = null;
                    var expiresIndx = pragmaStr.indexOf(expireKey);
                    if (expiresIndx > -1) {
                        commaIndx = pragmaStr.indexOf(",", expiresIndx);
                        commaIndx = commaIndx < 0 ? pragmaStr.length : commaIndx;
                        expiresIndx = expiresIndx + expireKey.length;
                        var expiresMillis = parseInt(pragmaStr.substr(expiresIndx, (commaIndx - expiresIndx)));
                        if (expiresMillis != 0 && expiresMillis != NaN) {
                            sessionExpires = new Date();
                            sessionExpires.setMilliseconds(sessionExpires.getMilliseconds() + expiresMillis);
                        }
                    }

                    setSessionData(sessionId, sessionExpires);
                }
            }
        }
    }
}

程式碼說明

因 fetch 回傳的 response 和 XMLHttpRequest 的不同,所以抄了原來的 executeMethodURL、 parseSessionID 為 #fetchMethodURL 和 #parseFetchSessionID,加了「#」是為私有屬性,以避免免被其它開發者誤用。

應用程式大多需要同步處理以得到較好的操作性,故使用 async / await 等待 response 回來。

修改和執行結果如下:

這裡要留意 Webpack Babel 外掛在編譯這類別的時候可能會出現【regeneratorRuntime is not defined】錯誤訊息,原因很複雜,【Webpack 前端打包工具 - 使用 babel-loader 編譯並轉換 ES6+ 代碼 】提供了說明,而解決方式很簡單:

安裝 Babel transform-runtime 外掛:

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
//babel.config.json


{
"presets": ["@babel/preset-env"], "plugins": ["@babel/transform-runtime"]
}


本次 npm 用到的指令

npm init
npm i webpack -D
npm install --save-dev css-loader
npm install --save-dev style-loader
npm install --save-dev html-webpack-plugin
npm install --save-dev mini-css-extract-plugin
npm install -D babel-loader @babel/core @babel/preset-env webpack
npm install --save-dev clean-webpack-plugin
npm install copy-webpack-plugin --save-dev
// 排除 ES6 太新語法造成 "Babel 7 - ReferenceError: regeneratorRuntime is not defined"
// https://stackoverflow.com/questions/53558916/babel-7-referenceerror-regeneratorruntime-is-not-defined
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime

結論時間

透過這次 Webpack 的練習,發現只要 JS 該引用的單元都正確引用,Visual Studio Code 都能夠正確追蹤到程式碼來源或 JS DOC,尤其是在追蹤 JS DataSnap framework,VS Code 真的有效加強學習的效果,向你推薦!

本次練習專案我放在 Github 上,連結在 See also 區,有興趣的朋友歡迎來看看。 😉


See also

2021/10/08

Webpack 5 初探筆記

 


在寫完【還在 React 從入門到放棄?告訴你選擇比努力還重要的事】這篇文章之後,原本要繼續我的 React 裸奔之旅,但看到【【前端速成】Webpack5 快速入門|Tiktok工程師帶你入門前端|布魯斯前端】的直播內容,發現 Webpack 和 Delphi 的專案檔實在太像了,手癢忍不住動手把自己的 jQuery 網站利用 Webpack 重新打包順道重構,過程很有趣,而且整理後的專案內容就像是看網站地圖一樣,看了心情大好。

趁還有記憶時把 Webpack 的參數檔和 NPM package 參數檔透過這偏文章寫下來。

Package.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// package.json
{
  "name": "eden_first_project",
  "version": "1.0.0",
  "description": "",
  "main": "maindesk.js",
  "scripts": {
    "build": "SET NODE_ENV=production && webpack",
    "design_build": "SET NODE_ENV=development && webpack && copyfile_with_debug.bat",
    "dev": "webpack serve"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.15.5",
    "@babel/preset-env": "^7.15.6",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^4.0.0",
    "html-webpack-plugin": "^5.3.2",
    "webpack": "^5.56.1",
    "webpack-cli": "^4.8.0",
    "webpack-dev-server": "^4.3.1"
  }
}

程式說明

devDependencies:內容是 npm 安裝的外掛工具及版本,功能有:

  • webpack 本體
  • webpack 外掛 -- 清理編譯後檔案
  • webpack 外掛 -- 頁面樣板設定
  • babel -- 轉譯 ES6 程式碼為其它瀏覽器可讀的程式碼內容

 

scripts:在命令提示字元視窗下執行 webpack 的指令,語法為【npm run [scripts]】,SET 是設定 Windows 的環境變數,可以依照【設計】或【產品】語法來改變 webpack 封裝設定。[&&] 是 Windows 執行多指令的節點,某些靜態檔案還可以透過批次檔進行複製。

npm 在對專案處理時都會以 package.json 作為進入點,比較要記的就是這兩個屬性。


webpack.config.js - part 1

程式說明

package.json 的 SET NODE_ENV,它會在 webpack.config.js 的 process.env 被 NPM 寫入環境變數。

先快速導覽大分類,之後再一個個進去看。

module.export 這是 webpack 專案檔的核心。

mode 設定【設計】或【產品】模式;entry 要被 webpack 編譯的 js 檔案。

output 指定原檔案編譯後存放的路徑和命名規則。

devServer 執行 webpack server 時 Web 伺服器的參數。

module webpack 在讀取 js 檔以外的檔案時,所需要的 loader

devtool 編譯時做對照檔 (map),使瀏覽器能夠讀出編譯和原始程式碼的對照檔

plugin webpack 外掛設定區。 

webpack.config.js - part 2


程式說明

entry 裡,要寫上所有需要編譯的 js 檔案,格式為【暱稱:路徑】,暱稱在之後的【plugins 》 HtmlWebpackPlugin】會用到。

output 裡,path 說明 entry 裡的檔案在編譯後所放置的路徑,以 package.json 所在路徑為 root,可以自由指定,filename 為了確保客戶端每次都會重新讀取,可以把檔案加入【hash】參數,編譯會將 hash 碼帶入檔名,如:maindesk.f22745d9520340e7af08.bundle.js。


webpack.config.js - part3


程式說明

plugin 裡面主要放 webpack 外掛模組,有:

CleanWebpackPlugin 是每次編譯當下都會將目的目錄內的檔案全數清空。

webpack.DefinePlugin 是定義編譯環境的全域變數,此例可以在網路中的任何 JS 寫上類似以下的內容:

可以在編譯時決定要執行哪部份的程式內容,好用!

HtmlWebpackPlugin 決定讀取的樣板和 HTML 檔案路徑,chunks 是決定要寫入 entry 的哪些檔案,以暱稱設定,設為空字串時則不匯入任何 js 檔;也可以使用 excludeChunks 決定不加入的 js 清單。


結論時間

學習 Webpack 的過程中不斷帶入以前寫 Delphi 專案檔的時光,所以還蠻能理解 Webpack 的各項設定的內容,都已經用編譯了,那就順便把 Delphi 的設定概念套入,Webpack 還有很多細節的設定,只要大方向有了,再往細節裡找的難度就會下降很多!

今日成就:把 jQuery 網站套入 Webpack 專案編譯成功!


和你分享 😉

2021/07/24

用CSS畫出IE限定的BorderColorLight、BorderColorDark等效果


最近在翻一些古早實作書籍,看到幾個別開生面的 HTML 屬性:BorderColorLight, BorderColorDark,使用 Chrome、FireFox 等支援 HTML5 的瀏覧器來開時會是這樣:


當時只覺得為什麼都是黑白線條,現在該是回來解謎的時候!😉

圖片中的上半部使用到 BorderColorLight, BorderColorDark 屬性,經查才知道是 IE 限定,之後在 IE 裡開啟效果如下:


效果還蠻好看的,原來表格也可以上色彩。

IE 真的好,用 CSS 才驚覺這 BorderColorLight 和 BorderColorDark 是神馬黑技術!

拆解 BorderColorLight 和 BorderColorDark

圖片來源:linuxtopia

Linuxtopia 的圖片真的太到位,連光源都送你了,Light 是指被太陽照到的地方,圖片以淺藍色表示;Dark 則是太陽照不到的地方,圖片以深藍色表示。

理解意思後,就要找 CSS 有沒有可以解的解方。

答案是有的,只是和 IE 相比在 CSS 上並不容易呈現,必須以上下左右獨立設置,回頭看看 IE,簡單明瞭!

原 IE 程式碼:

<table border="2" cellspacing="2" cellpadding="2" 
  width="99%" bordercolordark="#99cc66" bordercolorlight="#ffcccc">
  <caption>Eden IE Table Test</caption>
  <tr>
    <td>IE Column 1</td>
    <td>IE Column 2</td>
    <td>IE Column 3</td>
  </tr>
</table>

改 CSS 程式碼:

在修改前還要說明下,cellspacing 和 cellpadding 這兩個 HTML 屬性又是 IE 限定,這下又多了一道關卡:

圖片來源:維基百科

簡單的說就是填上一堆空白或留白,好在 CSS 這裡相對容易理解,就是多了一道標籤手工活。

<style>
.cssIETable
{
  border-top: 2px solid #ffcccc;
  border-left: 2px solid #ffcccc;
  border-right: 2px solid #99cc66;
  border-bottom: 2px solid #99cc66;
  border-collapse: separate; /*cellspacing */
  border-spacing: 2px;
}
.cssIETable TD {      
   padding: 2;  /*cellpadding*/
} 
</style>
<body>
<table class="cssIETable" border="2"
  width="99%">
  <caption>Eden CSS Table Test</caption>
  <tr>
    <td>CSS Column 1</td>
    <td>CSS Column 2</td>
    <td>CSS Column 3</td>
  </tr>
</table>
</body>


嗯,那復古風味感終於又回來了!😎


See also

2020/10/28

My JSON Server -- 偽線上 REST 服務


開發 JavaScript 前端程式時,為了架設方便性,會使用類似 CodeSandbox 的前端線上程式編輯器進行測試,開發速度很快,對學習有加乘效果。

缺點就是在需要和後端 API 進行連結測試時,內部的 Web Server 往往還沒有在網際網路上公開,當然也就無法完整測試前端效果。這時就可以使用【My JSON Server】來模擬後端應用。

2020/02/19

Json SQL (JSQL) 概述

打造自己的GraphQL!
縁起

研究GraphQL後,發現它和SOAP的概念蠻像的,要先製定出前後端都認識的Schema,查詢的方式就是透過Schema放入查詢條件,很特別的是GraphQL可以在1次查詢取得多個Schema或多個查詢結果。省下很多Client發送Request/Response交握的時間成本。

SOAP的WSIL(WS-Inspection document)很類似GrapQL的Schema

2020/01/31

GraphQL使用評估

在學習React的過程有接觸到GraphQL這個新名詞,簡單來說GraphQL是:
一種新興的JSON Remote Procedure Call (RPC)規範
REST API寫多了,不是發展出一套龐大的路由切換器就是龐大的參數解析器

路由切換器
/person/id
/friend/id
/person/id/friend
...

參數解析器
persion_id=id
friend_id=id
...

如果系統簡單還好,但當系統一變多或變大,一定會有以下的問題:
  • API根目錄(ROOT)數量爆增,後端專案管理是個問題
  • API一旦異動(常見型別不同或欄位異動),前端必須等待後端更新,徒增開發效率不彰
GraphQL制定了前後端規範,只要後端Schema制定好,前端即有極大自由,減少工程師橫向溝通的時間就能加快開發速度。

使用GraphQL就能飛天?

前面說到,GraphQL嚴格來說僅止於制定規範,實作也只有提供簡易的Server讓使用者【嘗鮮】,實戰使用GraphQL往往需要搭配三方工具:Apollo或Relay,看來也是一條漫漫長路。

或許我們直接取用GraphQL的概念進行實作會更佳合宜。

能不能再簡單一點?

GraphQL到目前為止還是很新的技術,目前已知的資訊還不算成熟,如果自己採用類似JSON ORM的概念是否會更加簡單?

以上是目前對GraphQL一些看法,提供各位參考。謝謝各位觀看!

See also





2018/05/24

WebBroker cookie in ISAPI (CHT)


Cookie 在網頁開發幾乎是必須,而它有幾個限制:

  • 容量為 4 KB
  • 明碼傳遞
  • 會強制放在 Request / Response 一起傳送,增加傳輸流量

增加 4 KB 的傳輸流量在現在這個動不動就幾個 Mbit 的網路速度來說,離負載也差得太遠。

Https 也快要成為主流,所以明碼傳遞也不是問題。

最近的問事中,有一項是開發 WebBroker 專案中,在 Cookie 設計上遇到些麻煩:

  1. Exe / Debug Server 模式下,Cookie 在 Server 及 Client 皆可正確存取。
  2. ISAPI 模式下,Server 可以存取由 Server 建構的 Cookie,但 Client 找不到,反之亦然。

Delphi 3 以來,從沒見過有人提出這類問題。

2017/06/09

Delphi DataSnap REST server and HTML5 client

緣起:

前一陣子和網頁前端開發者合作一個網站的開發,使用 Delphi 開發 REST Service 非常的快,兩三下就完成交差。但就在這時,前端工程師卻問了我一個問題:

你的 REST API 要怎麼用 JavaScript 連?



也許有什麼好方法可以讓前端也能快速存取 REST 方法。這讓我想到從 XE 開始,DataSnap REST Application 就有內建 JavaScript 套件和 2 個 Boilerplate,也許可以從官方文件中吸取一些知識。

2016/04/05

HTML5 Builder 對非 PRCL framework 網站的除錯

HTML 5 Builder (以下簡稱 H5B),在除錯時,可以發現在網址列中有著「XDEBUG」字樣的訊息,如下圖:


H5B 在進行 Debug 時會接著一組帶有 XDEBUG 的字串

由此可知,H5B 利用了 Xdebug 完成「除錯工作」,不過目前在非 PRCL 專案,進入第二層後的網頁都無法除錯,還在查問題在哪邊……

to be continued...


See also:

2014/05/09

HTML5 Builder 的試用心得

HTML5 Builder 是 Embarcadero 的網頁製作產品之一
RadPHP 即是它的前身,HTML5 Builder 這名字更明確的表示它不只是著重在 PHP 設計上,也強化在 Client 端的 HTML5、Javascript 和 CSS 快速設計

如果想設計 Web Application 的話, HTML5 Builder 是個不錯的好選擇

2009/11/15

網站設計的評估(下)

上回提到了前端程式以及後端程式在賣出網站後,可能引發網頁原始碼外洩的可能性。

不論是ASP或是PHP,都會有可能發生的問題。

那麼,有其它解決方法的可能性嗎?

別忘了,還有一個被遺忘很久的網頁設計技術--CGI(Common Gateway Interface)

這是種類似像AJAX的一門動態網頁設計技術,唯一與JavaScript不同的地方是,CGI程式是各種程式語言都可以寫得出來的程式,這在C++ Builder上是很吃香的!

更棒的是,它編譯出來的是EXE檔,這在反解譯的部分可以說是大大提高了安全性(網路傳輸的安全性是屬另一個議題了)

在BCB裡編譯的好處是,還可以與系統週邊的硬體做通訊,由前端到後端都可以通吃,這也是其它網路程式設計無法替代的功能之一。

唯一的缺點應該就是CGI太耗資源了,同時10人連線時,WEB SERVER的系統資源就吃緊了呀!

嗯嗯…果然有一好就沒二好呀……

2009/11/10

網站設計的評估(上)

不論是用ASP, PHP做為後端對資料庫的存取及網頁程式設計,都可以將程式碼藏身在後端而不讓有心人偷走程式設計師的智慧財產。

但,如果是要把網站「賣」給客戶呢?

不論再怎麼藏,網頁的程式碼就是赤裸裸地躺在那邊讓客戶自由取用,這對程式設計師來說真是一件很冒汗的事情。

架構的好壞估且不說,日後可能有後續挖錢合作的可能性都沒有了,那設計師靠什麼吃飯呢?

---為了避免吃虧,所以把售價調高到「買斷」的等級,這樣就可以把損失減到最低。---

通常,我們第一個所想到的都是這個方法。但,客戶們也不是笨蛋,價格這麼高,怎麼買的下手呢?尤其是在現在經濟這麼不景氣的時代,更是要錙銖必較呀!

---那麼,加密就好了!這樣總不會被客戶解開了吧!哈哈哈!---

嗯,這也算一個好方法,畢竟會找程式設計師寫網站的客戶,就表示自己對這方面不熱。要破解什麼的,基本上也不太可能。

那麼,加密軟體不用錢嗎?天底下哪有這麼好的事情!價格還不便宜呢!

不過,現在的網路這麼發達,要找到加密軟體的xx程式,還不簡單!

別人會想到這招,那一定會想到如何「反加密」囉!

所以反加密的程式也可以在網站上找到……歹路不可走呀!

..........看來這個方法也不可行.............

2009/11/05

C++ Builder VS Web 初步心得

最近公司提出了要寫網頁的需求
雖然很早就知道BCB可以寫WEB網頁,但完全不知道它的能耐到哪邊
所以就開始找尋相關的資料

所有的資料寫的很詳盡,但對我來說還真是夠難的
還是直接從範例下手吧!
經過了一些範例中,總算是能了解BCB對WEB的詮釋了
簡易的架構圖大概會像是:

(BCB上對WEB SERVICE的簡化詮釋)

就上面的圖片就可以知道,BCB負責了「接收資料→處理需求→資料庫存取→輸出資料」四個步驟,也就是「BCB's WEB Module」這個區塊。

在書上寫著「不論你的web server提供cgi、isapi之類的服務,在BCB中的寫法一律相同。」

這有點像是...資料庫程式中BDE可以連接各式各樣的資料庫,但在BCB中的寫法都不會變的道理是相同的。

但事實上會這麼簡單的嗎?

要知道,如果這麼理想的話,在2006的版本之後就不會出現IntraWEB這種玩樣兒出現了。

事實上,在ie裡面看到的網頁叫什麼呢?



「網頁」才是主角,別忘記它了啊!

「HTML」!

是的,在書上的簡易範例中著墨最少的html,因為BCB所提供的元件只能回傳簡易的html碼,所以必需在html上多加學習。


既然要寫html,當然少不了css排版…還有很多很多。


而BCB對web的態度也僅僅是這樣嗎?

BCB6的誕生年代是沒有ASP這種東西的,在當時的網頁如果要做電子商務的話,除了CGI、JAVA外,就沒有其他東西可以用了。所以只能將就點用了,在當下的時機點,CGI是歷史最悠久的,所以當時的寶蘭才會覺得非它莫屬了吧(自以為)。

會不會覺得太簡單了呢?