Tag: access-control
【用Keycloak學習身份驗證與授權00】開始之前
這系列文章將帶大家探討軟體開發上,那些身份驗證與授權的相關議題。此外的話題還有身份識別、存取控制。 以目前諸多流行應用都以非單人使用的狀況之下,身份驗證與授權,幾乎是每位開發者都會遇到的題目。 不管你是串接OAuth、管理資源、寫後臺界面,甚至在最初應用的設計,幾乎都會扯上邊。 在業務邏輯之外,這或許會是相當重要的一部分。
關於身份驗證與授權,每一個部分都非常重要,也都可以分開來看,卻也非常難分開來看。
Because these three techniques are so closely related in most real applications, it is difficult to talk about them separate from one another. In particular, authentication and authorization are, in most actual implementations, inextricable. 1
就如同在Oracle上可查詢到的相關資料,這些部分儘管代表者不同概念,但彼此非常相關,實在很難分開來看。雖然如此,每一個部分都是非常龐大的內容,而本系列將會着重於授權控制與存取控制。在此之上會在探討近來已經非常普遍的OAuth 2.0、Open-Id、單點登入(SSO)和基於角色的存取控制(RBAC)
Tag: anki
Tag: authentication
【用Keycloak學習身份驗證與授權】系列目錄
本系列同樣發表於iThome體人賽 - 用Keycloak學習身份驗證與授權。
本頁後面還有一些小後記喔~
- 【用Keycloak學習身份驗證與授權01】Quick Start(1)
- 【用Keycloak學習身份驗證與授權02】Quick Start(2)
- 【用Keycloak學習身份驗證與授權03】淺談身份驗證與授權(1)
- 【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
- 【用Keycloak學習身份驗證與授權05】什麼是Keycloak
- 【用Keycloak學習身份驗證與授權06】Keycloak的替代品
- 【用Keycloak學習身份驗證與授權07】什麼是OAuth
- 【用Keycloak學習身份驗證與授權08】OAuth 2
- 【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
- 【用Keycloak學習身份驗證與授權10】深入OAuth 2
【用Keycloak學習身份驗證與授權33】Device Code(4)
這次應用使用PySide
來實現界面;qrcode
來產生需要的QR Code;並使用requests
來與身份驗證與授權伺服器的API溝通。現在透過pip
進行安裝需要的packages。
pip install PySide6 requests qrcode
其實本來可以考慮用electron.js,但是基於一些考量,最後決定使用PySide。
在昨天,透過Qt Designer建立了兩個需要的使用者界面,今天來實現邏輯部分。
建立Widget
在之前所設計的ui檔案分別是:example-device-code-app.ui
和login-dialog.ui
。這部分會分別將這兩份載入到類別內使用。所以同樣來建立兩個Widgets:ExampleDeviceCodeApp
和loginDialog
。
【用Keycloak學習身份驗證與授權32】Device Code(3)
本文接續device code(2)
現在已經知道了Device Code的登入流程了,那麼實際應用起來是怎麼樣的呢? 本片來實現一個可以使用Device Code Flow登入的應用。
使用者界面設計
首先,與「快速開始」應用相同,同樣需要一個顯式使用者資訊的地方,以及登入與登出的按鈕。
是的非常簡單。但悄悄先回到RCF8628,有一部分描述使用者界面的範本。該界面建議包含:操作說明、登入連接和user_code
。
【用Keycloak學習身份驗證與授權31】Open ID Connect & Social Login(2)
Keycloak Open-Id Connect
其實除了使用GitHub等社群帳號登入外,Keycloak也可以作爲Open-Id登入的提供者(Provider)。接著需要使用Keycloak本身來實現社群帳號登入,因爲這樣子可以看到更多細節。
建立新的Realm
現在,需要一個新的帳號系統。你可以在建立一個Keycloak伺服器,或是建立一個新的Realm。不同Realm的帳號系統是獨立不相干擾的,所以這裏就先建議一個新的Realm – G00gle
。
【用Keycloak學習身份驗證與授權30】Open ID Connect & Social Login(1)
因為略過了一些JWT格式細節分析。所以這部分也有部分不會好好提到
到目前爲止,爲何不同應用可以使用同一個帳號登入,已經在說明Client解釋過。這是在相關系統的應用下,那麼…沒直接相關的系統呢?譬如:使用Google登入。像這種使用不同帳號系統登入的方式,在Keycloak分成兩種。第三方系統登入,這篇僅會說明與 OAuth / Open-Id 相關的一種。如果你使用過Firebase、Auth0等服務,或是看過使用Google、Facebook、Microsoft、GitHub帳號登入的應用,對就是這類。這種社群帳號登入(Social Login)的方式,與前幾天提到的內容相關,而且可以在Keycloak實現。
鐵人賽只會實現而已,一些細節和更多的範例並不會提到。 (雖然原本就計劃寫)
Social Login 社群帳號登入
以GitHub帳號登入
【用Keycloak學習身份驗證與授權29】JWT權杖格式介紹(1)
總覺得…直接開始說明什麼是JWT格式來著。但感覺這樣會很無聊,不如我們從已經拿到的Token來看吧!
至今爲止,除了存取權杖(access_token
)、更新權杖(refresh_token
)外,還拿到過識別權杖(id_token
)。仔細看三者,都有兩個「.
」可以將權杖分成三個部份。
這些權杖都可以透過JWT.io去解析。總之先透過Password Grant Flow取得access_token
和refresh_token
,或是透過「快速開始」應用取得id_token
。
【用Keycloak學習身份驗證與授權28】Role
在帳號系統下,除了帳號本身與帳號群組外,通常還存在一個非常重要的部分–角色(Role),更有基於角色的存取授權方式(RBAC)。
寫到有點累了,沒意外的話之後是會提到RBAC
帳號如果代表一個人,這個人可能有多個角色身份。可能是個老師、主任、校長;可能是爸媽、叔姨;可能是員工、部長、處長、老闆,且可能有一群人擁有同一種角色。角色和帳號群組有點像,但在Keycloak是兩個概念。除此之外,在Keycloak還分成兩類型角色– Realm Roles 和 Client Roles 。
建立 Realm Roles
首先,你可以建立Realm共用的角色,像是員工、老闆等等較爲通用的角色。
點選在 Realm 選單下的 Roles ,然後再點選 Add Role :
【用Keycloak學習身份驗證與授權27】User & Claim & Profile
接著來看看爲什麼更新帳號資訊,在「快速開始」會有那些變化。
這與client scope和claim有關。關於後者之後會在詳細說說,而目前就先了解一下這個現象發生的原因。
首先,在我們取得token
的時候曾申明需要的scope
爲openid profile email
。其中profile
這個scope爲這次變化的主要原因。
來到Keycloak管理選單下的 Client Scopes ,然後找到 profile 。
接著將頁籤切換到 Mappers , 你會看見一堆與 User Attribute 有關的設定。
【用Keycloak學習身份驗證與授權26】User & Group
帳號(User)
基本訊息
接著來看看與帳號有關的設定。
在之前,已經建立過一帳號–bob
。過去學習實驗,也都以bob驗證身份。接著我們要來更新一下這個帳號。
首先看一下基本訊息:
來添加一些資訊:
- Email:
bob@fake.email
- First Name:
Bob
- Last Name:
Lee
- Email Verified:
ON
此外,可以要求使用者在必須做一些事情,譬如:驗證信箱、更新密碼、更新個人資訊等。
再次登入到應用–「快速開始」,可以看到有一些訊息也有些不同了。
【用Keycloak學習身份驗證與授權24】Clients
Client與一些安全相關的設定
在OAuth架構下的Client(客戶端)可以想象成是一個一個的應用程式。到目前爲止也已經建立過幾個Client:
這些Client有著自己的規則、資源、授權方式等。
可以複寫一些Realm的設定,包含產生存取權杖的方式。像是認爲RS256
簽名不夠,需要使用到RS512
:
【用Keycloak學習身份驗證與授權23】Realm
Realm,中文或許會翻作「域」,但基本很像是程式開發上,語言層面提供的包(package
)或是命名空間(namespace
)。或者可能可以更貼切的說是工作空間(workspace)。
你可以想象就像是一個企業、部門或是其他組織。有著相同的一些規範,同事們在同樣地工作空間生活、工作。但不同的企業、部門或是其他組織,可能會有類似的規範,但兩者不互相影響。
會特別有這個概念,是因爲Keycloak是可以建立多個Realm的。也就是,在同一間公司內,不同部門都可以有自己的Realm,制定部門自己的管理規範。或是特別爲外部客戶建立一個Realm,並制定特殊規範。
不同的Realm內,有著自己的帳號系統、密碼規範政策等。利用這個特性,之後也會用來更清楚的理解Open-Id。
你也可以同樣簡單視爲一個帳號資料庫、身份驗證伺服器。特別的是在會話成立期間,可以不需要再進行一次驗證,而這部分,會在提到Client時在多做說明。
如何建立一個Realm
要建立一個Realm是非常簡單。在之前也建立過「quick-start」這個Realm。也幾乎就只需要給個名字而已。
【用Keycloak學習身份驗證與授權22】Keycloak使用基本概念:前導
【用Keycloak學習身份驗證與授權21】在Flow這段小旅途外的風景
在這一小段路中介紹了Password Flow、Implicit Flow、Code Flow、Refresh Token Flow、Client Credentials Flow、PKCE、Device Code Flow。有些模式已經被發現可能有潛在風險,有些模式無法單獨使用。這或許還不是全部,至少到現在為止都還沒有提到過金融級應用Flow–CIBA。
Client Initiated Backchannel Authentication Profile(CIBA)
本小節也不會詳細介紹CIBA(Client Initiated Backchannel Authentication)。儘管CIBA現在階段還只是草案(Draft),但在Keycloak v15版本中已經可以使用。大概也已經確實有一些應用使用。
為什麼你不該繼續使用Implicit Flow?
在談到Implicit Flow時候,提到過:
將存取權杖暴露在使用者面前也不是非常好的做法
【用Keycloak學習身份驗證與授權20】Device Code(2)
光要完成這個範例就花了幾乎整整一天
做完後決定…來拆篇這第二部份,將有部份內容會在【實戰篇】展開。 今天就先來看看成果。
成果發表
【用Keycloak學習身份驗證與授權19】Device Code(1)
+----------+ +----------------+
| |>---(A)-- Client Identifier --->| |
| | | |
| |<---(B)-- Device Code, ---<| |
| | User Code, | |
| Device | & Verification URI | |
| Client | | |
| | [polling] | |
| |>---(E)-- Device Code --->| |
| | & Client Identifier | |
| | | Authorization |
| |<---(F)-- Access Token ---<| Server |
+----------+ (& Optional Refresh Token) | |
v | |
: | |
(C) User Code & Verification URI | |
: | |
v | |
+----------+ | |
| End User | | |
| at |<---(D)-- End user reviews --->| |
| Browser | authorization request | |
+----------+ +----------------+
Figure 1: Device Authorization Flow
The device authorization flow illustrated in Figure 1 includes the
following steps:
(A) The client requests access from the authorization server and
includes its client identifier in the request.
(B) The authorization server issues a device code and an end-user
code and provides the end-user verification URI.
(C) The client instructs the end user to use a user agent on another
device and visit the provided end-user verification URI. The
client provides the user with the end-user code to enter in
order to review the authorization request.
Device Code Flow這個與前面幾個特別不一樣。在之前,以往都是從登入開始,然後跳轉頁面回到App(Client)。也就是通常先有的是前端通訊,然後才是後端通信。
【用Keycloak學習身份驗證與授權18】PKCE
+-------------------+
| Authz Server |
+--------+ | +---------------+ |
| |--(A)- Authorization Request ---->| | |
| | + t(code_verifier), t_m | | Authorization | |
| | | | Endpoint | |
| |<-(B)---- Authorization Code -----| | |
| | | +---------------+ |
| Client | | |
| | | +---------------+ |
| |--(C)-- Access Token Request ---->| | |
| | + code_verifier | | Token | |
| | | | Endpoint | |
| |<-(D)------ Access Token ---------| | |
+--------+ | +---------------+ |
+-------------------+
Figure 2: Abstract Protocol Flow
PKCE模式
說穿了PKCE是基於Code flow的安全強化版。在整個過程前後添加了兩個動作–產生code_verifier
和code_challenge
,並在最後透過code_challenge
驗證code_verifier
。其目的有很大程度是為了建立前端通訊與後端通訊的關聯。
原先風險
那麼先來看看原本發生了什麼問題。
【用Keycloak學習身份驗證與授權17】Client Credentials
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
Figure 6: Client Credentials Flow
嘗試 Client Credentials flow
Client Credentials,這個模式有點特別。除了前面看到的它可能與其他模式並用以外,最特別的是,單純使用它,完全不需要資源擁有者參予。總之先來看看:
你可以使用RESTfer嘗試看看:
grant_type: client_credentials
client_id: oauth_tools
client_secret: <之前所產生的secret>
或是同樣可以透過OAuth.Tools嘗試看看。
【用Keycloak學習身份驗證與授權16】Refresh Token
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
Figure 2: Refreshing an Expired Access Token
The flow illustrated in Figure 2 includes the following steps:
(A) The client requests an access token by authenticating with the
authorization server and presenting an authorization grant.
(B) The authorization server authenticates the client and validates
the authorization grant, and if valid, issues an access token
and a refresh token.
(C) The client makes a protected resource request to the resource
server by presenting the access token.
(D) The resource server validates the access token, and if valid,
serves the request.
(E) Steps (C) and (D) repeat until the access token expires. If the
client knows the access token expired, it skips to step (G);
otherwise, it makes another protected resource request.
(F) Since the access token is invalid, the resource server returns
an invalid token error.
使用refresh_token
取得access_token
接著是使用Refresh Token換取Access Token的流程。這大概是所有中最簡單的一個模式之一了。
但因爲先決條件是取得可用的 Refresh Token ,所以無法單獨存在。在RCF6749相關的流程圖中,關注的是G、H的部分。
至於一開始有什麼方式取得Refresh Token就非常的多。在已經介紹的密碼模式和code模式都有可能返回refresh_token
。
【用Keycloak學習身份驗證與授權15】Authorization Code
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Note: The lines illustrating steps (A), (B), and (C) are broken into
two parts as they pass through the user-agent.
Figure 3: Authorization Code Flow
Authorization Code是在 RFC6749第一個提到的流程,所以有時又被視爲 標準流程(Standard Flow) 。
它與前兩個流程很不一樣,分成 前端通訊(frontchannel) 和 後端通訊(Backchannel) 。不過,其實反倒是前兩個是所有模式裡的怪胎,在隱含模式下,後端通信並在前端通訊;在密碼模式下,根本不存在前端通信,資源擁有者需要高度信任客戶端(說穿了在前端通信下,資源擁有者也是高度信賴瀏覽器或代理(User-Agent))。
【用Keycloak學習身份驗證與授權14】Implicit (Legacy)
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
Note: The lines illustrating steps (A) and (B) are broken into two
parts as they pass through the user-agent.
Figure 4: Implicit Grant Flow
如果說password適用於原生應用環境(Native Application)下的話,接著就是適用於純前端環境。 在現在前後分離架構的情況,前端與後端連接並不緊密,甚至前端幾乎就可以視爲一個完整的應用。 因此將前端視爲授權框架下的「客戶端(Client)」也就不會太難理解。
【用Keycloak學習身份驗證與授權13】Password Grant (Legacy)
首先,先來看看直接使用帳號密碼授權的。
是的, OAuth 是有一個模式支援直接使用帳號密碼的。 與萬能鑰匙不太一樣的是,授權的結果仍然是由授權伺服器的權杖和資源伺服器決定。 儘管透過中央授權控制可以限制存取權杖可以做些什麼,但畢竟直接使用帳號密碼並不是特別好, 故在其他模式下都不適用時,才應該再考慮此模式。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Figure 5: Resource Owner Password Credentials Flow
事前準備
安裝RESTer
【用Keycloak學習身份驗證與授權12】Flows這一小段路上路前注意事項
其實我原本是想要 RESTer 幹到底的哈😜。
今天有一點是插話的。考慮到接下來幾天的內容,所使用到的工具會有點多樣,所以行前做個提醒。
首先,你最好了解:
- HTTP Request / Response
- HTTP API (Web API)
- JSON
- BASE64
諾對於Postman這類工具有所熟悉再好不過。但接者幾天會使用:
- RESTer
- curl
有一些情況會直接使用。 - python
主要用於格式化JSON。
除此之外,如果熟悉Bash
的話同樣也有助於理解所有內容。此外還有可能會使用到 OAuth Tools 、 jwt.io 。(JWT的部分更有可能出現在之後關於Open-Id內容前後)
但其實,以上並非全部都是必須。最重要的是希望你能夠學習到OAuth本身的部分。
【用Keycloak學習身份驗證與授權11】OAuth 2
終於要來談談OAuth裡定義的細節了~
目前OAuth 2.0 一共定義了7種流程(flow)。在未來本系列可能稱之爲模式,不同模式適用於不同情況、不同環境。 就是因爲如此,OAuth才有高彈性的優勢。
OAuth 2.0 的可擴展性和模塊化是其最大的優勢之一,因為這使得該協議適用於各種環境。然而,正是這種靈活性導致不同的實現之間存在基本的相容性問題。當開發人員想在不同的系統上實現 OAuth 時,它提供的眾多自定義選項容易使人困惑。
本系列會介紹的模式包含:
- Password Grant (密碼模式)
- Implicit (隱含模式)
- Authorization Code (Code模式)
- Refresh Token
- Client Credentials (特殊密碼模式)
- PKCE
- Device Code
儘管 Implicit 和 Password Grant 被標記爲傳奇的(Legacy),但有時候仍然可能會使用到。重要的是你應該知道什麼情況應該使用什麼模式。同時記住,即使一個系統按照規範正確地實現了 OAuth,也不意味著該系統在實踐中就是安全的。
「OAuth 2.0 實戰」有一章決策圖可以幫助你決定使用什麼模式。但本系列應該不會提供。
【用Keycloak學習身份驗證與授權10】深入OAuth 2
喔不,其實今天還不會真正提到OAuth 2.0的深度內容。今天要來談談的是取得資源的細節。
使用帳號密碼,假裝自己是用戶
首先先試著想想看,如果你想要寫一支程式代替你處理某些事情。譬如:收信、發信。 更詳細的說,你寫了一個信件的客戶端(如:Thunderbird、Outlook)。 然後你會需要告訴這支程式你信箱的登入帳號密碼,由他去代替你收信、寄信。這個樣子就像是你把你所有的祕密都交給了它, 交給了它那把萬能鑰匙,而你完全信任這支程式。
其實這種狀況還真不少見。尤其在於你所申請的帳號,和使用的客戶端服務實際就是同一個時,這種行爲在正常不過。
但當它們是不同服務時,就可能出現問題了。你還能信任你提交的密碼不會被誤用嗎?不可能發生?
你可能有Gmail的帳號,你會很正常的使用Gmail的服務。但你知道Gmail除了自己本身外,它還可以幫你收其他信箱嗎?
比如說你還有ymail的帳號,但你更喜歡Gmail的界面,所以你希望使用Gmail來處理yamil的信件。這時候其實你就是告訴了Gmail 關於yamil的帳號密碼。相對的,也就是你應該是信任的Google的服務。
【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
再談身份驗證與授權
現在,讓我們再一次把視線放到「身份驗證」和「存取控制」這些名詞身上。 在入門篇的「淺談身份驗證與授權」已經相當程度的解釋過各個名詞。 不過今天將要更關注在身份驗證與存取控制的細節上。
對於一個應用來說,最重要的是它的 業務邏輯 。 除了業務邏輯本身,為完成所需的工作,會需要取得必要之資源。這可能是一份檔案, 鏡頭、麥克風資源等不同種形式。
在 取得資源 過程中,也會有另外一層業務邏輯,也可能本身就是另一隻程式服務,對所需取得的資源,進行 存取控制 。
最後,爲了判斷是否具有存取該項資源的權限,有可能有必要進行 身份驗證或授權 。
【用Keycloak學習身份驗證與授權08】OAuth 2
這是入門篇的最後一天了,今天不會寫什麼內容,但來帶大家看個入門概念可用的工具 – OAuth 2.0 Playground。
OAuth 2.0定義了幾個flow,可用於不同情境下,由於後續會有更多詳細說明,所以今天只會帶大家初步認識,嚐鮮看看。
註冊帳號
點選 register a client and a user。別擔心,這是個隨時可以廢棄的帳號。你完全不用真的去記他,他也不會要求你提供什麼資訊。
然後你會得到一組帳號密碼。然後點選「open in new window」之後就可以按下「continue」。
【用Keycloak學習身份驗證與授權07】什麼是OAuth
先來回憶一下,何爲「授權」。試想像有一座宅邸,裏頭有無數房間。而你作爲這座宅邸的管家,擁有一把萬能鑰匙,可以開始宅邸內所有門扉。 此外,這把萬能鑰匙還有一個作用,就是產生出開啓特定門扉的鑰匙。 你可以產生出的鑰匙交給其他人,其他人就可以自由進出特定房間。這個動作就是「授權」。
OAuth 是一個開放標準的 授權協議 ,它允許 軟體應用 代表 資源擁有者 訪問資源擁有者的 資源 1。
OAuth是什麼?
【用Keycloak學習身份驗證與授權05】什麼是Keycloak
終於要來好好介紹一下甚麼是Keycloak了~
收先先來看一下Keycloak的基本資訊:
- 名稱: Keycloak
- 開發使用的程式語言: Java
- 公用: 單點登入驗證與授權工具
- 許可協議: Apache License 2.0
- 公開倉庫: https://github.com/keycloak/keycloak
- 官方網站: https://www.keycloak.org
- 撰寫當下最新版本: 15.0.2 (2021年8月20日)
在 快速開始 提到過起始畫面有一些細節:
【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
實際上,在昨天已經將多數基礎都已經解釋過了,不過我想到還有一些東西可以再多做補充的。
對啦! 擔心彈藥不足,把一篇拆成兩篇來啦!👻
沒有身份識別的存取控制
在我們拆分的整個流程中分成:身份識別、身份驗證、授權、存取控制。但現在,你將Web App登出後再登入一次,你會發現「授權」的部分不見了! 但我們不會立刻來討論這個部分。先來說說身份識別。
不覺得,身份識別在整個流程之中非常雞肋嗎?也就只是將你這個「自然人」與系統中存在的「帳號」對應起來。 也確實如此,在這樣的拆分中,身份識別對於存取控制並不是必要的。在後來已MAC爲基礎發展的存取控制框架,也多不直接與帳號相關。
別擔心,之後會提到什麼是MAC(強制存取控制, Mandatory Access Control)。
不過還有一個更直接沒了這個流程的例子。在以「單人使用」作爲設計的系統之中,我們只需要拿到鑰匙就可以進行存取。
什麼?你說現在還有這種系統嗎?其實還真不少呢,加密上鎖過的壓縮檔案,上鎖的部落格文章。還有授權之後的流程,可能也不包含身份識別。
【用Keycloak學習身份驗證與授權02】Quick Start(2)
昨天,已經完成了一部分配置,且也已經可以建立帳號並登入了。
不過,這只能算是半套,而今天要在來完成另外半套。
你可以按照昨天的做法,重新建立一個新的Client。
只是注意在建立的時候,「Root URL」改爲: http://localhost:4200
。
今天,我們要自己實現一個前端網頁去的Web App,然後綁定Keycloak去做登入。
前置要求:
- 用Keycloak建立一個Client
- 網頁開發基礎知識(HTML/CSS/JavaScript)
- TypeScript的部分知識
- Angular知識(非必要)
調整Keycloak的Client配置
前面說過,Keycloak的Client實際上並不是真正的Client Application,只是做了一些關聯。 今天就要來 快速開始 個自己的Web App。而首先,需要先調整Client的關聯:
- 選擇「Clients」
- 找到昨天建立的「my-quick-start-app」,然後點選「Edit」
這此調整主要做兩個修改:
【用Keycloak學習身份驗證與授權01】Quick Start(1)
開始之前~2🎃。開完笑的~
但是想了許久,總覺的就這麼直接開始解釋各個名詞不太好。
想找個範例又有諸多擔心。
不如…先來快速開始做個範例!
快速開始將分成兩天。 今天會先跑過一次簡單的流程,明天才會寫一點程式。
這兩天看完後,依照需求,你甚至可以開始開發自己的應用。
那我們從Keycloak開始吧!
今天的前置需求:
- 只要裝好docker就好囉~
- 阿!對,你還要安裝個瀏覽器。
(不過你拿什麼在看本系列文章呢?)
透過Docker建立一個Keycloak應用
docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:15.0.2
這麼一條指令就可以開始這系列多數內容了(吧)。現在Keycloak會聆聽本機的8080 port。嘗試用瀏覽器開啓 http://localhost:8080 後,你應該會看到以下畫面:
【用Keycloak學習身份驗證與授權00】開始之前
這系列文章將帶大家探討軟體開發上,那些身份驗證與授權的相關議題。此外的話題還有身份識別、存取控制。 以目前諸多流行應用都以非單人使用的狀況之下,身份驗證與授權,幾乎是每位開發者都會遇到的題目。 不管你是串接OAuth、管理資源、寫後臺界面,甚至在最初應用的設計,幾乎都會扯上邊。 在業務邏輯之外,這或許會是相當重要的一部分。
關於身份驗證與授權,每一個部分都非常重要,也都可以分開來看,卻也非常難分開來看。
Because these three techniques are so closely related in most real applications, it is difficult to talk about them separate from one another. In particular, authentication and authorization are, in most actual implementations, inextricable. 1
就如同在Oracle上可查詢到的相關資料,這些部分儘管代表者不同概念,但彼此非常相關,實在很難分開來看。雖然如此,每一個部分都是非常龐大的內容,而本系列將會着重於授權控制與存取控制。在此之上會在探討近來已經非常普遍的OAuth 2.0、Open-Id、單點登入(SSO)和基於角色的存取控制(RBAC)
Tag: authorization
【用Keycloak學習身份驗證與授權】系列目錄
本系列同樣發表於iThome體人賽 - 用Keycloak學習身份驗證與授權。
本頁後面還有一些小後記喔~
- 【用Keycloak學習身份驗證與授權01】Quick Start(1)
- 【用Keycloak學習身份驗證與授權02】Quick Start(2)
- 【用Keycloak學習身份驗證與授權03】淺談身份驗證與授權(1)
- 【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
- 【用Keycloak學習身份驗證與授權05】什麼是Keycloak
- 【用Keycloak學習身份驗證與授權06】Keycloak的替代品
- 【用Keycloak學習身份驗證與授權07】什麼是OAuth
- 【用Keycloak學習身份驗證與授權08】OAuth 2
- 【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
- 【用Keycloak學習身份驗證與授權10】深入OAuth 2
【用Keycloak學習身份驗證與授權33】Device Code(4)
這次應用使用PySide
來實現界面;qrcode
來產生需要的QR Code;並使用requests
來與身份驗證與授權伺服器的API溝通。現在透過pip
進行安裝需要的packages。
pip install PySide6 requests qrcode
其實本來可以考慮用electron.js,但是基於一些考量,最後決定使用PySide。
在昨天,透過Qt Designer建立了兩個需要的使用者界面,今天來實現邏輯部分。
建立Widget
在之前所設計的ui檔案分別是:example-device-code-app.ui
和login-dialog.ui
。這部分會分別將這兩份載入到類別內使用。所以同樣來建立兩個Widgets:ExampleDeviceCodeApp
和loginDialog
。
【用Keycloak學習身份驗證與授權32】Device Code(3)
本文接續device code(2)
現在已經知道了Device Code的登入流程了,那麼實際應用起來是怎麼樣的呢? 本片來實現一個可以使用Device Code Flow登入的應用。
使用者界面設計
首先,與「快速開始」應用相同,同樣需要一個顯式使用者資訊的地方,以及登入與登出的按鈕。
是的非常簡單。但悄悄先回到RCF8628,有一部分描述使用者界面的範本。該界面建議包含:操作說明、登入連接和user_code
。
【用Keycloak學習身份驗證與授權31】Open ID Connect & Social Login(2)
Keycloak Open-Id Connect
其實除了使用GitHub等社群帳號登入外,Keycloak也可以作爲Open-Id登入的提供者(Provider)。接著需要使用Keycloak本身來實現社群帳號登入,因爲這樣子可以看到更多細節。
建立新的Realm
現在,需要一個新的帳號系統。你可以在建立一個Keycloak伺服器,或是建立一個新的Realm。不同Realm的帳號系統是獨立不相干擾的,所以這裏就先建議一個新的Realm – G00gle
。
【用Keycloak學習身份驗證與授權30】Open ID Connect & Social Login(1)
因為略過了一些JWT格式細節分析。所以這部分也有部分不會好好提到
到目前爲止,爲何不同應用可以使用同一個帳號登入,已經在說明Client解釋過。這是在相關系統的應用下,那麼…沒直接相關的系統呢?譬如:使用Google登入。像這種使用不同帳號系統登入的方式,在Keycloak分成兩種。第三方系統登入,這篇僅會說明與 OAuth / Open-Id 相關的一種。如果你使用過Firebase、Auth0等服務,或是看過使用Google、Facebook、Microsoft、GitHub帳號登入的應用,對就是這類。這種社群帳號登入(Social Login)的方式,與前幾天提到的內容相關,而且可以在Keycloak實現。
鐵人賽只會實現而已,一些細節和更多的範例並不會提到。 (雖然原本就計劃寫)
Social Login 社群帳號登入
以GitHub帳號登入
【用Keycloak學習身份驗證與授權29】JWT權杖格式介紹(1)
總覺得…直接開始說明什麼是JWT格式來著。但感覺這樣會很無聊,不如我們從已經拿到的Token來看吧!
至今爲止,除了存取權杖(access_token
)、更新權杖(refresh_token
)外,還拿到過識別權杖(id_token
)。仔細看三者,都有兩個「.
」可以將權杖分成三個部份。
這些權杖都可以透過JWT.io去解析。總之先透過Password Grant Flow取得access_token
和refresh_token
,或是透過「快速開始」應用取得id_token
。
【用Keycloak學習身份驗證與授權28】Role
在帳號系統下,除了帳號本身與帳號群組外,通常還存在一個非常重要的部分–角色(Role),更有基於角色的存取授權方式(RBAC)。
寫到有點累了,沒意外的話之後是會提到RBAC
帳號如果代表一個人,這個人可能有多個角色身份。可能是個老師、主任、校長;可能是爸媽、叔姨;可能是員工、部長、處長、老闆,且可能有一群人擁有同一種角色。角色和帳號群組有點像,但在Keycloak是兩個概念。除此之外,在Keycloak還分成兩類型角色– Realm Roles 和 Client Roles 。
建立 Realm Roles
首先,你可以建立Realm共用的角色,像是員工、老闆等等較爲通用的角色。
點選在 Realm 選單下的 Roles ,然後再點選 Add Role :
【用Keycloak學習身份驗證與授權27】User & Claim & Profile
接著來看看爲什麼更新帳號資訊,在「快速開始」會有那些變化。
這與client scope和claim有關。關於後者之後會在詳細說說,而目前就先了解一下這個現象發生的原因。
首先,在我們取得token
的時候曾申明需要的scope
爲openid profile email
。其中profile
這個scope爲這次變化的主要原因。
來到Keycloak管理選單下的 Client Scopes ,然後找到 profile 。
接著將頁籤切換到 Mappers , 你會看見一堆與 User Attribute 有關的設定。
【用Keycloak學習身份驗證與授權26】User & Group
帳號(User)
基本訊息
接著來看看與帳號有關的設定。
在之前,已經建立過一帳號–bob
。過去學習實驗,也都以bob驗證身份。接著我們要來更新一下這個帳號。
首先看一下基本訊息:
來添加一些資訊:
- Email:
bob@fake.email
- First Name:
Bob
- Last Name:
Lee
- Email Verified:
ON
此外,可以要求使用者在必須做一些事情,譬如:驗證信箱、更新密碼、更新個人資訊等。
再次登入到應用–「快速開始」,可以看到有一些訊息也有些不同了。
【用Keycloak學習身份驗證與授權24】Clients
Client與一些安全相關的設定
在OAuth架構下的Client(客戶端)可以想象成是一個一個的應用程式。到目前爲止也已經建立過幾個Client:
這些Client有著自己的規則、資源、授權方式等。
可以複寫一些Realm的設定,包含產生存取權杖的方式。像是認爲RS256
簽名不夠,需要使用到RS512
:
【用Keycloak學習身份驗證與授權23】Realm
Realm,中文或許會翻作「域」,但基本很像是程式開發上,語言層面提供的包(package
)或是命名空間(namespace
)。或者可能可以更貼切的說是工作空間(workspace)。
你可以想象就像是一個企業、部門或是其他組織。有著相同的一些規範,同事們在同樣地工作空間生活、工作。但不同的企業、部門或是其他組織,可能會有類似的規範,但兩者不互相影響。
會特別有這個概念,是因爲Keycloak是可以建立多個Realm的。也就是,在同一間公司內,不同部門都可以有自己的Realm,制定部門自己的管理規範。或是特別爲外部客戶建立一個Realm,並制定特殊規範。
不同的Realm內,有著自己的帳號系統、密碼規範政策等。利用這個特性,之後也會用來更清楚的理解Open-Id。
你也可以同樣簡單視爲一個帳號資料庫、身份驗證伺服器。特別的是在會話成立期間,可以不需要再進行一次驗證,而這部分,會在提到Client時在多做說明。
如何建立一個Realm
要建立一個Realm是非常簡單。在之前也建立過「quick-start」這個Realm。也幾乎就只需要給個名字而已。
【用Keycloak學習身份驗證與授權22】Keycloak使用基本概念:前導
【用Keycloak學習身份驗證與授權21】在Flow這段小旅途外的風景
在這一小段路中介紹了Password Flow、Implicit Flow、Code Flow、Refresh Token Flow、Client Credentials Flow、PKCE、Device Code Flow。有些模式已經被發現可能有潛在風險,有些模式無法單獨使用。這或許還不是全部,至少到現在為止都還沒有提到過金融級應用Flow–CIBA。
Client Initiated Backchannel Authentication Profile(CIBA)
本小節也不會詳細介紹CIBA(Client Initiated Backchannel Authentication)。儘管CIBA現在階段還只是草案(Draft),但在Keycloak v15版本中已經可以使用。大概也已經確實有一些應用使用。
為什麼你不該繼續使用Implicit Flow?
在談到Implicit Flow時候,提到過:
將存取權杖暴露在使用者面前也不是非常好的做法
【用Keycloak學習身份驗證與授權20】Device Code(2)
光要完成這個範例就花了幾乎整整一天
做完後決定…來拆篇這第二部份,將有部份內容會在【實戰篇】展開。 今天就先來看看成果。
成果發表
【用Keycloak學習身份驗證與授權19】Device Code(1)
+----------+ +----------------+
| |>---(A)-- Client Identifier --->| |
| | | |
| |<---(B)-- Device Code, ---<| |
| | User Code, | |
| Device | & Verification URI | |
| Client | | |
| | [polling] | |
| |>---(E)-- Device Code --->| |
| | & Client Identifier | |
| | | Authorization |
| |<---(F)-- Access Token ---<| Server |
+----------+ (& Optional Refresh Token) | |
v | |
: | |
(C) User Code & Verification URI | |
: | |
v | |
+----------+ | |
| End User | | |
| at |<---(D)-- End user reviews --->| |
| Browser | authorization request | |
+----------+ +----------------+
Figure 1: Device Authorization Flow
The device authorization flow illustrated in Figure 1 includes the
following steps:
(A) The client requests access from the authorization server and
includes its client identifier in the request.
(B) The authorization server issues a device code and an end-user
code and provides the end-user verification URI.
(C) The client instructs the end user to use a user agent on another
device and visit the provided end-user verification URI. The
client provides the user with the end-user code to enter in
order to review the authorization request.
Device Code Flow這個與前面幾個特別不一樣。在之前,以往都是從登入開始,然後跳轉頁面回到App(Client)。也就是通常先有的是前端通訊,然後才是後端通信。
【用Keycloak學習身份驗證與授權18】PKCE
+-------------------+
| Authz Server |
+--------+ | +---------------+ |
| |--(A)- Authorization Request ---->| | |
| | + t(code_verifier), t_m | | Authorization | |
| | | | Endpoint | |
| |<-(B)---- Authorization Code -----| | |
| | | +---------------+ |
| Client | | |
| | | +---------------+ |
| |--(C)-- Access Token Request ---->| | |
| | + code_verifier | | Token | |
| | | | Endpoint | |
| |<-(D)------ Access Token ---------| | |
+--------+ | +---------------+ |
+-------------------+
Figure 2: Abstract Protocol Flow
PKCE模式
說穿了PKCE是基於Code flow的安全強化版。在整個過程前後添加了兩個動作–產生code_verifier
和code_challenge
,並在最後透過code_challenge
驗證code_verifier
。其目的有很大程度是為了建立前端通訊與後端通訊的關聯。
原先風險
那麼先來看看原本發生了什麼問題。
【用Keycloak學習身份驗證與授權17】Client Credentials
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
Figure 6: Client Credentials Flow
嘗試 Client Credentials flow
Client Credentials,這個模式有點特別。除了前面看到的它可能與其他模式並用以外,最特別的是,單純使用它,完全不需要資源擁有者參予。總之先來看看:
你可以使用RESTfer嘗試看看:
grant_type: client_credentials
client_id: oauth_tools
client_secret: <之前所產生的secret>
或是同樣可以透過OAuth.Tools嘗試看看。
【用Keycloak學習身份驗證與授權16】Refresh Token
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
Figure 2: Refreshing an Expired Access Token
The flow illustrated in Figure 2 includes the following steps:
(A) The client requests an access token by authenticating with the
authorization server and presenting an authorization grant.
(B) The authorization server authenticates the client and validates
the authorization grant, and if valid, issues an access token
and a refresh token.
(C) The client makes a protected resource request to the resource
server by presenting the access token.
(D) The resource server validates the access token, and if valid,
serves the request.
(E) Steps (C) and (D) repeat until the access token expires. If the
client knows the access token expired, it skips to step (G);
otherwise, it makes another protected resource request.
(F) Since the access token is invalid, the resource server returns
an invalid token error.
使用refresh_token
取得access_token
接著是使用Refresh Token換取Access Token的流程。這大概是所有中最簡單的一個模式之一了。
但因爲先決條件是取得可用的 Refresh Token ,所以無法單獨存在。在RCF6749相關的流程圖中,關注的是G、H的部分。
至於一開始有什麼方式取得Refresh Token就非常的多。在已經介紹的密碼模式和code模式都有可能返回refresh_token
。
【用Keycloak學習身份驗證與授權15】Authorization Code
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Note: The lines illustrating steps (A), (B), and (C) are broken into
two parts as they pass through the user-agent.
Figure 3: Authorization Code Flow
Authorization Code是在 RFC6749第一個提到的流程,所以有時又被視爲 標準流程(Standard Flow) 。
它與前兩個流程很不一樣,分成 前端通訊(frontchannel) 和 後端通訊(Backchannel) 。不過,其實反倒是前兩個是所有模式裡的怪胎,在隱含模式下,後端通信並在前端通訊;在密碼模式下,根本不存在前端通信,資源擁有者需要高度信任客戶端(說穿了在前端通信下,資源擁有者也是高度信賴瀏覽器或代理(User-Agent))。
【用Keycloak學習身份驗證與授權14】Implicit (Legacy)
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
Note: The lines illustrating steps (A) and (B) are broken into two
parts as they pass through the user-agent.
Figure 4: Implicit Grant Flow
如果說password適用於原生應用環境(Native Application)下的話,接著就是適用於純前端環境。 在現在前後分離架構的情況,前端與後端連接並不緊密,甚至前端幾乎就可以視爲一個完整的應用。 因此將前端視爲授權框架下的「客戶端(Client)」也就不會太難理解。
【用Keycloak學習身份驗證與授權13】Password Grant (Legacy)
首先,先來看看直接使用帳號密碼授權的。
是的, OAuth 是有一個模式支援直接使用帳號密碼的。 與萬能鑰匙不太一樣的是,授權的結果仍然是由授權伺服器的權杖和資源伺服器決定。 儘管透過中央授權控制可以限制存取權杖可以做些什麼,但畢竟直接使用帳號密碼並不是特別好, 故在其他模式下都不適用時,才應該再考慮此模式。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Figure 5: Resource Owner Password Credentials Flow
事前準備
安裝RESTer
【用Keycloak學習身份驗證與授權12】Flows這一小段路上路前注意事項
其實我原本是想要 RESTer 幹到底的哈😜。
今天有一點是插話的。考慮到接下來幾天的內容,所使用到的工具會有點多樣,所以行前做個提醒。
首先,你最好了解:
- HTTP Request / Response
- HTTP API (Web API)
- JSON
- BASE64
諾對於Postman這類工具有所熟悉再好不過。但接者幾天會使用:
- RESTer
- curl
有一些情況會直接使用。 - python
主要用於格式化JSON。
除此之外,如果熟悉Bash
的話同樣也有助於理解所有內容。此外還有可能會使用到 OAuth Tools 、 jwt.io 。(JWT的部分更有可能出現在之後關於Open-Id內容前後)
但其實,以上並非全部都是必須。最重要的是希望你能夠學習到OAuth本身的部分。
【用Keycloak學習身份驗證與授權11】OAuth 2
終於要來談談OAuth裡定義的細節了~
目前OAuth 2.0 一共定義了7種流程(flow)。在未來本系列可能稱之爲模式,不同模式適用於不同情況、不同環境。 就是因爲如此,OAuth才有高彈性的優勢。
OAuth 2.0 的可擴展性和模塊化是其最大的優勢之一,因為這使得該協議適用於各種環境。然而,正是這種靈活性導致不同的實現之間存在基本的相容性問題。當開發人員想在不同的系統上實現 OAuth 時,它提供的眾多自定義選項容易使人困惑。
本系列會介紹的模式包含:
- Password Grant (密碼模式)
- Implicit (隱含模式)
- Authorization Code (Code模式)
- Refresh Token
- Client Credentials (特殊密碼模式)
- PKCE
- Device Code
儘管 Implicit 和 Password Grant 被標記爲傳奇的(Legacy),但有時候仍然可能會使用到。重要的是你應該知道什麼情況應該使用什麼模式。同時記住,即使一個系統按照規範正確地實現了 OAuth,也不意味著該系統在實踐中就是安全的。
「OAuth 2.0 實戰」有一章決策圖可以幫助你決定使用什麼模式。但本系列應該不會提供。
【用Keycloak學習身份驗證與授權10】深入OAuth 2
喔不,其實今天還不會真正提到OAuth 2.0的深度內容。今天要來談談的是取得資源的細節。
使用帳號密碼,假裝自己是用戶
首先先試著想想看,如果你想要寫一支程式代替你處理某些事情。譬如:收信、發信。 更詳細的說,你寫了一個信件的客戶端(如:Thunderbird、Outlook)。 然後你會需要告訴這支程式你信箱的登入帳號密碼,由他去代替你收信、寄信。這個樣子就像是你把你所有的祕密都交給了它, 交給了它那把萬能鑰匙,而你完全信任這支程式。
其實這種狀況還真不少見。尤其在於你所申請的帳號,和使用的客戶端服務實際就是同一個時,這種行爲在正常不過。
但當它們是不同服務時,就可能出現問題了。你還能信任你提交的密碼不會被誤用嗎?不可能發生?
你可能有Gmail的帳號,你會很正常的使用Gmail的服務。但你知道Gmail除了自己本身外,它還可以幫你收其他信箱嗎?
比如說你還有ymail的帳號,但你更喜歡Gmail的界面,所以你希望使用Gmail來處理yamil的信件。這時候其實你就是告訴了Gmail 關於yamil的帳號密碼。相對的,也就是你應該是信任的Google的服務。
【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
再談身份驗證與授權
現在,讓我們再一次把視線放到「身份驗證」和「存取控制」這些名詞身上。 在入門篇的「淺談身份驗證與授權」已經相當程度的解釋過各個名詞。 不過今天將要更關注在身份驗證與存取控制的細節上。
對於一個應用來說,最重要的是它的 業務邏輯 。 除了業務邏輯本身,為完成所需的工作,會需要取得必要之資源。這可能是一份檔案, 鏡頭、麥克風資源等不同種形式。
在 取得資源 過程中,也會有另外一層業務邏輯,也可能本身就是另一隻程式服務,對所需取得的資源,進行 存取控制 。
最後,爲了判斷是否具有存取該項資源的權限,有可能有必要進行 身份驗證或授權 。
【用Keycloak學習身份驗證與授權08】OAuth 2
這是入門篇的最後一天了,今天不會寫什麼內容,但來帶大家看個入門概念可用的工具 – OAuth 2.0 Playground。
OAuth 2.0定義了幾個flow,可用於不同情境下,由於後續會有更多詳細說明,所以今天只會帶大家初步認識,嚐鮮看看。
註冊帳號
點選 register a client and a user。別擔心,這是個隨時可以廢棄的帳號。你完全不用真的去記他,他也不會要求你提供什麼資訊。
然後你會得到一組帳號密碼。然後點選「open in new window」之後就可以按下「continue」。
【用Keycloak學習身份驗證與授權07】什麼是OAuth
先來回憶一下,何爲「授權」。試想像有一座宅邸,裏頭有無數房間。而你作爲這座宅邸的管家,擁有一把萬能鑰匙,可以開始宅邸內所有門扉。 此外,這把萬能鑰匙還有一個作用,就是產生出開啓特定門扉的鑰匙。 你可以產生出的鑰匙交給其他人,其他人就可以自由進出特定房間。這個動作就是「授權」。
OAuth 是一個開放標準的 授權協議 ,它允許 軟體應用 代表 資源擁有者 訪問資源擁有者的 資源 1。
OAuth是什麼?
【用Keycloak學習身份驗證與授權05】什麼是Keycloak
終於要來好好介紹一下甚麼是Keycloak了~
收先先來看一下Keycloak的基本資訊:
- 名稱: Keycloak
- 開發使用的程式語言: Java
- 公用: 單點登入驗證與授權工具
- 許可協議: Apache License 2.0
- 公開倉庫: https://github.com/keycloak/keycloak
- 官方網站: https://www.keycloak.org
- 撰寫當下最新版本: 15.0.2 (2021年8月20日)
在 快速開始 提到過起始畫面有一些細節:
【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
實際上,在昨天已經將多數基礎都已經解釋過了,不過我想到還有一些東西可以再多做補充的。
對啦! 擔心彈藥不足,把一篇拆成兩篇來啦!👻
沒有身份識別的存取控制
在我們拆分的整個流程中分成:身份識別、身份驗證、授權、存取控制。但現在,你將Web App登出後再登入一次,你會發現「授權」的部分不見了! 但我們不會立刻來討論這個部分。先來說說身份識別。
不覺得,身份識別在整個流程之中非常雞肋嗎?也就只是將你這個「自然人」與系統中存在的「帳號」對應起來。 也確實如此,在這樣的拆分中,身份識別對於存取控制並不是必要的。在後來已MAC爲基礎發展的存取控制框架,也多不直接與帳號相關。
別擔心,之後會提到什麼是MAC(強制存取控制, Mandatory Access Control)。
不過還有一個更直接沒了這個流程的例子。在以「單人使用」作爲設計的系統之中,我們只需要拿到鑰匙就可以進行存取。
什麼?你說現在還有這種系統嗎?其實還真不少呢,加密上鎖過的壓縮檔案,上鎖的部落格文章。還有授權之後的流程,可能也不包含身份識別。
【用Keycloak學習身份驗證與授權01】Quick Start(1)
開始之前~2🎃。開完笑的~
但是想了許久,總覺的就這麼直接開始解釋各個名詞不太好。
想找個範例又有諸多擔心。
不如…先來快速開始做個範例!
快速開始將分成兩天。 今天會先跑過一次簡單的流程,明天才會寫一點程式。
這兩天看完後,依照需求,你甚至可以開始開發自己的應用。
那我們從Keycloak開始吧!
今天的前置需求:
- 只要裝好docker就好囉~
- 阿!對,你還要安裝個瀏覽器。
(不過你拿什麼在看本系列文章呢?)
透過Docker建立一個Keycloak應用
docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:15.0.2
這麼一條指令就可以開始這系列多數內容了(吧)。現在Keycloak會聆聽本機的8080 port。嘗試用瀏覽器開啓 http://localhost:8080 後,你應該會看到以下畫面:
【用Keycloak學習身份驗證與授權00】開始之前
這系列文章將帶大家探討軟體開發上,那些身份驗證與授權的相關議題。此外的話題還有身份識別、存取控制。 以目前諸多流行應用都以非單人使用的狀況之下,身份驗證與授權,幾乎是每位開發者都會遇到的題目。 不管你是串接OAuth、管理資源、寫後臺界面,甚至在最初應用的設計,幾乎都會扯上邊。 在業務邏輯之外,這或許會是相當重要的一部分。
關於身份驗證與授權,每一個部分都非常重要,也都可以分開來看,卻也非常難分開來看。
Because these three techniques are so closely related in most real applications, it is difficult to talk about them separate from one another. In particular, authentication and authorization are, in most actual implementations, inextricable. 1
就如同在Oracle上可查詢到的相關資料,這些部分儘管代表者不同概念,但彼此非常相關,實在很難分開來看。雖然如此,每一個部分都是非常龐大的內容,而本系列將會着重於授權控制與存取控制。在此之上會在探討近來已經非常普遍的OAuth 2.0、Open-Id、單點登入(SSO)和基於角色的存取控制(RBAC)
Tag: browser
【工欲善其事,必先利其器】那些你可能不知道的Firefox Browser快速鍵
「工欲善其事,必先利其器 」現在人們最常使用的工具之一,大概就是瀏覽器。能夠了解工具的使用,對於生活處理事物的方式,也會有很明顯的幫助。過去我也研究寫過「Windows快速鍵」、「Libre Office使用方式」、「Google搜尋的隱藏技巧」、「簡單的Jupyter Notebook操作學習心得 」,或許未來會更新更新,再公開來分享給大家。
以前沒有直接寫的習慣。全寫在ODT裡面。 很久沒碰Windows了,還不知道有沒有改很多,大概也很多忘了。Libre Office可能也要更新下作法。 重點是,我不是很記得內容寫了啥?直接公開總覺得會有點害羞(。◕‿◕。)。
這個分享主要是在Linux Mint 19.2上做測試的。或許不同作業系統上有不同操作方式。可能要自己在嘗試看看。
題外話
透過DuckDuckGo和Google搜尋,能夠得到的快速答案也蠻不一樣的,就我看來,DuckDuckGo的更為準確。
參考資料
儘管以設計思考的角度來看,工具的設計應該要讓人好理解、好使用。但很難有如此完美的設計,因此一份完整的使用手冊非常必要。
Firefox Browser是Mozilla的Firefox系列產品的其中一個主力產品。這個系列包含Firefox Browser、Send、Monitor、Lockwise和收購的Pocket。
本篇文章主要參考http://mzl.la/16NAhwz ,以及https://moztw.org/firefox/support/keyboard/ (該頁面在後來測試中無法瀏覽)。你也可以參考以下內容(儘管似乎有部份陳舊):
重要快速鍵
Tag: c++
什麼是IIFE(Immediately Invoked Function Expression)
前言
這原本我是在Facebok一篇貼文的回應,因為覺得還蠻清楚的,所以修改過來這裡紀錄一下。(當然也可能我有理解錯誤就是)
什麼是IIFE
根據MDN是這樣寫的:
It is a “design pattern” which is also known as a Self-Executing Anonymous Function
(他又稱為 Self-Executing Anonymous Function,也是一種常見的"設計模式”)
注意 設計模式(design pattern)
這不是特指某一種技術、特性。而是思想、設計。竟然是想法,實現有差異、認知有差異可以理解的。
所以按照中文定義( 定義完馬上就執行 ),淺層廣義的來看:
function f(){};
f();
確實也是定義後執行
撰寫風格上OK,只是益處不大。
(f = function(){})() // execute first
f() // execute second
也是一樣。(這再js是合法的,之後還可以在執行f()
)
但就語法上就更精簡許多
從狹義、技術來看,是看是不是有附值(函式命名也是),然後才執行。 如此剛題的兩個,都是命名後才執行,狹義不算是IIFE 不過這終究只是一種設計模式,實現方式沒有一定。
function f(){};
f();
如果上面的形式,編譯器可以做優化,而直接忽略f
,那他也可能是狹義的定義。
(另外這裡廣義、狹義只是我片面的解讀)
此外,在維基百科這麼寫到:
“立即呼叫函式表達式” 最早稱為「自執行(匿名)函式」 但是立即執行的函式不一定是匿名的。 ECMAScript 5的 strict mode 禁止arguments.callee 因此,這個術語不夠準確。
這或許也側面證明我的想法。
Tag: c++11
JShell? 程式語言越來越像Lisp
JShell
JShell是在JAVA 9之後引入進來。 我是在翻JAVA 12(OpenJDK)工具的時後看到的,雖然好像長期支援(LTS)版本是JAVA 11… 算了,反正目前系統裝有JAVA 8(1.8)和手動下載的JAVA 12。
每個版本之前是有差異的。同樣都是編譯成class檔案,但在不同JRE下可能無法執行。上學期就有學弟妹來問,結果A編譯的B不能執行,只能由B來編譯分享給A。(向後兼容)
TK 之後再來說些這幾天看了幾個開源專案後對於這版本的事情的想法。
現在說回來 JShell。
Tag: coding
嘗試localhost的HTTPS伺服器
前言
因為一些原因,在區域網路開發環境的時後,還是需要HTTPS伺服器來做一些測試(最近瀏覽器實在太嚴格了Orz,不是HTTPS有些東西就無法做…)。
爲此,我主要參考了如何在五分鐘內建立SSL憑證和SSL是什麼鬼(方便起見,我改過標題,還請自己連回去看看原文)。前者甚至在GitHub建立了腳本可直接拿來使用,不過我決定還是親自嘗試一下。
除了上述兩篇以外,其實還有找到許多不錯的文章,還自徑往下看看參考資料。不過這兩篇,前者的CA與伺服器的私鑰、憑證是正式分開的,
此次我只想要建立一個自我簽署的憑證,能在區域網路使用就好。而後一篇足夠簡單,但應付我所需要的情況還需要做些調整。
快開始吧
使用環境:
- Linux
- OpenSSL
- Node.js
- express.js
建立伺服器私鑰(同時也是CA私鑰)
openssl genrsa -des3 -out server.key 4096
這邊我改成4096。雖然是區域網路使用,我還是想要高一點安全性。
Tag: common-lisp
【閱讀筆記】Common Lisp相關好文閱讀筆記
我對Common Lisp的喜愛應該不用多說。我不知道他還可以帶給我多少驚喜。
節錄幾個Common Lisp文章的相關敘述。
超凡脫俗的極限 - Common Lisp 文/田春
在文中最後寫:
原文: 超凡脫俗的極限 文/田春 鏈接已失效。
我閱讀位置:https://open343.github.io/Writing/zh-cmn-Hant/Overworldly-Common-Lisp.html
這應該是我第二次看,第一次看應該是在原本連結處。
語法
中序表達式可以徹底避免運算符優先級,例如 C 語言的表達式 1+23 在 Lisp 中將寫成 (+ 1 ( 2 3)) ,其中的 + 和 * 都是普通函數的名稱,和其他用戶定義的函數沒有區別。 值得注意的是,小括號的使用並不是必須,只是 Lisp 讀取器的一種標識,完全可以定製。如果用戶喜歡用中括號甚至後序表達式來描述 Lisp 程序,也是有可能的,相關的方法請查詢 Common Lisp 的 get-macro-character 和 set-macro-character 函數。
高度賦予程式成員自由性
Common Lisp 是唯一的允許程序員控制從源代碼到目標程序的所有方面的編程語言 。典型的 Lisp 代碼的處理分為三個階段:讀取、編譯、加載以及執行,其中每個階段都允許程序員介入。
- 在讀取階段,用戶可以設置特殊的讀取宏,用簡潔的形式讀取用戶自定義的對象;
- 在編譯階段,通過定義宏可以執行任意代碼來生成被編譯器所讀取的代碼;
- 在程序加載階段,附加的代碼有機會被執行,例如全局變量的初始化;
- 而在最終的程序執行階段,Lisp 系統還仍然有機會繼續編譯和加載程序的其餘部分,例如補丁,因為包括 compile 和 load 在內的函數是語言規範的一部分。
在讀取階段有set-macro-character
等讀取宏(read macro,我更喜歡使用原文。宏或巨集都不太能表達其強大)。
在編譯階段有defmacro
、define-compiler-macro
等可以使用。
適合嵌入使用的程式語言 在Raspberry Pi上的編譯紀錄
幾乎所有應用都不可能只使用一個程式語言完成。
忘了是那看過的,現在想想,當時他所說的,或許不只是ABI、與C交互、或是組合語言,像是應用層面的HTML,或是通訊成面的HTTP等,應也可以視為程式語言裡的異語言。既然無可避免,那多少了解一些其他語言也是必須的(不過近期打算更進一步XD),不過我自己初衷很單純是「興趣」(原來是興趣使然的程式語言研究員阿)。
之前曾經找過適合作為嵌入式,嵌入到其他程式語言的腳本語言有哪些。
當然是受到最適合、也是以此為目標設計的Lua影響,但Lua及其簡單,就表達層面上,不適合作為複雜應用。
拿個簡單的例子來說,同為原形設計的物件導向程式語言–EMCAScript後來在ES6也有了class
的語法糖。
總之,Lua很簡單,執行效率也極高,這想點我想是相輔相成的,也沒比要為了語法糖提高其語言解析(parse)的難度。但同樣作為一個圖靈完全(Turing completeness)的程式語言,Lua必然也可以實現複雜應用。
但既然Lua已經幾乎達到其設計目標,那如果需要其他設計方法怎辦,作法之一是使用Python這類膠水語言(glue language,Lua也是),但不管是CPython、CRuby還是Node.js恐怕實現都有些過於「肥大」。繞個路的作法就是使用其他實現,接著帶大伙看看幾個我認為有潛力的語言實現。
嵌入式程式語言
[Common Lisp] 狀況(condition)處理快速筆記
Common Lisp的狀況系統(Condition System)遠比其他語言的錯誤處理來的強大,卻也不那麼容易理解。LAG也學習了不下三次,在重新翻閱「實用Common Lisp」之後,有更深程度的理解。今天先來快速的寫下重點筆記。
Tag: contour
Tag: csharp
關於C#內建類型的怪異設計(LeetCode:reverse string)
前言
先說一下我的背景,可能會比較了解為什麼我會這樣想。我自己學過C++和Java,這兩個都不算是完全物件導向。相對來說,另外一個蠻愛使用的Python就連基本型態也是物件。至於C#,因為一些個人喜好的因素,並沒有去好好學習。不過也有好一段時間幫朋友除錯,除ASP.NET的錯誤。
之所以能夠幫忙,或快速上手,應該也與C++、Java、C#三者的歷史有關。在C++之後,推出了一個更物件導向的程式語言:Java,最基礎的單獨單位是class
,並且在JVM上運行,號稱可以跨平台、跨設備;隨後,Microsoft為了與Java競爭,並推廣自家的.Net Framework,學習了Java的設計,推出了C#。(早期更是有J#)
基礎型別&基礎類別
所以,三個是很相像的,更重要的是在物件導向的概念上。所以會一個,基本上另外兩個也就應該容易上手。不過C++從C遺留下來的基礎型態int
、char
、float
以及struct
等等,註定讓它不那麼物件。Java可能也有類似歷史因素,而為了處理這樣問題,Java將所有基礎型態做了包裝,一一對應1。
※ Java基本型態對應的類別
Java中有兩個型態系統,基本型態與類別型態,使用基本型態目的在於效率,然而更多時候,會使用類別建立實例,因為物件本身可以攜帶更多資訊,如果要讓基本型態像物件一樣操作,可以使用Long、Integer、Double、Float、Boolean、Byte等類別來包裹(Wrap)基本型態。 –Source: https://openhome.cc/Gossip/Java/Wrapper.html
為什麼會突然覺得C#設計的有點怪?
這主要是最近寫到一題LeetCode,要做字元陣列的反轉。如果使用C#的話,需要使用Array的類別方法。
public class Solution {
public void ReverseString(char[] s) {
Array.Reverse(s)
}
}
Tag: ctf
【筆記】第一次打CTF就放棄(BambooFox CTF的快速紀錄筆記)
第一次打CTF就放棄
(BambooFox CTF的快速紀錄筆記)
去年到今年,打了人生中第一個CTF。(其實也就2019/12/31~2020/01/01)
打完感想:「想打?我還早一百年😭」
還好有大神朋友Carry討論,要不然我都想打電腦了(物理)
有人可以一起討論的感覺真的很好~
※ 本篇主要修改自我分享給朋友的筆記。內容不涉及朋友補充的Reverse題形。主要原因還是因為我是想重新整理一下感覺,如果有興趣…可以去找找看我分享在那。
BambooFox CTF 在2019/12/31 18:00 ~ 2020/1/1 18:00期間舉辦,共計24小時。包含以下題形(中文參考字這篇文章):
- General
送分 - Web
網頁 - Crypto
加解密 - Reverse
逆向工程 - Pwn
弱點或漏洞分析 - OnlineJudge
題目 - Misc
綜合
Tag: develop
跨平台遊戲開發嘗試 貪吃蛇遊戲
Tag: ecl
適合嵌入使用的程式語言 在Raspberry Pi上的編譯紀錄
幾乎所有應用都不可能只使用一個程式語言完成。
忘了是那看過的,現在想想,當時他所說的,或許不只是ABI、與C交互、或是組合語言,像是應用層面的HTML,或是通訊成面的HTTP等,應也可以視為程式語言裡的異語言。既然無可避免,那多少了解一些其他語言也是必須的(不過近期打算更進一步XD),不過我自己初衷很單純是「興趣」(原來是興趣使然的程式語言研究員阿)。
之前曾經找過適合作為嵌入式,嵌入到其他程式語言的腳本語言有哪些。
當然是受到最適合、也是以此為目標設計的Lua影響,但Lua及其簡單,就表達層面上,不適合作為複雜應用。
拿個簡單的例子來說,同為原形設計的物件導向程式語言–EMCAScript後來在ES6也有了class
的語法糖。
總之,Lua很簡單,執行效率也極高,這想點我想是相輔相成的,也沒比要為了語法糖提高其語言解析(parse)的難度。但同樣作為一個圖靈完全(Turing completeness)的程式語言,Lua必然也可以實現複雜應用。
但既然Lua已經幾乎達到其設計目標,那如果需要其他設計方法怎辦,作法之一是使用Python這類膠水語言(glue language,Lua也是),但不管是CPython、CRuby還是Node.js恐怕實現都有些過於「肥大」。繞個路的作法就是使用其他實現,接著帶大伙看看幾個我認為有潛力的語言實現。
嵌入式程式語言
Tag: ecmascript
你可能都不瞭解的JS變數祕密 - 一文了解無宣告、var、let、const變數細節
本文優先發表於ALPHAcamp
前言
這類問題我被問到不止一次。不得不說JS的變數蠻特別的。新手菜鳥會問,連老鳥也都常搞錯。
更甚者…近日更是聽到一個自稱有五年經驗的軟體工程師稱: var
宣告的變數是全域變數
我知道我身邊的朋友,也有不少可能不清楚,或是沒探究這麼深入,相關文章有但不多。 於是乎…感覺我再想拖延,也應該把這篇文章寫出來。
這篇文章對於你寫更好的JS並沒有太多幫助,有許多部分平常根本不太會用到。但卻是非常基礎的概念。儘管不知道,通常按照當前常見規範,程式碼亦不會太糟糕。
這篇文章主要是從一份回覆修改而來。
變數的生存範圍
無關鍵字賦值、var
宣告、let
宣告最大的差別在於生存區域的不同。無關鍵字賦值 - 這意味著全域變數的宣告,當然你在全域範圍使用var
/let
宣告也是全域的。只是無關鍵字可能引發意外的情況,像是你預期變數應該是函數區域的:
function printG(){
g = 1
console.log(`printG: `, g)
}
printG() // => printG: 1
console.log(`Global G:`, g) // => Global G: 1
上例中全域情況也取得到在printG
函數裏定義的全域變數。這相當於你顯式定義g
於全域:
什麼是jQuery?前端框架盛行還需要JavaScript函式庫嗎?
- 本篇首發於ALPHACamp Blog(2020-03-25)
- 2020-04-05 更新
前端框架盛行還需要JavaScript函式庫嗎?
如果你想透過本篇文章,直截了當的知道該不該使用jQuery,可能要讓你失望了。
很難說到底應不應該使用jQuery,可能在某些情況下使用jQuery仍是最好的選擇。即使是現在的流行的三大前端框架,也是依照情況與需求使用。
了解jQuery到底是什麼、前端框架是什麼,會比要不要使用jQuery更有意義。本篇帶你看看我了解的jQuery前世今生
瀏覽器發展歷史上出現過幾次大混戰。儘管後來有了W3C(全球資訊網協會/World Wide Web Consortium)進行標準的制定,各個瀏覽器廠商仍開發各自瀏覽器的「特異功能」吸引使用者與開發者。這些特異功能促使了瀏覽器標準的進步,也照成了開發上的困難。
為解決網路應用中不同平台、技術和開發者帶來的不相容問題,保障網路資訊的順利和完整流通,全球資訊網協會制定了一系列標準並督促網路應用開發者和內容提供者遵循這些標準。1
2006年1月,jQuery
推出了第一個版本,隨後變成為了最受歡迎的JavaScript函式庫之一2。附帶一題,我們現在熟悉的HTML5標準是在2014年正式定稿推出3,即使是現在,每年瀏覽器的標準也是快速地在變動。在後文我會在說明為何開頭我想提到這兩件事情。
什麼是jQuery? 從常見功能來看
jQuery之所以受歡迎,是因為他提供了一系列簡單方便,而且兼容的操作,省去開發人員的部份煩惱。這裡舉幾個簡單的例子:
選取器
現代瀏覽器標準分成三個部份:
- HTML
- CSS
- ECMAScript / JavaScript
其中第三個就是常聽到的JS/Javascript。目前更正式的名稱是ECMAScript,也是今天主要jQuery主要的基礎。不同於瀏覽器腳本(Script)的標準,CSS2在以前就提供豐富易用的選擇器,讓設計人員撰寫樣式表(stylesheet)
7天搞懂JS進階議題(day07-1)-系列目錄
寫了這麼久的JS,你還在物件之前的時代嗎?只有資料、函式可以用,破破的抽象化,不會難以維護?儘管JS起初並不以物件導向設計,但透過原形鏈設計,其仍然可以具有好維護的物件導向特色。本系列從最基礎的this,深入ES6之後的class。
系列目錄
- 第0天 - Oh…開始了……
- 第1天 - this & bind: 你不能不知道的
- 第2天 - new & factory: 如何建立一個新物件
- 第3天 - Function & Object: 關於Prototype Chain繼承
- 第4天 - Class & constructor: 吃語法糖別噎到
- 第5天 - getter & setter: 屬性描述器
- 第6天 - yield & yield*: 生成器
- 第7天 - Symbol & Proxy: 以前沒有的
- 第8天 - Closure & Private:番外短篇 隱私成員
各章節簡介
(第1天) this & bind: 你不能不知道的
物件導向必不可少的,就是如何引用參考自己。
要是自己的錢包都拿不出來,你要怎買個冰棒?
(第2天) new & factory: 如何建立一個新物件
既然是要來物件導向,當然要先來學怎麼建立物件。本節帶你看看如何建立一個新的物件。
(第3天) Function & Object: 關於Prototype Chain繼承
在class
之前,必須了解的prototype chain。
JavaScript 是個沒有實做 class 關鍵字的動態語言,所以會對那些基於類別(class-based)語言(如 Java 或 C++)背景出身的開發者來說會有點困惑。(在 ES2015 有提供 class 關鍵字,但那只是個語法糖,JavaScript 仍然是基於原型(prototype-based)的語言)。
(第4天) Class & constructor: 吃語法糖別噎到
7天搞懂JS進階議題(day08) Closure & Private:番外短篇 隱私成員
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
這篇是臨時起意補的一篇短篇,用於示例如何模擬私有屬性。儘管這可能不是JS主流設計思想方法,但知道相信對你沒壞事。
在第5天-getter & setter: 屬性描述器,曾經看過這樣的例子:
var 神崎家 = class {
constructor(name){
this.__name = `神崎・${name}`;
}
static born(name){
return new 神崎家(name)
}
set name(new_name){
this.__name = `神崎・${new_name}`;
}
get name(){
let first_name = this.__name.substr(0,2),
last_name = this.__name.substr(3, this.__name.length + 1);
return `${first_name}・H・${last_name}`
}
introduce(){
console.log(`Hi~ My name is ${this.name}`)
}
rename(new_name){
this.name = new_name;
return this.name;
}
}
可能會有人問…為什麼__name
不直接這樣寫就好:
7天搞懂JS進階議題(day07)-Symbol & Proxy: 以前沒有的
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
最後一天,來看兩個特別的類別–Symbol
和Proxy
。
以前的物件(object)
key只能是字串
在以前,物件的key一定要是基礎字串,不過因為JS語法糖的關係可以不用加引號:
var obj = {
"name": "World",
}
// 等價於
var obj = {
name: "World",
}
如果不是呢?
7天搞懂JS進階議題(day06)-yield & yield*: 生成器
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
今天會往物件導向外頭邁出一步。是的,到昨天已經差不多把JS物件導向介紹的差不多了。那今天的主題是什麼呢?生成器(generator)。這個類型的建立與使用,和普通的JS類別有些不同,來看看吧!
生成器(generator)
什麼是生成器(generator)?簡單說就是一個 序列工廠 ,你跟他要東西他就給你東西,直到原料不足無法生產。
7天搞懂JS進階議題(day05)-getter & setter: 屬性描述器
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
有了物件然後呢?
今天來說說關於成員(field/attribute/member)背後的屬性。
getter & setter
有一個家族– 神崎家 ,生了一個小孩叫 アリア ,但生下後被 りこ 偽裝,被發現後重新命名為 アリア。
如有雷同存屬巧合
7天搞懂JS進階議題(day04)-Class & Constructor: 吃語法糖別噎到
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
現在你應該已經有發車前的基礎準備了。繫緊安全帶,撈思跡要踩油門加速了!
關於class
這個關鍵字,JS將其作為保留字好一段時間,直至ES6標準的制定,再經過瀏覽器漫長的實做,至今才有class
的語法糖可以使用。
7天搞懂JS進階議題(day03)-Function & Object: 關於Prototype Chain繼承
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
Prototype
有人說:「在寫JVM語言前,你必須先是Java程式開發人員」。
在寫TypeScript前,你還是得先會JavaScript。
[在進階一點(誤)] 在寫任何程式語言前,比必須先會組合語言
在class
之前,必須了解的prototype chain。
好拉,上面引言最後一個是來亂的,但是在正式開始寫JS的之前,你還是比需要有 prototype chain 的概念。
JavaScript 是個沒有實做 class 關鍵字的動態語言,所以會對那些基於類別(class-based)語言(如 Java 或 C++)背景出身的開發者來說會有點困惑。(在 ES2015 有提供 class 關鍵字,但那只是個語法糖,JavaScript 仍然是基於原型(prototype-based)的語言)。1
本節不會立馬就進到原形鏈,在那之前,會先來看看Python、Ruby這類OOP裡同樣有的東西。如果你不是從那來的,可以直接到原形鏈去看。但建議多少還是看一點,有些在JS還蠻常用的。
7天搞懂JS進階議題(day02)-new & factory: 如何建立一個新物件
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
既然是要來物件導向,當然要先來學怎麼建立物件。本節帶你看看如何建立一個新的物件。
直接建立
obj0 = {
name: "World",
hello(){console.log(`Hello, ${this.name}`)},
}
obj0.hello();
工廠模式
可以直接建立物件,也可以透過工廠模式建立並初始化物件。
7天搞懂JS進階議題(day01)-This & Bind: 你不能不知道的
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
this
物件導向必不可少的,就是如何引用參考自己。
要是自己的錢包都拿不出來,你要怎買個冰棒?
寫過C++、Java對於this
這個關鍵字應該不陌生,雖然JS的this
有著很大的不同,但再說明之前,為了來自其他地方的同鞋,容我再多提幾個相對應的例子。
來自Python、Ruby、Rust的朋友
你們可能習慣看到的是self
。
※ Note: Python可以不使用self
;Rust必須顯示宣告self
或&self
;Ruby則比較像是JS是[隱含宣告](#隱含宣告 vs 明確宣告)self
來自VB和VB.NET的朋友
你們會看到的是Me
、My
。
7天搞懂JS進階議題(day00)-Oh...開始了......
這系列文章內容會是什麼?
本篇整理了7項ES6之後的相關進階特性(this可能除外),包含:
- this & bind
- Prototype chain
- class & object
- getter, setter
- attr
- Symbol & Proxy
- yield
概述
寫了這麼久的JS,你還在物件之前的時代嗎?只有資料、函式可以用,破破的抽象化,不會難以維護?
儘管JS起初並不以物件導向設計,但透過原形鏈設計,其仍然可以具有好維護的物件導向特色。
目前已經被證實的是,物件導向程式設計推廣了程式的靈活性和可維護性,並且在大型專案設計中廣為應用。 — from Wikipedia
本系列從最基礎的this
,深入ES6之後的class
。包含:
this
物件導向必不可少。
關於隱式宣告與顯示宣告、特殊變數。
prototype
在class
之前,必須了解的prototype chain
class class和new。從constructor開始。
getter, setter 有了物件然後呢?關於成員(field/attribute/member)。
Tag: elisp
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
Tag: emacs
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
Tag: emacs-lisp
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
Tag: emcascript
適合嵌入使用的程式語言 在Raspberry Pi上的編譯紀錄
幾乎所有應用都不可能只使用一個程式語言完成。
忘了是那看過的,現在想想,當時他所說的,或許不只是ABI、與C交互、或是組合語言,像是應用層面的HTML,或是通訊成面的HTTP等,應也可以視為程式語言裡的異語言。既然無可避免,那多少了解一些其他語言也是必須的(不過近期打算更進一步XD),不過我自己初衷很單純是「興趣」(原來是興趣使然的程式語言研究員阿)。
之前曾經找過適合作為嵌入式,嵌入到其他程式語言的腳本語言有哪些。
當然是受到最適合、也是以此為目標設計的Lua影響,但Lua及其簡單,就表達層面上,不適合作為複雜應用。
拿個簡單的例子來說,同為原形設計的物件導向程式語言–EMCAScript後來在ES6也有了class
的語法糖。
總之,Lua很簡單,執行效率也極高,這想點我想是相輔相成的,也沒比要為了語法糖提高其語言解析(parse)的難度。但同樣作為一個圖靈完全(Turing completeness)的程式語言,Lua必然也可以實現複雜應用。
但既然Lua已經幾乎達到其設計目標,那如果需要其他設計方法怎辦,作法之一是使用Python這類膠水語言(glue language,Lua也是),但不管是CPython、CRuby還是Node.js恐怕實現都有些過於「肥大」。繞個路的作法就是使用其他實現,接著帶大伙看看幾個我認為有潛力的語言實現。
嵌入式程式語言
什麼是IIFE(Immediately Invoked Function Expression)
前言
這原本我是在Facebok一篇貼文的回應,因為覺得還蠻清楚的,所以修改過來這裡紀錄一下。(當然也可能我有理解錯誤就是)
什麼是IIFE
根據MDN是這樣寫的:
It is a “design pattern” which is also known as a Self-Executing Anonymous Function
(他又稱為 Self-Executing Anonymous Function,也是一種常見的"設計模式”)
注意 設計模式(design pattern)
這不是特指某一種技術、特性。而是思想、設計。竟然是想法,實現有差異、認知有差異可以理解的。
所以按照中文定義( 定義完馬上就執行 ),淺層廣義的來看:
function f(){};
f();
確實也是定義後執行
撰寫風格上OK,只是益處不大。
(f = function(){})() // execute first
f() // execute second
也是一樣。(這再js是合法的,之後還可以在執行f()
)
但就語法上就更精簡許多
從狹義、技術來看,是看是不是有附值(函式命名也是),然後才執行。 如此剛題的兩個,都是命名後才執行,狹義不算是IIFE 不過這終究只是一種設計模式,實現方式沒有一定。
function f(){};
f();
如果上面的形式,編譯器可以做優化,而直接忽略f
,那他也可能是狹義的定義。
(另外這裡廣義、狹義只是我片面的解讀)
此外,在維基百科這麼寫到:
“立即呼叫函式表達式” 最早稱為「自執行(匿名)函式」 但是立即執行的函式不一定是匿名的。 ECMAScript 5的 strict mode 禁止arguments.callee 因此,這個術語不夠準確。
這或許也側面證明我的想法。
【微更】你可能沒看過得Python - Callable(續)
【微更】Callable的實現
之前我實現了Callable Class,當時對於CallableWrapper
的實現如下:
class CallableWrapper:
def __init__(self, wrap):
self.wrap = wrap
def __call__(self, f=None, *args):
if f == None:
return self.wrap
return CallableWrapper(f(self.wrap, *args))
這個實現有一些缺憾,CallableWrapper
的__call__
回傳值,也是CallableWrapper
。而CallableWrapper
本身預期被呼叫,這導致結果不能直接使用,需要多給一次空呼叫(result()
)。舉例來說:
arr = CallableWrapper([1,2,3,4,5,6,])
arr # => <__main__.CallableWrapper object at 0x7f2102f00668>
# arr(sum) + 10 # You can't do this, because CallableWrapper can't add integer
arr(sum)() + 10
上例中,並不能直接寫arr(sum) + 10
,要寫arr(sum)() + 10
。這感覺有點脫褲子放屁阿…
基於此想法,我改寫成:
Tag: firefox
【工欲善其事,必先利其器】那些你可能不知道的Firefox Browser快速鍵
「工欲善其事,必先利其器 」現在人們最常使用的工具之一,大概就是瀏覽器。能夠了解工具的使用,對於生活處理事物的方式,也會有很明顯的幫助。過去我也研究寫過「Windows快速鍵」、「Libre Office使用方式」、「Google搜尋的隱藏技巧」、「簡單的Jupyter Notebook操作學習心得 」,或許未來會更新更新,再公開來分享給大家。
以前沒有直接寫的習慣。全寫在ODT裡面。 很久沒碰Windows了,還不知道有沒有改很多,大概也很多忘了。Libre Office可能也要更新下作法。 重點是,我不是很記得內容寫了啥?直接公開總覺得會有點害羞(。◕‿◕。)。
這個分享主要是在Linux Mint 19.2上做測試的。或許不同作業系統上有不同操作方式。可能要自己在嘗試看看。
題外話
透過DuckDuckGo和Google搜尋,能夠得到的快速答案也蠻不一樣的,就我看來,DuckDuckGo的更為準確。
參考資料
儘管以設計思考的角度來看,工具的設計應該要讓人好理解、好使用。但很難有如此完美的設計,因此一份完整的使用手冊非常必要。
Firefox Browser是Mozilla的Firefox系列產品的其中一個主力產品。這個系列包含Firefox Browser、Send、Monitor、Lockwise和收購的Pocket。
本篇文章主要參考http://mzl.la/16NAhwz ,以及https://moztw.org/firefox/support/keyboard/ (該頁面在後來測試中無法瀏覽)。你也可以參考以下內容(儘管似乎有部份陳舊):
重要快速鍵
Tag: fp
用Python實現Callable Class,FP更好寫
This Article has English Version, please goto here to read.
前言
最近,和朋友們在解LeetCode的題目。看著不同人寫出來的程式,也讓我對於一個題目的解法,有更寬廣的視野。
938. Range Sum of BST
URL: https://leetcode.com/problems/range-sum-of-bst/
這篇文章與這個想法,也是受到朋友寫的一個Ruby寫法的啟發。我用同樣的邏輯去寫Python,如下圖:
當然,這題目有更好更快的寫法,但這樣寫更為有趣有意思。
但是,內部隱含的block
函數,從函數型語言的設計原則來看,他是危險、骯髒的。這是因為每次他的執行,都會修改到外部變數s
的的值。(這辦法不夠 純函數 )
因此,我修改成以下方式:
如此,你還可以改寫成一行:
Tag: functional-programming
關於Python Lambda那些可能不知道的三兩事
lambda本質和function無異
def f():
pass
type(lambda : None) # => <type 'function'>
type(f) # => <type 'function'>
lambda :None # => <function <lambda> at 0x7ffa6d343650>
f # => <function f at 0x7ffa6d349dd0>
lambda和function的型態都是function
,並沒有區分開來。
一個是語句、一個是表達式
差別在於def
是關鍵字形成的語句(statement),lambda
是表達式(expression)。他們差別在於,能出現在程式碼的不同位置。
※ Note: 表達是(expression)也是一種語句(statement)。為了簡單來看,接下來都會區分開來。
內建有多個函式可以接受函式參數
因為lambda和function本質差無異,所以不會有函式只可以接受lambda,不可以接受function。
filter(lambda parameter: expression, iterable)
filter(function, iterable) # more correct
比起使用lambda描述filter,用function更正確。reduce等也是這樣。
lambda多行的寫法
通常看到lambda
都只有一行,正常情況下也都是這樣。不過先來看看文件怎麼描述lambda的語法:
lambda_expr ::= "lambda" [parameter_list] ":" expression
lambda_expr_nocond ::= "lambda" [parameter_list] ":" expression_nocond
相當於:
def <lambda>(parameters):
return expression
用Python實現Callable Class,FP更好寫
This Article has English Version, please goto here to read.
前言
最近,和朋友們在解LeetCode的題目。看著不同人寫出來的程式,也讓我對於一個題目的解法,有更寬廣的視野。
938. Range Sum of BST
URL: https://leetcode.com/problems/range-sum-of-bst/
這篇文章與這個想法,也是受到朋友寫的一個Ruby寫法的啟發。我用同樣的邏輯去寫Python,如下圖:
當然,這題目有更好更快的寫法,但這樣寫更為有趣有意思。
但是,內部隱含的block
函數,從函數型語言的設計原則來看,他是危險、骯髒的。這是因為每次他的執行,都會修改到外部變數s
的的值。(這辦法不夠 純函數 )
因此,我修改成以下方式:
如此,你還可以改寫成一行:
Tag: game
跨平台遊戲開發嘗試 貪吃蛇遊戲
Tag: golang
【心得筆記】精通 Go 程式設計
簡評
怎麼說呢…看完當下的感覺是覺得,這本書很特別,我想這會許是因為這本書與Go語言設計者本身有關係。
Alan Donovan特别感謝:Sameer Ajmani、Chris Demetriou、Walt Drummond和Google公司的Reid Tatge允許他有充裕的時間去寫本書;
Alan A. A. Donovan 是作者之一,從致謝頁來看,同時也是Google員工。而Go語言,又是Google所開發的一個簡潔、高效的程式語言。Alan很可能參與了Go語言的設計與開發,也很可能是最早使用Go語言的一批人。這或許說明為何此種,跟閱讀「松本行弘的程序設計」之後的感覺,是相似的。推薦大家閱讀。
推薦指數:★★★★☆
Tag: hough-transform
Tag: https
嘗試localhost的HTTPS伺服器
前言
因為一些原因,在區域網路開發環境的時後,還是需要HTTPS伺服器來做一些測試(最近瀏覽器實在太嚴格了Orz,不是HTTPS有些東西就無法做…)。
爲此,我主要參考了如何在五分鐘內建立SSL憑證和SSL是什麼鬼(方便起見,我改過標題,還請自己連回去看看原文)。前者甚至在GitHub建立了腳本可直接拿來使用,不過我決定還是親自嘗試一下。
除了上述兩篇以外,其實還有找到許多不錯的文章,還自徑往下看看參考資料。不過這兩篇,前者的CA與伺服器的私鑰、憑證是正式分開的,
此次我只想要建立一個自我簽署的憑證,能在區域網路使用就好。而後一篇足夠簡單,但應付我所需要的情況還需要做些調整。
快開始吧
使用環境:
- Linux
- OpenSSL
- Node.js
- express.js
建立伺服器私鑰(同時也是CA私鑰)
openssl genrsa -des3 -out server.key 4096
這邊我改成4096。雖然是區域網路使用,我還是想要高一點安全性。
Tag: https-server
嘗試localhost的HTTPS伺服器
前言
因為一些原因,在區域網路開發環境的時後,還是需要HTTPS伺服器來做一些測試(最近瀏覽器實在太嚴格了Orz,不是HTTPS有些東西就無法做…)。
爲此,我主要參考了如何在五分鐘內建立SSL憑證和SSL是什麼鬼(方便起見,我改過標題,還請自己連回去看看原文)。前者甚至在GitHub建立了腳本可直接拿來使用,不過我決定還是親自嘗試一下。
除了上述兩篇以外,其實還有找到許多不錯的文章,還自徑往下看看參考資料。不過這兩篇,前者的CA與伺服器的私鑰、憑證是正式分開的,
此次我只想要建立一個自我簽署的憑證,能在區域網路使用就好。而後一篇足夠簡單,但應付我所需要的情況還需要做些調整。
快開始吧
使用環境:
- Linux
- OpenSSL
- Node.js
- express.js
建立伺服器私鑰(同時也是CA私鑰)
openssl genrsa -des3 -out server.key 4096
這邊我改成4096。雖然是區域網路使用,我還是想要高一點安全性。
Tag: it鐵人賽
你可能不知道在JS世界裡的特殊物件
特殊物件清單
JavaScript是一個有著龐大使用族群的程式語言,但是因為其歷史淵源和不同考量等因素下,其中有不少令人萬丈摸不著頭緒的設計。自連class
都只作為保留字而無實際作用的時候,就已經有在接觸,在後續越了解越多,想想應該是能來分享一些,其中一些我知道的特殊物件。
undefined
null
this
super
NaN
Infinity
new
new.target
Object.prototype
- 先有
Function
還是先有Object
- 先有
Symbol
Symbol.for()
、Symbol.keyFor()
document.all
typeof document.all
arguments
hashbang
- HTML comment
'use strict'
globalThis
window
document
其中有一些並不是真正的物件,但都是一些執行環境下支援特殊寫法。或許有一些並沒有實際作用,但可能很多人並不知道,畢竟平常大概也沒有人會這樣寫吧!所以其實也就是一些JavaScript裡無關緊要的有趣小地方。
當然…當中有一部分也有可能成為你日後會踩入的陷阱(抗)。那麼就先來說說undefined
和null
吧!
undefined
undefined
是一個屬於undefined
的物件。(但可能不是唯一)
typeof undefined; // -> "undefined"
In all non-legacy browsers, undefined is a non-configurable, non-writable property. (Even when this is not the case, avoid overriding it.) – from MDN
儘管在現今主流的瀏覽器都是不可改變全域的undefined
變數:
這些那些你可能不知道我不知道的web技術細節-目錄與完賽感想
終於、終於30天啦啦啦!!!
原本其實是有猶豫今年要不要報名的,因為去年結果讓我有點失落憔悴…
然後這次也沒有組團,沒拖人下水成功。雖然也還是有發現一些認識的朋友今年也有參加,但還是一度在猶豫要不要報名,所以我幾乎是拖到最後一天才報名的哈哈。
工作的這兩年,遇到的事情這樣看來應該不算少。我有把一些我有興趣的議題記錄下來的習慣,雖然回去看也就一些很零碎的關鍵字,甚至有些一度回想不起來是什麼玩意兒。 這些東西我是有可能另外寫出來做記錄發表的,所以這次抱著反正之後也還是想寫,那就參加寫吧的想法報名,沒完賽就算了。
不過其實原本是有兩個參賽主題的,但最後只選擇了一個。一方便是另外一個對現在我來說不太好組織,另外就是時間安排上,我是很後來才真正決定要報名的。
而且在開賽前幾天確診Orz
隔離了7天
「這些、那些、你可能不知道、我不知道的Web技術細節」記錄了一些受到工作同僚、朋友聊天討論啟發,進一步研究原本我不知道或沒那麼清楚的Web技術細節。儘管足有接近30篇,但其實與我原本記下的關鍵字還是少了不少。像是WebAssembly、WebRTC、WebGL、Mono Repo、Micro Frontend等等。有些東西我有一些接觸,也有不少是還需要花費大量時間學習的。
而且這個系列,每一篇都至少花 超過兩個小時 構思撰寫。並且我實在不是很像破壞每一篇的獨立和完整性。所以有些篇數對於一次要閱讀完怕是會有些吃力,但基本每一篇都可以獨立參考閱讀,而不需要在意閱讀順序性。
目錄(依發表時間序)
你可能不知道的Web API--Web Locks
前言
Web Locks相關的API目前還是實驗性質的,這意味著未來可能有所變動,會與本片內容提及用法、作用有差異。雖然是實驗性質,但目前主流瀏覽器都已經支援。
使用方式
最基本用法是透過navigator.locks.request()
取得一把鎖,如果無法取得就必須等待直到能夠取得。如果取得了,就可以執行後續callback的動作。通常callback是一個異步函式,舉例來說寫法會如下:
navigator.locks.request('lock-1', async (lock) => {
console.log('get lock-1');
console.log('do something');
console.log('release lock-1');
});
callback的執行區域,被稱作是 關鍵區域 (Critical section)。
如果設計的恰當,關鍵區域只會有一個在執行。把上面再改寫一下:
var lock_name = 'lock-1';
navigator.locks.request(lock_name, (lock) => {
console.log(`A: get lock ${lock.name}`);
return new Promise(res => {
/// 10秒後釋放鎖
setTimeout(() => {
console.log(`A: release lock ${lock.name}`);
res(); // release lock
}, 10000 /*ms*/);
})
})
navigator.locks.request(lock_name, (lock) => {
console.log(`B: get lock ${lock.name}`);
return new Promise(res => {
/// 5秒後釋放鎖
setTimeout(() => {
console.log(`B: release lock ${lock.name}`);
res(); // release lock
}, 5000 /*ms*/);
})
})
A: get lock lock-1 A: release lock lock-1 B: get lock lock-1 B: release lock lock-1
在上面範例,有兩個程式區塊A和B需要使用到lock-1
這把鎖。A需要消耗10秒,並優先取得了鎖;B必須等待10秒後,才會開始執行。
可以透過將Promise
的resolve()
或reject()
傳遞出來,來決定什麼時候要釋放鎖:
你可能不知道的(Web)API--FinalizationRegistry(GC)
你可能不知道的Web API–FinalizationRegistry(GC)
FinalizationRegistry是和WeakMap、WeakSet、WeakRef在ES12一同進入到語言規範裡的兩個API。其實後面幾個更容易使用到,但我今天偏偏就是要來聊聊前者–FinalizationRegistry
。
在說說為什麼這個API有點雞肋可能沒什麼人知道之前,還是先介紹介紹這個API的用法。這個API的作用是在變數物件在被記憶體回收以前,可以註冊一些清理動作。比如可以建立物件:
var registry = new FinalizationRegistry((heldValue) => {
console.log(`${heldValue} is cleaned`);
})
var obj1 = { toString() { return "<Object obj1>"} };
registry
的callback function將快被記憶體回收的訊息打印出來。如果我們希望了解obj1
和obj2
何時被回收,可以用.register()
註冊:
registry.register(obj1, obj1.toString());
那麼當obj1
或obj2
不再可以被存取的時候,就有可能被記憶體回收,進而列印出訊息出來:
obj1 = null;
至於為什麼
obj1
不能使用delete
,可以參考「你可能都不瞭解的JS變數祕密 - 一文了解無宣告、var、let、const變數細節」
(突然發現這也是你可能不知道系列呢XD)
你可能不知道的Web API--postMessage
前言
postMessage()
是少數可以讓兩個不同頁面交換訊息的方式。如其名,傳遞訊息,postMessage()
接收一段文字訊息,將這個文字訊息傳遞給通知的對象。通知的對象可以監聽message
事件獲取訊息。
關於postMessage()
實際上現在瀏覽器網頁API上,存在的postMessage()
API不只一個。有window.postMessage()
、Worker.postMessage()
、BroadcastChannel.postMessage()
和Client.postMessage()
,它們有著類似的使用方式。除了不同頁面溝通外,對於建立的Web Worker執行緒也有相似方式傳遞訊息,除Worker.postMessage()
外,在Web Worker環境下還可以建立BroadcastChannel
物件,使用BroadcastChannel.postMessage()
方法。至於Client.postMessage()
是在Service Worker使用的,有在寫PWA(Progressive Web Application,漸進式網頁應用程式)才比較會用到。
本節主要討論的是最基本的window.postMessage()
。
基本用法
基本上的用法可以傳遞一個字串作為訊息發送出去,像是window.postMessage("msg")
。然後監聽message
事件。所以我們可以做一個簡單的例子。
現在建立兩個頁面index.html
作為主畫面,sub.html
作為用iframe
嵌入在主畫面的子畫面。
主畫面內容如下,除了一個發送訊息的文字話框外,還有一個接收訊息的文字畫框,以及一個發送訊息的按鈕。
<!------ index.html ------>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>[DEMO] postMessage (Main)</title>
</head>
<body>
<form>
<textarea cols="30" id="msg" name="" rows="10"></textarea>
<br/>
<textarea cols="30" id="rev" name="" rows="10" disabled></textarea>
<br/>
<button>send</button>
</form>
<hr/>
<iframe frameborder="0" src="sub.html"></iframe>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
相對來說子畫面就簡單一些,只有一個接受訊息的地方。它會將接受到的訊息原封不動的回傳回去。
你可能不知道的CSS Injection
前言
當你只是簡單地設置了一個CSP以後:
Content-Security-Policy: defalut-src 'self';
會發現誒~為什麼inline-script也不工作了?
<script type="text/javascript">console.log('Hello, World');</script>
要解決這個問題,同樣必須計算腳本雜湊值,然後設置新的內容安全政策:
Content-Security-Policy: defalut-src 'self';script-src 'sha256-2cq9aRSFdLqAC0FNx8cqcUjxA2Bmk5ZjlSvbIPQ1x/U=';
當然不止這種做法,還可以設置
nonce
等方式。
你可能不知道的內容安全策略(Content-Security-Policy, CSP)
前言
當我們知道了XSS,瞭解到對於外部資源的引用檢查是何等重要。那麼除了開發上需要注意意外,還可以怎麼做?
服務器設置CSP相關回應頭
CSP,全名Content Security Policy,也就是內容安全政策。這是為了告訴瀏覽器,這個頁面允許什麼行為,讓瀏覽器幫忙在檢查一次。與設個相關的主要有兩個Response Headers:Content-Security-Policy
和Content-Security-Policy-Report-Only
。
Content-Security-Policy
可以設定一些政策,當瀏覽器發現也面內容行為不符合這些政策的時候,就會被阻擋下來。
如果在一開始調整,有大量不確定受到影響的頁面,並不希望行為直接被阻擋下來,可以改先使用Content-Security-Policy-Report-Only
。當發現不符合政策的頁面內容時,先發送到後端的一個端點記錄下來。再所有被發現存在問題的頁面都已經修正後,再改成設置為Content-Security-Policy
。
繼續以前一個例子來用:A網站 http://a.127.0.0.1.nip.io:8000/index 有以下內容:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>CSP</title>
<link rel="stylesheet" href="http://b.127.0.0.1.nip.io:8000/xss.css" type="text/css" media="screen" />
</head>
<body>
<h1>Hello, World</h1>
</body>
<script type="text/javascript" src="http://b.127.0.0.1.nip.io:8000/xss.js"></script>
</html>
這次也真夠糟糕的,引用到兩份B網站 http://b.127.0.0.1.nip.io:8000 存在問題的內容–
/xss.css
和/xss.css
在部署的時候可以在伺服器回應的HTTP Response加入Header:
Content-Security-Policy: defalut-src 'self';
這次這兩個不安全的內容就會被瀏覽器阻擋下來。
如果確定外部資源內容是需要的話該怎麼辦?
你可能不知道的跨站腳本攻擊(Cross-Site Scripting,XSS)
前言
跨站腳本攻擊,英文Cross-Site Scripting,縮寫原本應該是CSS,但與階層樣式表–Cascading Style Sheets的縮寫相同,所以通常已X當做「交叉」的Cross,就變成是XSS。
在今天算是一個很嚴重的漏洞攻擊,因為有可能做到身份偽造,然後去進行資料竊取或破壞。但防禦跨站腳本攻擊,不單單只是前端開發工程師的責任,很大程度上也與服務如何部署有關係。
什麼是跨站腳本攻擊
跨站腳本攻擊,是在頁面上存在從其他來源引入的腳本,而這些腳本帶有惡意行為。要注意的是,腳本並不只是指JavaScript,HTML、CSS或其他資料內容也有可能是惡意注入的對象。
The malicious content sent to the web browser often takes the form of a segment of JavaScript, but may also include HTML, Flash, or any other type of code that the browser may execute.
惡意的JavaScript
比如說在A網站 http://a.127.0.0.1.nip.io:8000/index.html 有以下內容:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>XSS</title>
</head>
<body>
<h1>Hello, World</h1>
</body>
<script type="text/javascript" src="http://b.127.0.0.1.nip.io:8000/xss.js"></script>
</html>
在這個頁面中,使用了另一個網站B http://b.127.0.0.1.nip.io:8000 的JavaScript。但是其實xss.js
的檔案並不是自己可以掌控的,它的內容可能是:
你可能不知道的HTTP Header--If-Match和該怎麼設計Web API
前言
問個 假如我今天有個表單在前端要送,然後我想要先檢查這個表單有沒有被其他人更新
這是在一次和朋友的討論中有人提出來的問題。
恩…這是很典型資料競爭的問題,但並不是多個執行緒存取一份資料,而是多個瀏覽器客戶端(Browser Client)存取同份伺服器資源。
通常,我們在討論RESTful - 一種當下很常見的Web API設計方式的時候,只會提到建立(Create / POST)、讀取(Read / GET)、更新(Update / PUT)、補充(PATCH)和刪除(DELETE),也就是一般的CRUD。甚少會在深度討論一些細節,就如開頭提到的問題。並且通常在瀏覽網站,也很少去注意各個網站的API設計方式。
那麼這個問題要怎麼解決呢?
不管怎麼說,伺服器上的資料正確性最終需要由伺服器處理保護。 可以簡單一些在資料上添加版本(Version)或最後修改時間(Last Update time)的欄位。當收到更新或刪除訊息,該欄位應該保持一致。不過這就會出現一個很奇妙的現象:
> GET /data1
< 200 OK
< Content-Type: application/json
<
< {"name": "Bob", "age": 18, "version": 1}
當我們有一個資料data1
其版本為1
,如果我們需要更新的話,下面的訊息代表什麼意思?
> PUT /data1
> Content-Type: application/json
>
> {"name": "Alice", "age": 18, "version": 1}
< 204 No Content
這是一個更新,版本是不是應該跳到2
?但是按照前述這個版本應該要與記錄一致才會被修改,但是這樣似乎又與PUT
的意圖衝突?
或許有一些設計是檢查更新版本-1
是不是與當前記錄版本
相同作為檢查判斷條件。但是想想,其實版本號的決定,似乎不該由Client傳進的資料決定,而應該由後端資料管理行為決定。
那麼來介紹另一種設計方式,其實HTTP一般性規範都幫你準備好了,只是在我經驗觀察上,似乎並沒有那麼易用(也可能是因為了解的人真的不多),並不經常看到這樣的設計。這可能會包含一些你可能不知道的HTTP狀態碼(HTTP Status Code)和HTTP Headers。
你可能不知道的JS自動型別轉換
前言
如果要針對一個 物件陣列 取最大值該怎麼做?
一天,一位同事這麼問到。
首先需要先知道的是一般在JavaScript物件是無法比較大小的,所以這句話的意思是將物件特定屬性作為比較參考值,或是將物件數個屬性計算成一個可以比較的值後作為參考值,再進行比較尋找最大值。
起初,針對這個問題我第一想到的就是三種方式:
- 使用
Array.prototype.reduce()
- 使用
Array.prototype.sort()
- 使用
Math.max()
最後一個你可能會很好奇:物件無法比較,並且Math.max()
又不像Python的max()
可以輸入key
函式,這樣可以比較嗎?
沒錯,這個方式是存在問題的,但原因並不是因為「物件無法比較」,而是結果型別的問題,這個問題之後會提到。並且這也引發了今天的議題–「JS自動型別轉換」。
物件陣列怎麼取最大值?
這並不是今天主題的重點,也就不賣關子了,直接給出做法。
同樣拿Person類別為例子:
class Person {
name = "";
#birthday = new Date(); // 私有屬性
get birthday() { return this.#birthday }; // 調整處
addr = ""
get age() {
return new Date().getYear() - this.birthday.getYear();
}
constructor(name, birthday, addr = "") {
this.name = name;
this.#birthday = birthday; // 調整處
this.addr = addr;
}
hello() {
console.log(`Hello, ${this.name}.`);
}
}
現在有10個年幼不依的人:
var people = [
"Alice",
"Bob",
"Candy",
"Danel",
"Frank",
"Grant",
"Harry",
"Iris",
"Joe",
"Kevin"];
people = people.map(name => new Person(name, new Date(Math.floor(Math.random() * 100) + 1990, 1), ""))
現在如果希望找到年紀最大的,這裡提供幾種方式:
你可能不知道的JS物件私有屬性
前言
前幾年我曾經寫過「7天搞懂JS進階議題」系列文章,你可以在我的網站或是CoderBridge閱讀。其中在番外篇提到過「隱私成員」,在當時因為JavaScript並沒有隱私屬性的設計,所以想實現,當時使用了閉包和屬性描述器來處理。
時過境遷,在我寫完發表沒多久,ES11(2020)也正式推出了,其中就有關於私有屬性和私有方法的設計。根據Can I Use使用支援程度已經超過九成,也就是在今天主要瀏覽器除了一些舊版本和特別的瀏覽器外應該多數也都支援了。
原先JavaScript設計的問題
class Person {
name = "";
birthday = new Date();
addr = ""
get age() {
return new Date().getYear() - this.birthday.getYear();
}
constructor(name, birthday, addr = "") {
this.name = name;
this.birthday = birthday;
this.addr = addr;
}
hello() {
console.log(`Hello, ${this.name}.`);
}
}
var bob = new Person(/* name = */ "Bob",
/* birthday = */ new Date(2004/* year */,
1 /* mouth */,
1 /* day */),
/* addr = */ "臺灣")
console.log(`${bob.name}今年${bob.age}歲`); // Bob今年18歲
bob.hello();
bob.birthday = new Date(); // 變更生日
console.log(`${bob.name}今年${bob.age}歲`); // Bob今年0歲
如果設計了一個類別Person
有名字(name
)、生日(birthday
)、地址(addr
)等屬性,從嘗試性來說生日在出生以後就不能夠變了,但是上面程式碼片段當我們建立一個實例物件bob
後,依然可以變更生日。
你可能不知道的WebAuthN(FIDO)
名詞解釋
AuthN 和 AuthZ 分別是 Authentication 和 Authorization 的簡寫,也就是驗證和授權。
不光兩個英文字像…簡寫形式一樣容易讓人混淆🐷
不過比起全名或是 A12n 或 A11n ,這樣好分辨多了Orz…
那麼 WebAuthN 就是 Web 和 AuthN 的結合,也就是在 Web 上的身份驗證。這裡特別指的是由FIDO聯盟推出的FIDO2的其中一部分。特徵就是讓使用者在瀏覽器瀏覽網頁時,可以利用辨識、臉部辨識等方快速登入。至於FIDO是Fast IDentity Online的縮寫,也就是「快速線上身份識別」,也就不難理解聯盟成立的目的為何。
身份驗證的方式
在之前系列「用Keycloak學習身份驗證與授權」–淺談身份驗證與授權(1)和再談身份驗證與授權中,將整個身份驗證、授權到取得資源處理業務邏輯分成幾個部分看。
這次主要談的是WebAuthN,也就是身份驗證這一塊。能夠證明身份的方式通常又分成這麼幾種:
- 只有你知道。像是密碼、一次性密碼(OTP)、簡訊驗證碼等等。
- 只有你持有。像是汽機車鑰匙、硬體金鑰、手機手錶(手機或智慧手錶在附近自動解鎖)。
- 只有你天生是。指紋、虹膜、DNA。
你可能不知道cookie是怎被保存、保存在哪裡?
前言
再來是cookie最後,也是我認為最有意思的一部分–「cookie是怎被保存、保存在哪裡」 。
服務後端
對於開發後端服務的軟體開發人員,自己應當知道cookie存在哪裡,如何才可以將HTTP Request的狀態保留對應下來。而一般前端人員也不需要很清楚的知道cookie儲存在哪裡,只要認為儲存在瀏覽器內或記憶體內就好。
瀏覽器外存在哪裡
但是重開機後有一些cookie也存在,這表示必定有一個檔案儲存地方儲存這cookie。
以Google Chrome在Windows平臺為例,Cookie可能儲存在%LocalAppData%\Google\Chrome\User Data\Default\cookies
或%LocalAppData%\Google\Chrome\User Data\Default\Network\cookies
,這要看是什麼版本的若有在更新目前應該是後者。
這是以SQLite作為檔案型資料庫儲存的,因此可以使用對應的工具打開來看看:
你可能不知道cookie有些只能走特定道路(HTTP)
回信
當瀏覽器收到並儲存cookies後,在下一次的request就會將cookies跟著帶著送出去,回到Server。對於無狀態的HTTP,就可以使用這樣的方式讓Server回憶這個Request之前是誰,做到保留狀態。
同樣地接者調整前篇的程式碼片段。這次要將Server收到的Cookies直接作為Response的內容返回。先來添加需要的package:
from fastapi.requests import Request
然後添加一個endpoint–GET /cookies
,將Server收到的Cookies直接作為Response的內容返回:
@app.get('/cookies')
def cookies(request: Request):
return request.cookies
現在瀏覽 http://127.0.0.1.nip.io:8000/cookies 就可以收到傳給Server的Cookies作為JSON返回。
AJax with credition
你可能不知道cookie可以寄城市還可以分路段
預設收件區
Set-Cookie
就像寫信,除了內容當然還有收件區號。如果並沒有給Domain,就是當前的Domain。比如當瀏覽 http://localhost:8000/index1.html ,其記錄的Domain就是localhost
。
這個Domain就像是在寫信時填寫的區號,比如「桃園市楊梅區」的區號就是326
。
填寫收件區號
現在可以複製你可能不知道cookie是怎麼被製造出來的裡的DEMO程式碼內容,在此基礎上繼續修改,填寫收件區碼。
你可能不知道cookie是怎麼被製造出來的
前言
你是タコたち嗎?你喜歡吃cookies嗎?那麼你知道cookies是怎麼被製造出來的嗎?
cookie是怎麼被製造出來的
cookies是在瀏覽器儲存的小小資料片段,通常來說當瀏覽器發出request時,有可能同時將cookie發送出去。利用這個特性,可以將通常來說無狀態的HTTP保有記憶,做到登入功能、追蹤行爲等等。
雖然我們可以透過瀏覽器開發工具新增cookie。
你可能不知道的Function.prototype.bind()
前言
Function有三種用法,除了一般呼叫方式外,還可以使用Function.prototype.call()
或Function.prototype.apply()
方法。此外,Function還有一個很常見,偶爾會與後兩個用法混淆的方法–Function.prototype.bind()
。沒錯,這節就是要來說說Function.prototype.bind()
和另外兩者的差異,以及常見用法和你可能不知道的Function.prototype.bind()
。
<Fn>.call()
/<Fn>.apply()
和 <Fn>.bind()
的差異
由於過去其實我是寫過bind()
的相關內容的。所以我個人並不曾將三者搞混,蠻能區分用法上的不同的。不過在偶然幾次討論程式碼應該如何寫的過程中,發現偶爾會有人弄不清楚何時應該使用bind()
?何時使用其他兩者?
回頭看我過去所寫的,也蜻蜓點水的點到過call()
和apply()
。它們三者的參數形式確實有些像,特別是bind()
和call()
都接受一個thisArg
參數和多個參數展開。
所以Function.prototype.bind()
有甚麼不同之處嗎?
Function.prototype.call()
和Function.prototype.apply()
與一般函式呼叫寫在同一節裡,他們三著共同點是「會真的執行函示內容」。與他們不同的是Function.prototype.bind()
並不會真的執行函式,它會返回一個新的函式。
function helloWorld() {
console.log(`Hello World`)
}
helloWorld(); // 會印出 Hello World
helloWorld.call(); // 會印出 Hello World
helloWorld.apply(); // 會印出 Hello World
helloWorld.bind(); // 不會印出 Hello World。返回一個函式物件
新的函式物件與原本的可能沒有什麼差異:
var newFn1 = helloWorld.bind();
newFn1(); // 會印出 Hello World
雖然上面程式碼很像是直接賦值給變數,但還是有些差異。
var newFn2 = helloWorld;
newFn2 === helloWorld; // true。直接賦值的話是同一個函式物件
newFn1 === helloWorld; // false。使用bind()會產生一個新的函式物件,儘管它們用起來可能很像,但依然不同
直接賦值的話是同一個函式物件;相對來說,使用bind()
會產生一個新的函式物件。儘管它們用起來可能很像,但依然不同。
常見用法
在JavaScript裡面this
是一個特別的存在。它經常會有隱含綁定和隱含遺失的狀況。
為什麼你需要知道Function的三種用法
前言
在設計函式與呼叫函式前,或許得認識到一些限制,這些限制有可能造成需要使用不同的設計方式或呼叫方式。就來談一下一些在JavaScript語言裡的一些限制吧!
安全整數範圍
JavaScript裡關於「整數」是有範圍限制在的,按照規範這個值的範圍是(±2**53)
內,也就是-9007199254740991~9007199254740992
。這個值你可以透過Number.MIN_SAFE_INTEGER
和Number.MAX_SAFE_INTEGER
取得。
BigInt
在ES11後多加了一個基本類別BigInt
,儘管這個類型的使用方式和Number
並不相容1。但是在過去寫過的7天搞懂js進階議題中曾經使用過。如果你有需要超過-9007199254740991~9007199254740992
範圍的整數,可以考慮使用BigInt
。
陣列長度
Array
會需要留意:屬性.length
的最大值爲2**32-1
也就是4294967295
。
這意味著以下一些操作是會出問題的
var arr = Array(4294967296); // 超出最大範圍
{
let arr = Array(4294967295);
arr.push(0); // 超出最大範圍
}
此外,-1
的索引值並不是像Python會得到最後一個元素2。實際上經過以下操作:
var arr = [1,2,3,4];
arr[-1] = 5;
console.log(arr);
最後arr
的結果應該會像是:
[-1: 5, 1, 2, 3, 4]
另外.length
也不會算上-1
的索引值。
你可能不知道Function的三種用法
前言
原預計標題「你可能不知道的Math.max()
三種用法」。因為這是在調整Math.max()
時引發的話題。在這之後過了幾周,有另外一個同事詢問Function.prototype.call()
、Function.prototype.apply()
的差異。
因此,接下來將來看看「你可能不知道Function的三種用法」。除了一般的呼叫外,還有<Fn>.call()
和<Fn>.apply()
。試想已經有參數陣列args
:
var args = Array(15).fill(0);
args.forEach((arg, i, arr) => arr[i] = Math.floor(Math.random()*50));
如果要將args
傳遞給函式Math.max()
執行,通常可以這麼做:
Math.max(...args);
這相當於:
Math.max.call(null, ...args);
此外你還可以這麼做:
Math.max.apply(null, args);
這三種作法都可以得到相同結果:
Math.max(...args) === Math.max.call(null, ...args); //true
Math.max(...args) === Math.max.apply(null, args); // true
除此之外,因為Math.max()
的處理特性,恰好可以使用函式型開發方式中reduce
的概念,也確實可以使用args.reduce()
去得到與上面相同的結果:
Math.max(...args) === args.reduce((m, c) => Math.max(m, c))
接下來也會談到一些Array.prototype.reduce()
的事情。
你可能不知道Array.prototype.forEach()沒跟你說的事情
Array.prototype.forEach()
的用法
自知道Array
有forEach
的方法後,我自己是還蠻愛用的。
var names = ["World", "Bob", "Alice"]
names.forEach(name => console.log(`Hello, ${name}`))
並且與其他多數Array
支援的callback方法一樣,有多個很有效的參數:
var names = ["World", "Bob", "Alice"]
names.forEach((name, idx, arr) => {
console.log(`Hello, ${name}`)
arr[idx] += "."
})
console.log(names); // ["World.", "Bob.", "Alice."]
我們甚至可以用而外的thisArg
來處理某些事情:
var obj = {
"World": undefined,
"Bob": undefined
};
function checkHello(name) {
if (name in this)
return void (this[name] = "Yes");
return void (this[name] = "No");
}
var names = ["World", "Bob", "Alice"];
names.forEach(checkHello, obj);
console.table(obj);
結果:
name | result |
---|---|
World | Yes |
Bob | Yes |
Alice | No |
關於性能
在通常情況下,不會由瀏覽器處理大量的資料。通常而言forEach()
的需要時間基本沒有什麼差別:
你可能不知道的Call Stack
前言
Call Stack,中文「呼叫堆疊」,是一個很重要的概念。這並不是Web相關技術中特有的,不過為了解釋後續的內容,我決定安插一節說一下Call Stack的概念。
Call Stack
Stack 是一個先進後出(First-In-Last-Out / FILO)的資料結構。就像一本本書疊起來,然後只能一本本從最上面開始拿下來。
Call Stack 就是每次函式呼叫,都會將函數的環境狀態保存進Stack,函數的環境狀態通常叫做「Call Frame」,而儲存Call Frame的Stack,就是Call Stack。
最明顯的例子就是遞歸函式,比如:
function sum(accum, end) {
if (end === 0)
return accum;
return sum(accum + end, end -1);
}
sum(0, 5);
sum()
是將 0 到 end
之間的整數累加起來,且end
必須是大於等於0的整數。
當呼叫sum(0, 5)
的時候,Call Stack便會儲存這筆資訊
你可能不知道的即時更新方案:multipart/x-mixed-replace
multipart/x-mixed-replace
除了Polling、Long Polling、Server Send Event(SSE)和WebSocket以外,還可以透過multipart/x-mixed-replace
來更新資料。
multipart/x-mixed-replace
和Server Send Event(SSE)一樣,只能夠由Server單向傳送資料給瀏覽器。
不同的是它可能不能使用JavaScript處理更新的資料,但現在主流瀏覽器多數還是支援其中部分特性,這使得從前端部分實現非常簡單。
不再支持 XMLHttpRequest 中的 multipart 属性和 multipart/x-mixed-replace 响应。这是一个 Gecko 独有的特性,从来没被标准化过。你可以使用Server-Sent Events, Web Sockets (en-US)或者在 progress 事件中查看 responseText 属性的变化来实现同样的效果。1
Lab
資源
這次實驗會透過不斷讀取不同圖片,讓瀏覽器上不斷更新圖片內容。首先是圖片資源:
這些圖片資源名稱是: 1.jpg
、2.jpg
、3.jpg
。後續會輪流讀取回傳給瀏覽器。
前端畫面
<!-- index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>即時更新內容 - multipart/x-mixed-replace</title>
</head>
<body>
<img alt="" src="/fake_video"/>
</body>
</html>
基本上這次連JavaScript都不需要寫,只要載入一張圖片資源即可。
你可能不知道的即時更新方案:WebSocket
WebSocket
WebSocket進一步解決了Long Polling會遇到的兩個問題:
- 取得Response後,需要在建立一次Reqeust。
- 僅能夠單向傳輸更新資訊。
不過WebSocket並不是超文本傳輸協定(HyperText Transfer Protocol,HTTP),但確實由HTTP開始的。因此首先是在瀏覽器發起Request之後,要進行協議的切換。Server會回傳切換資訊。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: gx+UXBfX3qJcxachxkN/n8/3+WQ=
Sec-WebSocket-Extensions: permessage-deflate
Date: Sun, 04 Sep 2022 10:36:52 GMT
在這之後就可以進行雙向傳輸,當然以可以用於更新畫面資料。
優點
- 雙向傳輸
- 連線可以重複使用
缺點
實現複雜。不是所有瀏覽器都支援,不過現在主流瀏覽器基本支援。對於伺服器也有一定要求,在我經驗上許多免費服務器是無法使用相關技術的。
Lab
你可能不知道的即時更新方案:Server Send Event
Server Send Event
Server Send Event(SSE)解決了Long Polling會需要建立多次Request的問題。相比起Long Polling「取得Response後,需要在建立一次Reqeust」。Server Send Event在同一次HTTP連線中,由Server送出多次更新資料。
優點
連線可重複使用。
相比起Long Polling「取得Response後,需要在建立一次Reqeust」。Server Send Event在同一次HTTP連線中,由Server送出多次更新資料。
缺點
僅能夠由Server傳送訊息到瀏覽器的單向傳輸。
Lab
前端頁面
<!-- www-data/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>即時更新內容 - Sever Send Event</title>
</head>
<body>
<h1 id="content"></h1>
</body>
<script defer type="module">
const DEFAULT_TIMEOUT = 30000 /*ms*/;
const HEATBEAT_INTVAL = 5000 /*ms*/;
const contentEl = document.querySelector('#content');
let evtSource = new EventSource("/connect");
/* recive default message
evtSource.addEventListener('message', async (event) => {
console.log(`recive message: ${event.data}`);
})
*/
/* recive special event - update */
evtSource.addEventListener('update', (event) => {
contentEl.innerText = event.data;
});
</script>
</html>
前端頁面實現也算是簡單的,透過EventSource()
建立Server Send Event來源的連線。透過監聽message
或<event>
來取得更新資訊。
後端API
你可能不知道的即時更新方案:Long Polling
Long Polling
Polling 有兩個明顯的缺點:
- 就算資料沒有更新,也會有一次 Request / Response 的來回。
- 資料有了更新,也需要等待下一次的Request才會知道。
Long Polling解決了這兩個問題。
雖然行為概觀上與Polling一樣,是一次Request後得到Response,才再發出一個Request。但是Reponse的回傳可能會拉的老長,過了許久才回應,像彗星(Comet)一樣。也確實有一種方式就叫做彗星(Comet),Long Polling是他的變種1。
在Long Polling模式下,有一個長期連線,這個連線在資料一更新時,變會回傳Response,並結束此輪連線,然後再發起一次長連線請求,以做到即時更新的效果。
有一段時間在Facebook、Plurk可以見得此種方式。甚至現在還在Facebook、Plurk還是可以見得一些Request長時間沒有Response,不過我無法確定是否同為Long Polling。
下圖是Plurk的部份網路請求節圖。其中可以看到一個/connect
相關的請求,並過一段時間後才返回Response,隨後又建立一比連線:
你可能不知道的即時更新方案:Polling
靜態網頁 & 動態網頁
全球資訊網路(World Wide Web, WWW)最早用於學術研究機構,用於分享研究報告成果。起初常見型態為:資訊分享者自行建立Web伺服器,提供HTML靜態頁面,讓資訊受者透過瀏覽器取得資訊。
英國科學家提姆·柏內茲-李於1989年發明了全球資訊網。1990年他在瑞士CERN的工作期間編寫了第一個網頁瀏覽器。網頁瀏覽器於1991年1月向其他研究機構發行,並於同年8月向公眾開放。1
「資訊分享者自行建立Web伺服器,提供HTML靜態頁面,讓資訊受者透過瀏覽器取得資訊」這從現在來看,可能分成兩個不同維度的類型:
- Web 1.0: 資訊分享者自行建立Web伺服器。用戶只能單向被動的接受由權威內容服務提供商提供的內容,用戶大部分為內容消費者,而網站則由內容驅動2
- 靜態內容網頁。網頁內容並不會因為內容接收者的不同、時間地區的不同而有所不同。
隨後出現部落格平台、線上論壇,出現可以提供資訊內容的網站使用者。在此,網站內容不再簡單以HTMl方式儲存,更多的內容儲存於資料庫,由使用者提供,動態的提供給瀏覽器。這是 動態網頁 和 Web 2.0 。
此外伴隨者瀏覽器腳本語言與API的逐漸統一,確立了 ECMAScript / JavaScript 在瀏覽器的地位,結束了過往百家爭鳴的情況(不過現在還有一些原生支援特殊腳本語言的瀏覽器存在,但JavaScript已成主流)。 似動非動,吸引人眼球由JS、CSS建立動畫效果的網頁內容開始玲瑯滿目地出現。
1996年11月,網景正式向ECMA(歐洲電腦製造商協會)提交語言標準。1997年6月,ECMA以JavaScript語言為基礎制定了ECMAScript標準規範ECMA-262。JavaScript成為了ECMAScript最著名的實現之一。除此之外,ActionScript和JScript也都是ECMAScript規範的實作語言。3
其中也還有另一個令人興奮、今天廣泛使用的技術出現: Ajax(Asynchronous JavaScript and XML)。
在現今,Ajax技術主要以XMLHttpRequest
和fetch
兩個API呈現。也可能隱藏在jQuery4、Axios等比較高層次的套件函式庫之下。
這也使得由瀏覽器處理、在前端渲染生成不同內容的畫面成為可能。儘管這不是當前唯一的技術方式,但有許多別具特色的功能、前後分離的網站都依賴者這項能力。
接著就讓我們來探索幾個取得更新資訊的模式吧!
你可能不知道URL的路徑編碼Percent-encoding
前言
語言文字事件有趣的事情,英文由26個字母反覆組合,描述各種現象。中文永字八筆,構築千萬象形文字。在電腦的世界裡,一切的一切都是由無數個0和1所表示,你所從螢幕見到的任何文字、圖片、影片都是。在文字的表示法中,有一個最早誕生的編碼方式–ASCII Code。從某種角度來說,它也是在電腦世界中最安全的表示方式。
我們介紹過如何安全的儲存JavaScript程式碼而不受到編碼影響。如何在有限的字元中,在網頁HTML表達多國語言的文字。分享過DNS與電腦是如何認識IDN。這次,要來說說URL裡關於Path字段的編碼方式。
URL格式
當我們瀏覽https://bob:bobby@www.lunatech.com:8080/file;p=1?q=2#third
1,實際上可以將這個URL拆分成好幾個部分來看。
+-------------------+---------------------+
| Part | Data |
+-------------------+---------------------+
| Scheme | https |
| User | bob |
| Password | bobby |
| Host | www.lunatech.com |
| Port | 8080 |
| Path | /file;p=1 |
| Path parameter | p=1 |
| Query | q=2 |
| Fragment | third |
+-------------------+---------------------+
https://bob:bobby@www.lunatech.com:8080/file;p=1?q=2#third
\___/ \_/ \___/ \______________/ \__/\_______/ \_/ \___/
| | | | | | \_/ | |
Scheme User Password Host Port Path | | Fragment
\_____________________________/ | Query
| Path parameter
Authority
你可能不知道隱藏在Domain裡的編碼punycode
前言
2017年我有幸取得一年免費的「.台灣」域名。自此我也花了一點時間了解什麼是國際化域名(Internationalized Domain Name,IDN)。
在今天你可能也看過不少並非由英文、數字組成的域名,如果是中文或是你的母語,或許會感到一種貼切感,有些又比羅馬拼音更好記。但你知道的嗎?這可能是隱藏在網址列的假象。
國際化域名(Internationalized Domain Name,IDN)
國際化域名(英語:Internationalized Domain Name,縮寫:IDN)又稱特殊字元域名,是指部分或完全使用特殊的文字或非拉丁字母組成的網際網路域名1
早期的頂級域名只有.com
、.gov
、.org
、.net
、.int
、.edu
、.mil
。
這些頂級域名最初指的都是美國。後來才開始有了國家級域名如.tw
。加上作用後同樣視為頂級域名如.com.tw
。
隨後也出現了.jobs
、.info
、.tv
、.cc
等等為後綴的域名。
域名的功用性,個人感覺某種程度在降低。有些域名的申請越來越容易。
在「.台灣」後綴前,似乎就已經有非英文組成的域名了。譬如:https://中文.tw/ 。
現在,除了.tw
的後綴,還可以申請.台灣
(繁體)、.台湾
(簡體)的後綴。譬如: https://中文.台灣/ 。
國際化域名(IDN)可以提供一個好記、在地化的名字,有時候或許可以提升品牌形象。
國際化域名編碼(英語:Punycode)
不過,早期的DNS其實並不認得這麼多字。實際上這部分瀏覽器事先做了轉換了,像是 https://中文.tw/ ,在我使用的Mozilla Firefox Browser還可以短暫看到https://xn--fiq228c.tw/
。是的這就是 https://中文.tw/ 編碼後的結果。透過在瀏覽器網址列輸入xn--fiq228c.tw
,同樣可以進入 https://中文.tw/ 。
你可能不知道HTML Code Number
我們都知道的 <
和 >
如果需要在HTML裡面讓瀏覽器顯示<
和>
,可不能直接寫。你應該要這樣寫:
<p>
example: <input/>
</p>
來呈現內容
example: <input/>
其中lt
表示: LESS-THAN ;而gt
表示:GREATER-THAN。這是他們的符號名稱,同時還有號碼名稱。
可以使用10進制或16進制來表示這兩個符號,因此還可以寫成:
<p>
example: <input/>
</p>
<p>
example: <input/>
</p>
關於HTML編碼這件事情
與其他程式語言的原始碼儲存方式一樣,HTML文件是以純文字方式存在的。 這也就存在一個問題:如果不告訴電腦該用什麼編碼方式解讀它,就可能解讀成亂碼。
比如以下HTML內容使用Big5儲存:
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8"/>
<title>你好,世界</title>
</head>
<body>
<h1>你好,世界</h1>
<p>怎麼跟得上臺灣</p>
</body>
</html>
你可能得到這樣的結果:
你可能不知道在JavaScript裡的萬國碼
那時、天下人的口音言語、都是一樣。
他們往東邊遷移的時候、在示拿地遇見一片平原、就住在那裏。
他們彼此商量說、來罷、我們要作甎、把甎燒透了。他們就拿甎當石頭、又拿石漆當灰泥。
他們說、來罷、我們要建造一座城、和一座塔、塔頂通天、爲要傳揚我們的名、免得我們分散在全地上。
耶和華降臨要看看世人所建造的城和塔。
耶和華說、看哪、他們成爲一樣的人民、都是一樣的言語、如今旣作起這事來、以後他們所要作的事、就沒有不成就的了。
我們下去、在那裏變亂他們的口音、使他們的言語、彼此不通。
於是耶和華使他們從那裏分散在全地上.他們就停工、不造那城了。
因爲耶和華在那裏變亂天下人的言語、使衆人分散在全地上、所以那城名叫巴別。〈就是變亂的意思〉
– 創世記11
從一封亂碼郵件開始說起
自己個人很少書寫電子郵件,收到的郵件也多數是某種活動、告知事項或廣告類信件。 有些時候會收到一些需要回信或是主動發信件的情況。絕大多數文字語言上都不是什麼問題。但就是偶爾會有一兩封信件在眼前呈現的是我看不懂得符號。
最近,我就收到過一封信件…是亂碼。
會呈現亂碼的原因,有很大程度是顯示軟體錯誤的解讀了信件內容。這種情況可能可以透過取得原始信件資料,在使用其他編輯器和解碼方式打開顯示。
以信件來說,常見的分成兩種類型:
- 純文字
- HTML文件
特別是HTML文件,會有一段以<meta/>
標記內容使用的編碼方式(charset
)。
你可能不知道void也是一個運算子
void也是一個運算子
你可能不知道void
也是一個ECMAScript的保留關鍵字 (reserved keyword),也可能不知道它也是一個 運算子。
誒黑~!
因爲這張卡的效果就是:
什麼都不給
你可以像是一元運算子那樣使用它:
var a = 1;
a = -a; // -1
a = !a; // false
a = typeof a; // boolean
a = void a; // undefined
也可以將表達式用括號包起來,像是函式呼叫:
var a = 100
a = void(a) // undefined
實際上這個行爲更像是先針對表達式
(a)
求值,在使用運算子void
。過程中不存在一般意義上的函式呼叫。
這些那些你可能不知道我不知道的Web技術細節系列(前言)
前言
「這些、那些你可能不知道、我不知道的Web技術細節」,這是個我在這短短兩年實際工作上,遇到的一些原先並不清楚,甚至不知道的Web技術。
有一些或許是新的技術規範、有一些只是很冷門的技術知識、也有一些與實際實現有關。
你可能已經知道,也可能不清楚。邀請你與我一起探索這些冷門舊技術、鮮爲人知的新技術的技術細節喔~
【用Keycloak學習身份驗證與授權】系列目錄
本系列同樣發表於iThome體人賽 - 用Keycloak學習身份驗證與授權。
本頁後面還有一些小後記喔~
- 【用Keycloak學習身份驗證與授權01】Quick Start(1)
- 【用Keycloak學習身份驗證與授權02】Quick Start(2)
- 【用Keycloak學習身份驗證與授權03】淺談身份驗證與授權(1)
- 【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
- 【用Keycloak學習身份驗證與授權05】什麼是Keycloak
- 【用Keycloak學習身份驗證與授權06】Keycloak的替代品
- 【用Keycloak學習身份驗證與授權07】什麼是OAuth
- 【用Keycloak學習身份驗證與授權08】OAuth 2
- 【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
- 【用Keycloak學習身份驗證與授權10】深入OAuth 2
【用Keycloak學習身份驗證與授權33】Device Code(4)
這次應用使用PySide
來實現界面;qrcode
來產生需要的QR Code;並使用requests
來與身份驗證與授權伺服器的API溝通。現在透過pip
進行安裝需要的packages。
pip install PySide6 requests qrcode
其實本來可以考慮用electron.js,但是基於一些考量,最後決定使用PySide。
在昨天,透過Qt Designer建立了兩個需要的使用者界面,今天來實現邏輯部分。
建立Widget
在之前所設計的ui檔案分別是:example-device-code-app.ui
和login-dialog.ui
。這部分會分別將這兩份載入到類別內使用。所以同樣來建立兩個Widgets:ExampleDeviceCodeApp
和loginDialog
。
【用Keycloak學習身份驗證與授權32】Device Code(3)
本文接續device code(2)
現在已經知道了Device Code的登入流程了,那麼實際應用起來是怎麼樣的呢? 本片來實現一個可以使用Device Code Flow登入的應用。
使用者界面設計
首先,與「快速開始」應用相同,同樣需要一個顯式使用者資訊的地方,以及登入與登出的按鈕。
是的非常簡單。但悄悄先回到RCF8628,有一部分描述使用者界面的範本。該界面建議包含:操作說明、登入連接和user_code
。
【用Keycloak學習身份驗證與授權31】Open ID Connect & Social Login(2)
Keycloak Open-Id Connect
其實除了使用GitHub等社群帳號登入外,Keycloak也可以作爲Open-Id登入的提供者(Provider)。接著需要使用Keycloak本身來實現社群帳號登入,因爲這樣子可以看到更多細節。
建立新的Realm
現在,需要一個新的帳號系統。你可以在建立一個Keycloak伺服器,或是建立一個新的Realm。不同Realm的帳號系統是獨立不相干擾的,所以這裏就先建議一個新的Realm – G00gle
。
【用Keycloak學習身份驗證與授權30】Open ID Connect & Social Login(1)
因為略過了一些JWT格式細節分析。所以這部分也有部分不會好好提到
到目前爲止,爲何不同應用可以使用同一個帳號登入,已經在說明Client解釋過。這是在相關系統的應用下,那麼…沒直接相關的系統呢?譬如:使用Google登入。像這種使用不同帳號系統登入的方式,在Keycloak分成兩種。第三方系統登入,這篇僅會說明與 OAuth / Open-Id 相關的一種。如果你使用過Firebase、Auth0等服務,或是看過使用Google、Facebook、Microsoft、GitHub帳號登入的應用,對就是這類。這種社群帳號登入(Social Login)的方式,與前幾天提到的內容相關,而且可以在Keycloak實現。
鐵人賽只會實現而已,一些細節和更多的範例並不會提到。 (雖然原本就計劃寫)
Social Login 社群帳號登入
以GitHub帳號登入
【用Keycloak學習身份驗證與授權29】JWT權杖格式介紹(1)
總覺得…直接開始說明什麼是JWT格式來著。但感覺這樣會很無聊,不如我們從已經拿到的Token來看吧!
至今爲止,除了存取權杖(access_token
)、更新權杖(refresh_token
)外,還拿到過識別權杖(id_token
)。仔細看三者,都有兩個「.
」可以將權杖分成三個部份。
這些權杖都可以透過JWT.io去解析。總之先透過Password Grant Flow取得access_token
和refresh_token
,或是透過「快速開始」應用取得id_token
。
【用Keycloak學習身份驗證與授權28】Role
在帳號系統下,除了帳號本身與帳號群組外,通常還存在一個非常重要的部分–角色(Role),更有基於角色的存取授權方式(RBAC)。
寫到有點累了,沒意外的話之後是會提到RBAC
帳號如果代表一個人,這個人可能有多個角色身份。可能是個老師、主任、校長;可能是爸媽、叔姨;可能是員工、部長、處長、老闆,且可能有一群人擁有同一種角色。角色和帳號群組有點像,但在Keycloak是兩個概念。除此之外,在Keycloak還分成兩類型角色– Realm Roles 和 Client Roles 。
建立 Realm Roles
首先,你可以建立Realm共用的角色,像是員工、老闆等等較爲通用的角色。
點選在 Realm 選單下的 Roles ,然後再點選 Add Role :
【用Keycloak學習身份驗證與授權27】User & Claim & Profile
接著來看看爲什麼更新帳號資訊,在「快速開始」會有那些變化。
這與client scope和claim有關。關於後者之後會在詳細說說,而目前就先了解一下這個現象發生的原因。
首先,在我們取得token
的時候曾申明需要的scope
爲openid profile email
。其中profile
這個scope爲這次變化的主要原因。
來到Keycloak管理選單下的 Client Scopes ,然後找到 profile 。
接著將頁籤切換到 Mappers , 你會看見一堆與 User Attribute 有關的設定。
【用Keycloak學習身份驗證與授權26】User & Group
帳號(User)
基本訊息
接著來看看與帳號有關的設定。
在之前,已經建立過一帳號–bob
。過去學習實驗,也都以bob驗證身份。接著我們要來更新一下這個帳號。
首先看一下基本訊息:
來添加一些資訊:
- Email:
bob@fake.email
- First Name:
Bob
- Last Name:
Lee
- Email Verified:
ON
此外,可以要求使用者在必須做一些事情,譬如:驗證信箱、更新密碼、更新個人資訊等。
再次登入到應用–「快速開始」,可以看到有一些訊息也有些不同了。
【用Keycloak學習身份驗證與授權24】Clients
Client與一些安全相關的設定
在OAuth架構下的Client(客戶端)可以想象成是一個一個的應用程式。到目前爲止也已經建立過幾個Client:
這些Client有著自己的規則、資源、授權方式等。
可以複寫一些Realm的設定,包含產生存取權杖的方式。像是認爲RS256
簽名不夠,需要使用到RS512
:
【用Keycloak學習身份驗證與授權23】Realm
Realm,中文或許會翻作「域」,但基本很像是程式開發上,語言層面提供的包(package
)或是命名空間(namespace
)。或者可能可以更貼切的說是工作空間(workspace)。
你可以想象就像是一個企業、部門或是其他組織。有著相同的一些規範,同事們在同樣地工作空間生活、工作。但不同的企業、部門或是其他組織,可能會有類似的規範,但兩者不互相影響。
會特別有這個概念,是因爲Keycloak是可以建立多個Realm的。也就是,在同一間公司內,不同部門都可以有自己的Realm,制定部門自己的管理規範。或是特別爲外部客戶建立一個Realm,並制定特殊規範。
不同的Realm內,有著自己的帳號系統、密碼規範政策等。利用這個特性,之後也會用來更清楚的理解Open-Id。
你也可以同樣簡單視爲一個帳號資料庫、身份驗證伺服器。特別的是在會話成立期間,可以不需要再進行一次驗證,而這部分,會在提到Client時在多做說明。
如何建立一個Realm
要建立一個Realm是非常簡單。在之前也建立過「quick-start」這個Realm。也幾乎就只需要給個名字而已。
【用Keycloak學習身份驗證與授權22】Keycloak使用基本概念:前導
【用Keycloak學習身份驗證與授權21】在Flow這段小旅途外的風景
在這一小段路中介紹了Password Flow、Implicit Flow、Code Flow、Refresh Token Flow、Client Credentials Flow、PKCE、Device Code Flow。有些模式已經被發現可能有潛在風險,有些模式無法單獨使用。這或許還不是全部,至少到現在為止都還沒有提到過金融級應用Flow–CIBA。
Client Initiated Backchannel Authentication Profile(CIBA)
本小節也不會詳細介紹CIBA(Client Initiated Backchannel Authentication)。儘管CIBA現在階段還只是草案(Draft),但在Keycloak v15版本中已經可以使用。大概也已經確實有一些應用使用。
為什麼你不該繼續使用Implicit Flow?
在談到Implicit Flow時候,提到過:
將存取權杖暴露在使用者面前也不是非常好的做法
【用Keycloak學習身份驗證與授權20】Device Code(2)
光要完成這個範例就花了幾乎整整一天
做完後決定…來拆篇這第二部份,將有部份內容會在【實戰篇】展開。 今天就先來看看成果。
成果發表
【用Keycloak學習身份驗證與授權19】Device Code(1)
+----------+ +----------------+
| |>---(A)-- Client Identifier --->| |
| | | |
| |<---(B)-- Device Code, ---<| |
| | User Code, | |
| Device | & Verification URI | |
| Client | | |
| | [polling] | |
| |>---(E)-- Device Code --->| |
| | & Client Identifier | |
| | | Authorization |
| |<---(F)-- Access Token ---<| Server |
+----------+ (& Optional Refresh Token) | |
v | |
: | |
(C) User Code & Verification URI | |
: | |
v | |
+----------+ | |
| End User | | |
| at |<---(D)-- End user reviews --->| |
| Browser | authorization request | |
+----------+ +----------------+
Figure 1: Device Authorization Flow
The device authorization flow illustrated in Figure 1 includes the
following steps:
(A) The client requests access from the authorization server and
includes its client identifier in the request.
(B) The authorization server issues a device code and an end-user
code and provides the end-user verification URI.
(C) The client instructs the end user to use a user agent on another
device and visit the provided end-user verification URI. The
client provides the user with the end-user code to enter in
order to review the authorization request.
Device Code Flow這個與前面幾個特別不一樣。在之前,以往都是從登入開始,然後跳轉頁面回到App(Client)。也就是通常先有的是前端通訊,然後才是後端通信。
【用Keycloak學習身份驗證與授權18】PKCE
+-------------------+
| Authz Server |
+--------+ | +---------------+ |
| |--(A)- Authorization Request ---->| | |
| | + t(code_verifier), t_m | | Authorization | |
| | | | Endpoint | |
| |<-(B)---- Authorization Code -----| | |
| | | +---------------+ |
| Client | | |
| | | +---------------+ |
| |--(C)-- Access Token Request ---->| | |
| | + code_verifier | | Token | |
| | | | Endpoint | |
| |<-(D)------ Access Token ---------| | |
+--------+ | +---------------+ |
+-------------------+
Figure 2: Abstract Protocol Flow
PKCE模式
說穿了PKCE是基於Code flow的安全強化版。在整個過程前後添加了兩個動作–產生code_verifier
和code_challenge
,並在最後透過code_challenge
驗證code_verifier
。其目的有很大程度是為了建立前端通訊與後端通訊的關聯。
原先風險
那麼先來看看原本發生了什麼問題。
【用Keycloak學習身份驗證與授權17】Client Credentials
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
Figure 6: Client Credentials Flow
嘗試 Client Credentials flow
Client Credentials,這個模式有點特別。除了前面看到的它可能與其他模式並用以外,最特別的是,單純使用它,完全不需要資源擁有者參予。總之先來看看:
你可以使用RESTfer嘗試看看:
grant_type: client_credentials
client_id: oauth_tools
client_secret: <之前所產生的secret>
或是同樣可以透過OAuth.Tools嘗試看看。
【用Keycloak學習身份驗證與授權16】Refresh Token
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
Figure 2: Refreshing an Expired Access Token
The flow illustrated in Figure 2 includes the following steps:
(A) The client requests an access token by authenticating with the
authorization server and presenting an authorization grant.
(B) The authorization server authenticates the client and validates
the authorization grant, and if valid, issues an access token
and a refresh token.
(C) The client makes a protected resource request to the resource
server by presenting the access token.
(D) The resource server validates the access token, and if valid,
serves the request.
(E) Steps (C) and (D) repeat until the access token expires. If the
client knows the access token expired, it skips to step (G);
otherwise, it makes another protected resource request.
(F) Since the access token is invalid, the resource server returns
an invalid token error.
使用refresh_token
取得access_token
接著是使用Refresh Token換取Access Token的流程。這大概是所有中最簡單的一個模式之一了。
但因爲先決條件是取得可用的 Refresh Token ,所以無法單獨存在。在RCF6749相關的流程圖中,關注的是G、H的部分。
至於一開始有什麼方式取得Refresh Token就非常的多。在已經介紹的密碼模式和code模式都有可能返回refresh_token
。
【用Keycloak學習身份驗證與授權15】Authorization Code
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Note: The lines illustrating steps (A), (B), and (C) are broken into
two parts as they pass through the user-agent.
Figure 3: Authorization Code Flow
Authorization Code是在 RFC6749第一個提到的流程,所以有時又被視爲 標準流程(Standard Flow) 。
它與前兩個流程很不一樣,分成 前端通訊(frontchannel) 和 後端通訊(Backchannel) 。不過,其實反倒是前兩個是所有模式裡的怪胎,在隱含模式下,後端通信並在前端通訊;在密碼模式下,根本不存在前端通信,資源擁有者需要高度信任客戶端(說穿了在前端通信下,資源擁有者也是高度信賴瀏覽器或代理(User-Agent))。
【用Keycloak學習身份驗證與授權14】Implicit (Legacy)
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
Note: The lines illustrating steps (A) and (B) are broken into two
parts as they pass through the user-agent.
Figure 4: Implicit Grant Flow
如果說password適用於原生應用環境(Native Application)下的話,接著就是適用於純前端環境。 在現在前後分離架構的情況,前端與後端連接並不緊密,甚至前端幾乎就可以視爲一個完整的應用。 因此將前端視爲授權框架下的「客戶端(Client)」也就不會太難理解。
【用Keycloak學習身份驗證與授權13】Password Grant (Legacy)
首先,先來看看直接使用帳號密碼授權的。
是的, OAuth 是有一個模式支援直接使用帳號密碼的。 與萬能鑰匙不太一樣的是,授權的結果仍然是由授權伺服器的權杖和資源伺服器決定。 儘管透過中央授權控制可以限制存取權杖可以做些什麼,但畢竟直接使用帳號密碼並不是特別好, 故在其他模式下都不適用時,才應該再考慮此模式。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Figure 5: Resource Owner Password Credentials Flow
事前準備
安裝RESTer
【用Keycloak學習身份驗證與授權12】Flows這一小段路上路前注意事項
其實我原本是想要 RESTer 幹到底的哈😜。
今天有一點是插話的。考慮到接下來幾天的內容,所使用到的工具會有點多樣,所以行前做個提醒。
首先,你最好了解:
- HTTP Request / Response
- HTTP API (Web API)
- JSON
- BASE64
諾對於Postman這類工具有所熟悉再好不過。但接者幾天會使用:
- RESTer
- curl
有一些情況會直接使用。 - python
主要用於格式化JSON。
除此之外,如果熟悉Bash
的話同樣也有助於理解所有內容。此外還有可能會使用到 OAuth Tools 、 jwt.io 。(JWT的部分更有可能出現在之後關於Open-Id內容前後)
但其實,以上並非全部都是必須。最重要的是希望你能夠學習到OAuth本身的部分。
【用Keycloak學習身份驗證與授權11】OAuth 2
終於要來談談OAuth裡定義的細節了~
目前OAuth 2.0 一共定義了7種流程(flow)。在未來本系列可能稱之爲模式,不同模式適用於不同情況、不同環境。 就是因爲如此,OAuth才有高彈性的優勢。
OAuth 2.0 的可擴展性和模塊化是其最大的優勢之一,因為這使得該協議適用於各種環境。然而,正是這種靈活性導致不同的實現之間存在基本的相容性問題。當開發人員想在不同的系統上實現 OAuth 時,它提供的眾多自定義選項容易使人困惑。
本系列會介紹的模式包含:
- Password Grant (密碼模式)
- Implicit (隱含模式)
- Authorization Code (Code模式)
- Refresh Token
- Client Credentials (特殊密碼模式)
- PKCE
- Device Code
儘管 Implicit 和 Password Grant 被標記爲傳奇的(Legacy),但有時候仍然可能會使用到。重要的是你應該知道什麼情況應該使用什麼模式。同時記住,即使一個系統按照規範正確地實現了 OAuth,也不意味著該系統在實踐中就是安全的。
「OAuth 2.0 實戰」有一章決策圖可以幫助你決定使用什麼模式。但本系列應該不會提供。
【用Keycloak學習身份驗證與授權10】深入OAuth 2
喔不,其實今天還不會真正提到OAuth 2.0的深度內容。今天要來談談的是取得資源的細節。
使用帳號密碼,假裝自己是用戶
首先先試著想想看,如果你想要寫一支程式代替你處理某些事情。譬如:收信、發信。 更詳細的說,你寫了一個信件的客戶端(如:Thunderbird、Outlook)。 然後你會需要告訴這支程式你信箱的登入帳號密碼,由他去代替你收信、寄信。這個樣子就像是你把你所有的祕密都交給了它, 交給了它那把萬能鑰匙,而你完全信任這支程式。
其實這種狀況還真不少見。尤其在於你所申請的帳號,和使用的客戶端服務實際就是同一個時,這種行爲在正常不過。
但當它們是不同服務時,就可能出現問題了。你還能信任你提交的密碼不會被誤用嗎?不可能發生?
你可能有Gmail的帳號,你會很正常的使用Gmail的服務。但你知道Gmail除了自己本身外,它還可以幫你收其他信箱嗎?
比如說你還有ymail的帳號,但你更喜歡Gmail的界面,所以你希望使用Gmail來處理yamil的信件。這時候其實你就是告訴了Gmail 關於yamil的帳號密碼。相對的,也就是你應該是信任的Google的服務。
【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
再談身份驗證與授權
現在,讓我們再一次把視線放到「身份驗證」和「存取控制」這些名詞身上。 在入門篇的「淺談身份驗證與授權」已經相當程度的解釋過各個名詞。 不過今天將要更關注在身份驗證與存取控制的細節上。
對於一個應用來說,最重要的是它的 業務邏輯 。 除了業務邏輯本身,為完成所需的工作,會需要取得必要之資源。這可能是一份檔案, 鏡頭、麥克風資源等不同種形式。
在 取得資源 過程中,也會有另外一層業務邏輯,也可能本身就是另一隻程式服務,對所需取得的資源,進行 存取控制 。
最後,爲了判斷是否具有存取該項資源的權限,有可能有必要進行 身份驗證或授權 。
【用Keycloak學習身份驗證與授權08】OAuth 2
這是入門篇的最後一天了,今天不會寫什麼內容,但來帶大家看個入門概念可用的工具 – OAuth 2.0 Playground。
OAuth 2.0定義了幾個flow,可用於不同情境下,由於後續會有更多詳細說明,所以今天只會帶大家初步認識,嚐鮮看看。
註冊帳號
點選 register a client and a user。別擔心,這是個隨時可以廢棄的帳號。你完全不用真的去記他,他也不會要求你提供什麼資訊。
然後你會得到一組帳號密碼。然後點選「open in new window」之後就可以按下「continue」。
【用Keycloak學習身份驗證與授權07】什麼是OAuth
先來回憶一下,何爲「授權」。試想像有一座宅邸,裏頭有無數房間。而你作爲這座宅邸的管家,擁有一把萬能鑰匙,可以開始宅邸內所有門扉。 此外,這把萬能鑰匙還有一個作用,就是產生出開啓特定門扉的鑰匙。 你可以產生出的鑰匙交給其他人,其他人就可以自由進出特定房間。這個動作就是「授權」。
OAuth 是一個開放標準的 授權協議 ,它允許 軟體應用 代表 資源擁有者 訪問資源擁有者的 資源 1。
OAuth是什麼?
【用Keycloak學習身份驗證與授權05】什麼是Keycloak
終於要來好好介紹一下甚麼是Keycloak了~
收先先來看一下Keycloak的基本資訊:
- 名稱: Keycloak
- 開發使用的程式語言: Java
- 公用: 單點登入驗證與授權工具
- 許可協議: Apache License 2.0
- 公開倉庫: https://github.com/keycloak/keycloak
- 官方網站: https://www.keycloak.org
- 撰寫當下最新版本: 15.0.2 (2021年8月20日)
在 快速開始 提到過起始畫面有一些細節:
【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
實際上,在昨天已經將多數基礎都已經解釋過了,不過我想到還有一些東西可以再多做補充的。
對啦! 擔心彈藥不足,把一篇拆成兩篇來啦!👻
沒有身份識別的存取控制
在我們拆分的整個流程中分成:身份識別、身份驗證、授權、存取控制。但現在,你將Web App登出後再登入一次,你會發現「授權」的部分不見了! 但我們不會立刻來討論這個部分。先來說說身份識別。
不覺得,身份識別在整個流程之中非常雞肋嗎?也就只是將你這個「自然人」與系統中存在的「帳號」對應起來。 也確實如此,在這樣的拆分中,身份識別對於存取控制並不是必要的。在後來已MAC爲基礎發展的存取控制框架,也多不直接與帳號相關。
別擔心,之後會提到什麼是MAC(強制存取控制, Mandatory Access Control)。
不過還有一個更直接沒了這個流程的例子。在以「單人使用」作爲設計的系統之中,我們只需要拿到鑰匙就可以進行存取。
什麼?你說現在還有這種系統嗎?其實還真不少呢,加密上鎖過的壓縮檔案,上鎖的部落格文章。還有授權之後的流程,可能也不包含身份識別。
【用Keycloak學習身份驗證與授權03】淺談身份驗證與授權(1)
在「快速開始」的單元中,實際上已經完成了所有身份識別、身份驗證、授權和一些存取控制的流程。
在今天,將會來說說,這些看是相同,卻又有一些不同的名詞。
名詞認識
那麼首先,就先來認識各個名詞的英文吧!
名詞 | 英文 | 簡短說明 |
---|---|---|
身份識別 | Identification | 讓系統知道你是誰 |
身份驗證 | Authentication | 讓系統相信你是誰 |
授權 | Authorization | 允許他人存取某項資源 |
存取控制 | Access Control | 檢驗是否有資格存取某項資源 |
而我認爲軟體開發上,最重要的是 存取控制 。這直接關係到系統上的資源,但卻無法只存在存取控制,勢必需要身份驗證與授權的配合。這也是爲什麼我們很難將這些分開來看。
已「快速開始」爲例
現在,讓我們把視線重新放回剛弄好的「快速開始」。在這個範例中,已經多少碰觸到了每一塊的概念。首先是身份識別:
【用Keycloak學習身份驗證與授權02】Quick Start(2)
昨天,已經完成了一部分配置,且也已經可以建立帳號並登入了。
不過,這只能算是半套,而今天要在來完成另外半套。
你可以按照昨天的做法,重新建立一個新的Client。
只是注意在建立的時候,「Root URL」改爲: http://localhost:4200
。
今天,我們要自己實現一個前端網頁去的Web App,然後綁定Keycloak去做登入。
前置要求:
- 用Keycloak建立一個Client
- 網頁開發基礎知識(HTML/CSS/JavaScript)
- TypeScript的部分知識
- Angular知識(非必要)
調整Keycloak的Client配置
前面說過,Keycloak的Client實際上並不是真正的Client Application,只是做了一些關聯。 今天就要來 快速開始 個自己的Web App。而首先,需要先調整Client的關聯:
- 選擇「Clients」
- 找到昨天建立的「my-quick-start-app」,然後點選「Edit」
這此調整主要做兩個修改:
【用Keycloak學習身份驗證與授權01】Quick Start(1)
開始之前~2🎃。開完笑的~
但是想了許久,總覺的就這麼直接開始解釋各個名詞不太好。
想找個範例又有諸多擔心。
不如…先來快速開始做個範例!
快速開始將分成兩天。 今天會先跑過一次簡單的流程,明天才會寫一點程式。
這兩天看完後,依照需求,你甚至可以開始開發自己的應用。
那我們從Keycloak開始吧!
今天的前置需求:
- 只要裝好docker就好囉~
- 阿!對,你還要安裝個瀏覽器。
(不過你拿什麼在看本系列文章呢?)
透過Docker建立一個Keycloak應用
docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:15.0.2
這麼一條指令就可以開始這系列多數內容了(吧)。現在Keycloak會聆聽本機的8080 port。嘗試用瀏覽器開啓 http://localhost:8080 後,你應該會看到以下畫面:
【用Keycloak學習身份驗證與授權00】開始之前
這系列文章將帶大家探討軟體開發上,那些身份驗證與授權的相關議題。此外的話題還有身份識別、存取控制。 以目前諸多流行應用都以非單人使用的狀況之下,身份驗證與授權,幾乎是每位開發者都會遇到的題目。 不管你是串接OAuth、管理資源、寫後臺界面,甚至在最初應用的設計,幾乎都會扯上邊。 在業務邏輯之外,這或許會是相當重要的一部分。
關於身份驗證與授權,每一個部分都非常重要,也都可以分開來看,卻也非常難分開來看。
Because these three techniques are so closely related in most real applications, it is difficult to talk about them separate from one another. In particular, authentication and authorization are, in most actual implementations, inextricable. 1
就如同在Oracle上可查詢到的相關資料,這些部分儘管代表者不同概念,但彼此非常相關,實在很難分開來看。雖然如此,每一個部分都是非常龐大的內容,而本系列將會着重於授權控制與存取控制。在此之上會在探討近來已經非常普遍的OAuth 2.0、Open-Id、單點登入(SSO)和基於角色的存取控制(RBAC)
【30天Lua重拾筆記】系列目錄
本系列文章為 第12屆iT邦幫忙鐵人賽 參賽文章。
你可以同時於iT邦幫忙找到本系列目錄。
最全面的Lua入門學習…筆記草稿?No, No, No, No, No 在30天要所有東西提到貌似是不太可能了,但這將會是一個由淺入深的Lua參考筆記。會竟可能涵蓋所有Lua相關核心內容。
Lua非常小,有經驗的人甚至可以在幾小時內熟悉Lua核心基礎內容、幾周內使用進階功能。並且透過輕而小的Lua,或許可以從另一角度重視其他程式語言。是C、Lisp以外,我最為推薦學習的程式語言之一。
本系列包含內容:認識Lua、基礎型別、控制流程、進階概念、範例嵌入C/Java。從頭帶你了解Lua怎麼回事。
系列目錄
【30天Lua重拾筆記35】完賽感想與延伸閱讀
完賽感言
這系列文章在我3月當兵時就開始在規劃了,可是寫出來也還是和原本預計的差了蠻多的,看看我一開始預計撰寫的內容…
起初,我更是想說Lua這麼小,那應該可以非常完整的說明完所有部份吧!但越後面開始規劃每日的文章才發現…絕對會超過30篇。
而且更多時候是寫到一半,發現這邊不得不提到一下之後才會說明的部份。又或者是邊查資料邊寫,結果與預計寫的方向差了不少。又有些時候,因為對Lua已經有一些基礎了解,手打字追不上想寫的思路,或是大思路雖然有了,卻一直在某些地方腦袋打結…
這系列文章,有很多篇也比我預計寫的內容詳細了許多,比起最初可能只是各方面都提到一點點的筆記,儼然已經變成超過我預期內容的一些探討。自己撰寫收穫也頗多的,也還好目前並不算忙碌,才可以這樣做。若要給未來自己一些建議:
- 不管想到的是否完整、好壞,都可以先寫下來。
這次有許多已經先寫下來的小紀錄,最後都變成很常的文章或範例程式。有更多更是在其他文章撰寫下,也不停的修改。 - 不用多,先寫下小小的東西就好。當做種下一顆未來的種子,當未來有需要使用時,再來灌溉、發芽。
- 還是自己寫過、想過的東西,在未來更容以理解。
最後,感謝閱讀完本系列的文章的各位,本文章原本想要深入淺出,但中有諸多未能提及之處,只能在最後分享一些資源。
延伸閱讀
【30天Lua重拾筆記34】番外篇: Fengari - 一個JS實現的Lua,運行Lua在瀏覽器內吧!
幾年前關注過Moonshine和lua.vm.js,不過這兩個項目貌似沒什麼在更新了。Fengari這個這次到又是讓我為之一亮
Lua的實現真蠻多樣的,光是想讓Lua運行在瀏覽器就有不少,像是Moonshine、lua.vm.js、Starlight。有些使用JS;也有些利用了WASM、emscripten。Fengari是屬於前者的實現,是JS實現的版本。其除了可以在瀏覽器執行外,也提供了基於Node.js的執行器。這是一個蠻新興的項目,整體設計粗淺看來也相當不錯而且完整,今天會略微介紹一下,但建議可以先閱讀閱讀為什麼我們使用JS重寫了Lua?(英文)
Fengari是希臘文「月亮」的意思
於瀏覽器執行Lua
【30天Lua重拾筆記33】Java + Lua計算機
這是我前幾年作為學習/練習的例子。
看過與C交互後,接著來看看一個更實際應用的例子。不過不用C,來用Java。
為甚麼呢?Java自帶一個跨平台的視窗開發套組,本身也有豐富的函式庫可用,第三方函式庫也眾多,作為宿主語言是蠻好的標的。不過不直接使用原本的Lua,需要使用LuaJ。
其實最初只是因為Android App使用Java開發。而當時Android Studio編譯APK實在太慢,才會有為啥不能先用其他方式寫邏輯、開發,為啥不先嵌入一個腳本語言?才去做的一個練習,之後我使用的套件也確實有用於Android App開發上。
是的,如果你是正在開發Android,不管是以前主要用Java還是後來的Kotlin,今天應該都還是可以使用LuaJ。
LuaJ已經停滯好一段時間了,本次範例的為GitHub上另一個分支。基本上遵循Lua 5.2的規則,也就是本系列有部份是Lua 5.3、5.4的語法並不能使用於之。
目標
本次範例主要目標:一個含有GUI的簡易計算機。其包含:
- Java提供GUI界面,和部份功能。
- Lua處理邏輯。
- 一個擴展方法,能夠改變成品行為。
使用Lua撰寫邏輯
使用Lua的好處之一,在不確定要以和總平台開發前,是可以先使Lua撰寫邏輯,最後在嵌入到某個平台或框架。
【30天Lua重拾筆記32】進階議題: LuaRocks & LuaDist
LuaRocks
LuaRocks是類似npm、pip這樣的套件管理工具,你可以在上頭找到近4000個別人已經寫好的模組。
下載/安裝LuaRocks
在示例前,你需要先下載安裝好LuaRocks,若要下載其他版本,可以在這個頁面尋找看看。
在Linux上,你只需要將其解壓縮於可執行路徑底下即可。Windows可以參考官方說明。
使用LuaRocks
初始化環境
你可以不用做這一步。略過這一不的話,之後安裝得套件會存在於全局裡。有這樣的作法,主要是用於開發其他套件時使用。
mkdir example
cd example
luarocks init
搜尋套件
【30天Lua重拾筆記31】進階議題: 記憶體回收&弱表
TL;DR:
不要去修改預設值,除非你知道在做什麼
Lua會自己做記憶體回收,絕大多數時候不必為記憶體分配、管理而操心,而且通常它做的很好。但如果真的因為記憶體回收而影響到程式效率的執行,且你確定你有足夠的記憶體,你可以暫停讓Lua執行記憶體回收的操作:
停止收集記憶體垃圾
collectgarbage("stop")
collectgarbage("isrunning") --> false
或者,如果你是嵌入在C,可能會更傾向使用:
lua_gc(L, LUA_GCSTOP)
收集記憶體垃圾
如果要恢復,也只要:
collectgarbage("restart")
collectgarbage("isrunning") --> true
或是
【30天Lua重拾筆記30】進階議題: 與C交互(+Python)
Hello, Lua & C
現在,我們來嘗試從C去執行一個Lua程式,Lua程式就用最簡單的Hello,並命名為hello.lua
print "Hello"
然後來寫C程式 – hello_C.c
。
引入標頭檔
需要下載含有標頭檔和函式庫的版本
#include "lua.h"
#include "lauxlib.h"
建立Lua虛擬機
// new a lua VM
lua_State *L = luaL_newstate();
打開預設的所有函式庫
通常而言,不會全部開啟所有功能。
這只是範例,讓hello.lua
檔案擁有所有能力。
// open all libraries
luaL_openlibs(L);
執行Lua檔案
【30天Lua重拾筆記29】進階議題: 物件導向程式設計
與ECMAScript相同,採用原形設計的物件導向。你可以與7天搞懂JS進階議題相互服用,可能會有意想不到的效果。
模擬封裝
Lua並沒有直接保護內部資料的方法,你可能會需要使用 閉包 、 弱表 、 metatable 等來達成條件。
但今天只討論封裝裡最簡單的概念–集成,也是物件的基礎:將相關的物件、方法關連到一個結構裡面。沒錯,就是 table
,在本文中使用物件(object)一詞基本可視為同義。
Lua的
table
也確實很像是JS裡的Object
,但可能更像是Map
(一個ES6後出現的新類別)。
最基礎的資料結構概念是雜湊表。
建立物件1
建立物件(object
)就是建立表(table
),非常簡單:
coco = {
name = "桐生ココ",
age = 3501,
birth = {month = 6, day = 17},
slogan = "good morning mother f**ker",
say = function (self)
print(self.name .. ": " .. self.slogan)
end
}
coco:say() --> 桐生ココ: good morning mother f**ker
工廠模式
但我們總不能每次都這樣直接建立物件,或許可以由一個加工工廠(函數)來處理?
【30天Lua重拾筆記28】進階議題: Meta Programming
Meta Programming / 元程式設計
元程式設計(英語:Metaprogramming),又譯超程式設計,是指某類電腦程式的編寫,這類電腦程式編寫或者操縱其它程式(或者自身)作為它們的資料,或者在執行時完成部分本應在編譯時完成的工作。多數情況下,與手工編寫全部代碼相比,程式設計師可以獲得更高的工作效率,或者給與程式更大的靈活度去處理新的情形而無需重新編譯。 – 維基百科
簡單說,元程式設計,就是讓「程式能夠編寫程式」,改變程式運行的部份行為。Lua本身具有部份如此的能力,舉例來說,如果想要建立一組變數A-Z
,或許正常會這樣子寫:
A = 1
B = 2
C = 3
-- ....
Z = 26
但Lua可以有更聰明(hacking)的寫法:
for i=65, 65+25 do -- ASCII Code of A is 65
print(string.char(i))
_ENV[string.char(i)] = i - 64 -- 1 to 26
end
還記得_ENV的作用嗎?
【30天Lua重拾筆記27】進階議題: debug
Lua本身並沒有獨立的debugger相關工具,但他有一個強大的內置套件— debug。
打印調錯訊息traceback
debug = require "debug"
do
local ten = 0
function div10(n)
print(debug.traceback()) -- 打印調錯訊息
return n / ten
end
end
更新變數值
透過直接觀看函式內容,可以知道這個函式div10
並不符合預期。
do
local ten = 0
function div10(n)
return n / ten
end
end
所以需要改變ten
值,但這次不直接修改程式原始碼。我們得先了解怎麼取得閉包變數ten
:
【30天Lua重拾筆記26】進階議題: 錯誤處理
作為一個寄宿型的嵌入式語言,Lua設計更傾向由宿主語言(通常是C)處理錯誤。
但是可以在保護模式下,執行函式,並檢查函式是否執行成功。
很像是Go語言。這就是Lua的錯誤處理基本方法。
錯誤處理
作為一個寄宿型的嵌入式語言,Lua設計更傾向由宿主語言(通常是C)處理錯誤。
num = 10
str = "string"
print('Hello, Lua')
result = num / str -- Error: attempt to div a 'number' with a 'string'
print('here will not show, because error hapend before')
一般來說,並不傾向於Lua處理最後,因為數字與字串相除,所引發的錯誤。這個錯誤會持續引發至宿主語言,由宿主語言進行錯誤處裡。
【30天Lua重拾筆記25】進階議題: 模組化
Lua並沒有完整的模組系統,更多的是依賴模組開發者的設計。在Lua 5.1曾經有module()
的函數可用,但於Lua 5.2已經被移除。更多的需要使用_G
、_ENV
來使用,相關說明可以參考全局表與環境表。
module (name [, ...])
在require()
之前,需要先說一下如何載入程式檔案並執行。
載入檔案
對於模組化設計,最重要的一點是如何切割程式為不同檔案,接著就是如何串連不同檔案。在之前介紹過load
,Lua提供了另一個類似的方法loadfile
。
示例
首先先建立個 hello.lua 檔案,簡單就好:
print "Hello, World"
然後才是主程式 loadfile_hello_example.lua :
hello = loadfile("./hello.lua")
hello() --> Output: Hello, World
如果你分別於REPL環境打入這兩行指令,你會發現第一行指令並沒有任何輸出結果,只得到一個hello函數。只有在第二行執行函式時,才真正執行hello.lua程式檔案的內容。
執行檔案
【30天Lua重拾筆記24】中級議題: coroutine
coroutine
Lua提供coroutine
的函式庫,使其有能力編寫不同模式的程式。
thread create
你可以透過coroutine.create()
建立一個thread
。
t1 = coroutine.create(function() print("Hello, World") end)
print(type(t1)) -- Output: thread
Lua並不是多執行緒的,其thread
是輕量的。儘管Lua本身沒有異步(async)的寫法,但可以創造出異步的寫法。相對而言,值執行順序是明確許多的。
thread running
可以透過coroutine.resume()
去觸發一個thread
的執行:
coroutine.resume(t1) -- Output: Hello, World
傳入參數
可以對一個剛建立好的thread
傳入參數。
function hello(name)
while true do
coroutine.yield("Hello, " .. name)
end
end
t1 = coroutine.create(hello)
print(coroutine.status(t1)) -- suspended
print(coroutine.resume(t1, "Bob")) -- Output: Hello, Bob
print(coroutine.status(t1)) -- suspended
print(coroutine.resume(t1, "World")) -- Output: Hello, Bob
thread close
【30天Lua重拾筆記23】中級議題: 閉包
變數的查找
對於一個變數,Lua會先嘗試從當前詞法環境(Lexical)尋找,再從當前環境中尋找(
_ENV
)。
那的對於區塊變數呢??
function parentFunction()
local L1 = 100
local function childFunction()
print("here is child")
end
return childFunction
end
f1 = parentFunction(100)
f1() -- Output: here is child
上例中,區塊變數L1
沒有任何人可以存取的到,簡直消失在了時空夾縫之中。
詞法環境(Lexical)
詞法環境(Lexical)指的是程式編寫當下,執行區塊由內而外,可以見到的範圍。
隱藏區塊變數
在Lua變數預設值為nil
。透過查找規則,可以暫時隱藏一切區塊變數。
【30天Lua重拾筆記22】中級議題: 全局表(_G)、環境表(_ENV)
_G
和_ENV
在Lua有兩個特殊變量–_G
和_ENV
,其分別表示全局環境和當前環境。_G
在與C交互時,另有作用。但大致上你可以將兩者視為相同。實際上,在Lua環境建立之初,兩著也確實是相同的:
print(_G == _ENV) -- => Output: true
先前說過,Lua只有表(table
)這個複合結構。而_G
和_ENV
也是table
結構。_ENV
中包含一個_G
的key
,其值指向_G
,而起出_G
就是_ENV
。像是一個咬尾蛇(Ouroboros)
print(_ENV._G == _ENV) -- => Output: true
print(_G == _ENV) -- => Output: true
print(_G._G == _ENV) -- => Output: true
print(_G._G == _G) -- => Output: true
變數查找
對於一個變數,Lua會先嘗試從當前詞法環境(Lexical)尋找,在從當前環境中尋找(_ENV
)。這意味著你可以準備一個乾淨的執行環境執行函數。
【30天Lua重拾筆記21】基礎3: 再看pairs, ipairs
ipairs()
的行為
iparis會嘗試從索引1開始迭代表(陣列),直到其值為nil
。所以很像是:
arr = {1,2,3,4,5}
function my_ipairs(t --[[table]])
local i = 1
while t[i] ~= nil do
print(i, t[i])
i = i + 1
end
end
my_ipairs(arr)
1 1
2 2
3 3
4 4
5 5
也因此其如果有斷值,會在一半中止:
arr = {1,2,nil, 4, 5}
my_ipairs(arr)
1 1
2 2
pairs()
和next()
【30天Lua重拾筆記20】基礎3: 複合結構 - table
Lua只有一個原生的複合結構 – table
。實際上陣列是table
的特例。
陣列是table
的特例
arr = {1,2,3,4}
print(type(arr)) --> table
建立表(table
)
table
對應於:
程式語言 | 型別 |
---|---|
python | dict |
js | object or Map |
這個結構幾乎是現代高階語言必備的基礎。其又稱作hash-table
,是一個鍵值對(key/value)的結構。在Lua之中,其key可以是除了nil
和NaN
(Not a Number)以外的任何型別。
key值值域
obj = {} -- 建立一個空表
obj[1] = 1 -- 整數是合法的key值
obj[1.0] = 2 -- 浮點數是合法的key值
obj["string"] = 1 -- 字串是合法的key值
obj[math.huge] = 1 -- inf是合法的key值
--[[
要注意的是 obj[1] 和 obj[1.0]相同
其obj[1]和obj[1.0]最終值為2
--]]
----------------
print(obj[nil]) --> nil
obj[nil] = 1 --> Error: nil不是合法的key值,儘管取值不會出錯
print(obj[0/0]) --> nil
print(0/0) --> -nan
obj[0/0] = 1 --> Error: NaN不是合法的key值
value值值域
其value可以是nil
以外的值。
nil表示該key不存在,亦可視為刪除該key
obj[1] = nil -- 刪除`obj[1]`
print(obj[1]) --> nil --表示obj[1]不存在
【30天Lua重拾筆記19】基礎3: 陣列從1開始
建立陣列
關於陣列,其實也已經看過了。不過其實陣列還有兩個祕密,一個今天會揭露,另一個等等明天。
要建立一個陣列很簡單,很像C語言,只是把中括號[]
改成大括號{}
,像是:
programming_language = {
"C",
"C#",
"C++",
"Java",
"Swift",
"Python",
"Haskell",
}
陣列取值
陣列從1開始
與C語言一樣,使用下標運算取陣列之中的值, 要注意的是,Lua陣列從1開始
print(programming_language[1]) -- => Output: "C"
陣列長度
可以使用長度運算#
,取得陣列長度:
print(#programming_language) -- => Output: 7
迭代陣列
知道陣列取值方式,也知道陣列長度後,就可以來迭代陣列:
一般for迴圈方法
for i=1, #programming_language, 1 do
print(i, #programming_language[i], programming_language[i])
end
看我小巧思,每個值的長度,正好與其對應的索引值相同。
for-in + ipairs() 方法
可以寫的更簡單點:
【30天Lua重拾筆記18】基礎2: 應該知道的(總集+補充)
關於變數
- 值(value)有型別;變數(varible)沒有
- 基礎型別一共有8個
nil
boolean
number
string
function
userdata
thread
table
- table是唯一的複合型別(之後會提到)
- Lua 5.4之後,可以為變數標記屬性。Lua 5.4支援
const
和close
兩種屬性 - 變數預設是全局變數(實際上與特殊變數
_ENV
和_G
有關,之後會提到) - 變數值型別會自動轉型,但不該依賴
數字
- 整數和浮點數會自動轉換
- 整數有範圍,有溢位問題
- 浮點數也有精度丟失的問題
布林
- 只有
nil
和false
為假,其他為真 0
、空字串、空表亦為真
控制結構
- 有label &
goto
- label只屬於當前函式區塊
- 使用
elseif
,而非else if
(這點我覺得C就設計的比較漂亮)- 嵌套使用
if
(else if
),記得補上end
- 嵌套使用
- 非強求,但最好做好排版
【30天Lua重拾筆記17】基礎2: pcall, xpcall, load (eval, exec, apply)
eval / load
作為一個直譯的環境,幾乎一定會有一個與eval
等價的能力,不過在Lua叫做load
,與其他程式相同,這個功能是強大而應該小心使用的。下方程式會新建一個全局變數g1
,使這個宣告過程原本是一段字串。
load([[
g1 = 1 -- create a global variable g1
]])()
print(g1) -- => Output: 1
要注意的是,load
不回直接執行,其實其返回一個包裝函式:
f = load[[g2 = 2]]
print(type(f)) -- => function
print(g2) -- => Output: nil
f()
print(g2) -- => Output: 2
ECMAScript
類似的JS也有。
不過相較於JS,Lua其實有更安全的作法(以後會提到)。
eval('var g1 = 1')
console.log(g1) // => Output: 1
Python
Python有兩個函式做類似事情–eval
和exec
。不過eval
雖有返回值,但無法執行語句,只能執行表達式。所以本例中只能使用exec
。exec
永遠回傳None
,比起eval
更加強大。
【30天Lua重拾筆記16】基礎2: 多值返回&具名參數
回傳多值/多值返回
Lua函數可以返回多值。在我看來,這個特性是特殊的,只有少數語言真正做到多值返回。什麼意思?這表示在接收一個函數的返回值,可以輕易忽略掉主要值以外的結果。這些結果一般用於輔助,絕大多數時候,我們不關心、不需要,但有時候非常有作用。
先來看看Python裡的dict.get:
_d = {}
_v = _d.get("key", "value")
print(_v) # => Output: value
大多數時候,並不關心_v
的值是否真的從_d
來的,還是其實是個預設值。如果我們想要確定,那就要在多寫一次:
found = "key" in _d
我們可以替Lua寫一個相似的函數,但其還多返回一個用於解釋是否是有找到的值:
function get(dict, key, default)
if dict[key] ~= nil then
return dict[key], true
else
return default, false
end
end
其實可以在簡化一些:
function get(dict, key, default)
return (dict[key] or default), dict[key] ~= nil
end
這麼一來,平常可以這樣用:
【30天Lua重拾筆記15】基礎2: Label and Goto
Label & goto
這是一個強大的工具,要寫的漂亮並不容易,許多語言禁止了他。
Lua保有他。他很靈活,但你也應該慎重考慮是否真的應該使用。
而我認為,開發人員應該要是自由的,這才有創造的價值,這才能體現思考的藝術。就如同松本行弘說的:「語言體現思考的價值」
如果你的想法最初就是這樣想的,就先寫下來吧!「童子軍原則」別忘了逐漸改得更健壯。
Lua保有C/C++裡,goto
的能力,可以跳轉到函數區塊內任意可見的標籤(Label)。標籤的形式由兩個冒號夾成:
:: <Label Name> ::
跳出多層迴圈
BTW, Python 認為如果你需要使用到這個功能,表示你應該在拆分程式碼。
有了這強大的能力可以做啥?可以學像ES6那樣跳出外部迴圈:
九九乘法表只計算到6x4
(()=>{
outer:
for(let j = 2; j <= 9; j++){
let line_output = ""
for(let i = 1; i <= 9; i++){
line_output += `${j}x${i}=${j*i},\t`
if(i > 3 && j > 5){
console.log(line_output)
break outer
}
}
console.log(line_output)
}
})()
【30天Lua重拾筆記14】基礎2: 控制-while、repeat迴圈
print("鐵人賽開始")
for day=1,30,1 do
print("第" .. day .. "天: 每天寫文章")
end
print("鐵人賽結束")
for
迴圈適合用於次數明確的情況。例如鐵人賽30天發文不間斷,每天發一篇,發完30完賽。向這樣知道進步條件(每天一篇),中止條件(30篇),就非常適合用 for
迴圈。
Repeat/until迴圈
但有時候只知道中止條件,過程並不確定。像是在第n天,就寫n篇文章來囤稿,對我而言會是幾天完賽呢?
print("鐵人賽開始")
day = 1 -- 第幾天
completed = 0 -- 完成篇數
repeat
completed = completed + day
print("第" .. day .. "天: 每天寫文章,完成"..completed.."篇")
day = day + 1
until completed > 30
print("鐵人賽結束,完成篇數:"..completed) -- Output: 鐵人賽結束,完成篇數:36 -- (1+8)*8/2
鐵人賽開始
第1天: 每天寫文章,完成1篇
第2天: 每天寫文章,完成3篇
第3天: 每天寫文章,完成6篇
第4天: 每天寫文章,完成10篇
第5天: 每天寫文章,完成15篇
第6天: 每天寫文章,完成21篇
第7天: 每天寫文章,完成28篇
第8天: 每天寫文章,完成36篇
鐵人賽結束,完成篇數:36
需要每天寫文章,直到累積的文章超過30篇,也就完賽了。如果在第n天寫n篇,也只需要8天就完賽。
While迴圈
【30天Lua重拾筆記13】基礎2: 控制-For迴圈
相較於if
,Lua的for
迴圈有兩種,或說是三種。
進步的for迴圈
印出1-10:
for i = 1, 10, 1 do
print(i)
end
很類似C語言
#include <stdio.h>
int main(void){
int i = 0;
for(i = 1; i <= 10; i++){
printf("%d\n", i);
}
}
不同的是以逗點代替分號,中止條件相同時仍會在執行一次,進步(step)使用表達是,會自動相加並重新賦予值。
了解後也可來寫成10到1的程式:
for i = 10, 1, -1 do
print(i)
end
for-do
也是一個區塊,於一開始宣告的變數僅do-end
內部可見。這表示下方寫法中,進步條件指的是外部的i
,其值為1
。結果與輸出1-10相同。
i = 1
for i = 1, 10, i do
print(i)
end
pairs & ipairs初探 - for-in迴圈
for-in
迴圈用於迭代陣列(array
)或表格(table
)時。
Lua是沒有array型態的(還記得8種基礎型別嗎),關於這點之後會在說明。
至於什麼是table
?可以想像成是ES6裡的object
,或者更接近於Map
可以分別用ipairs
和pairs
來處理:
迭代陣列
【30天Lua重拾筆記12】基礎2: 控制 - 條件
分支條件控制 - if/elseif/else
Lua的分支控制條件就僅有這麼一組:if-then
/elseif-then
/else
/end
和其他語言一樣,elseif
可以出現多次。你想用else if
寫成巢狀也沒人會管你。他們兩個寫起來也真的蠻像的,除了你可能要多寫幾次end
:
if true then
print("if block")
elseif true then
print("elseif block")
else
print("else")
end
if true then
print("if block")
else if true then
print("elseif block")
else
print("else")
end
end
注意到最後有兩個end
。實際上我試故意將他這樣排版的,這樣兩個看起來比較像。
通常需要使用到巢狀分支,還是好好縮排比較好。
switch
雖然只有if
蠻簡單的,但有時候會想要使用switch
這類結構。沒事,可以模擬:
option = "one"
switch = {
["one"] = function () print "run one" end,
["tow"] = function () print "run two" end,
}
switch[option]() -- => run one
【30天Lua重拾筆記11】基礎1: 註釋
基礎2: 註釋
--[[
{
author = "lagagain",
date = 20200904,
title = "Comments of Lua code",
}
--]]
註釋是一段永遠不會被執行的區塊。你可以在註釋區塊隨意寫任何東西,而不必遵守任何Lua的語法規則。但更好的,註釋應該是作為程式碼的部份補充。至今為止的幾天,其實也過不少註釋。像是:
------------------
阿勒? 😲 這不是分隔線嗎?
單行註釋
是的,最基本的單行註釋以 --
開頭,直至行尾都會被視為註釋。也看過:
do
local a = 1
local b = 2
local sum = a + b -- sum = 1 + 2 => 3
end
或是
print("Hello, World") -- Output: Hello, World
本系列會部份以這樣方式去說明程式碼,程式碼的執行結果。不過在寫註釋時,最好是明確的補充程式碼的意義,像是:
【30天Lua重拾筆記10】基礎1: 類型 - 布林和nil
nil
nil
是Lua裡的一個特殊值,代表什麼也沒有。其型別也是nil
type(nil) -- => nil
布林
布林值只有true
和false
真值
只要不是nil
或是false
都為真, 包含0、空表、空字串 。
這與目前多數主流語言不同,需要特別注意!
【30天Lua重拾筆記09】基礎1: 類型 - 函數
函數 宣告
函數可以使用function
來做宣告,並以end
結束。
function hello()
print("Hello, World")
end
實際上這是Lua的語法糖,上面相當於:
hello = function()
print("Hello, World")
end
函數也是第一公民
在Lua,函數是第一公民,是基本型別,可以榜定於變數,也可以作為參數傳遞,或是回傳 值。
function genFun()
return function()
print("Hello, World")
end
end
function callFun(f)
f()
end
local _f = genFun() -- # get a function
callFun(_f) -- print("Hello, World")
函數、區域變數與閉包
函數同時也是一個區塊,可以保有區域環境變數,這樣的特性可以用來做閉包。
【30天Lua重拾筆記08】基礎1: 類型 - 字串
關於字串
與Python相同,字串是不可變得。但Lua字串於內部表示時,完全採用8-bits表示,包含0(\0
)。這也是為什麼在基礎1: 變數一篇,會特別說明\u{1F603}
的使用。
所以字串如果取其長度,可能和你想像的不一樣:
-- note: with UTF-8
print(string.len("我")) -- equal print(#"我") => 3
不過Lua是認得UTF-8的,其本身可以協助不支援UTF-8的程式語言(ex: C語言)。
-- note: with UTF-8
print(utf8.len("我")) -- => 1
C90包含<wchar.h>; C11有<uchar.h>。
儘管如此,不表示就是UTF-8寬字元(Wide character) 是電腦抽象術語(沒有規定具體實現細節),表示比8位元字元還寬的資料類型。不同於Unicode。 – 維基百科
因為Lua認得UTF-8,所以其原始碼最好以UTF-8儲存,否則可能執行會與想像不同。
字串的表示式
要表示字串在Lua有多種方式。
字面字串
簡單表示
【30天Lua重拾筆記07】基礎1: 類型 - 數字
整數與小數
數字(number)是Lua的基礎型別之一。Lua會自動判斷是整數還是小數,會自動轉換,無明確分界。
1.0 == 1
list = {"Bob", "Lua", "Luna", "Selene"}
list[1] == list[1.0]
type(1.0) -- => number
type(1) -- => number
雖然會自動轉換,不代表沒有區別
math.type(1.0) -- => float
math.type(1) -- => integer
溢位(overflow)
Lua 5.4使用64位元的整數和浮點數(雙精度浮點數double)。
math.type(9223372036854775807) -- => integer
math.type(9223372036854775808) -- => float
這表示Lua還是有可能溢位
9223372036854775807 + 1 -- => -9223372036854775808
精度丟失
浮點數的表示也有極限,可能會有精度丟失的問題
【30天Lua重拾筆記06】基礎1: 變數
變數名稱
Lua的變數名稱可以是底線(_
)或是任意字母([a-zA-Z]
)開頭,不能是數字或其他字元。之後的組成可以包含數字([0-9]
)、字母([a-zA-Z]
)或底線,並且是大小寫敏感,abc
、Abc
、ABC
可以作為三個不同的變數。
Lua作為嵌入型語言,使用最基本的ascii code。儘管其本帶有utf8函式庫,也可能有部份實現允許其他字元作為變數名稱,但不建議這樣做,並且在使用到非ascii字元時,最好以\u{1F603}
這樣方式來寫。像是:
print("\u{1F603}")
print('😃')
上面兩著等價,或是下面兩者亦相同。
print("\u{4f60}\u{597d}\u{ff0c}\u{4e16}\u{754c}\u{ff01}")
print("你好,世界!")
當然其實Lua是了解utf-8的,本系列也不會以上述方式撰寫,所以請確定儲存的程式檔案是以utf-8儲存,否則執行結果可能不如預期。
此外,Lua的底線開頭的變數,和Python一樣有時也代表一些意義,這會在說道metatable和物件導向程式設計時說明。
變數可見範圍
與多數程式語言一樣,分有全局變數和局部變數。不同的是,預設變數可見範圍是全局的(類似JS的var變數)。
g1 = 1
function f()
print(g1)
g2 = 2
end
f()
print(g2)
【30天Lua重拾筆記05】基礎1: 程式區塊(block、chunk)、排版
Lua的關鍵字
Lua的關鍵字並不多,就只有這麼幾個而已:
and | break | do | else | elseif | end |
false | for | function | goto | if | in |
local | nil | not | or | repeat | return |
then | true | until | while |
區塊(block)
有一些關鍵字會組合成程式區塊(block)。像是:
funciton
-end
do
-end
if-then
-end
while-do
-end
repeat
-until
夾於這些關鍵字中間的程式碼會成為一個環境區塊。
在區塊環境內,可以保有一些局部變數:
do
local name = "World"
print("Hello, "..name)
end
print(name)
Hello, World
nil
chunk
【30天Lua重拾筆記04】基礎1: Hello, Lua!
假設你已經選擇好並安裝 Lua的實現,且也準備好開發環境。使用過lua -v
沒問題後,就可以來試試看今天的入門示範程式。
你不必馬上了解今天的所有內容,將來都會說明到。今天只是快速的看看Lua程式長什麼樣子。
你可以執行lua
,在REPL交互式環境執行範例程式碼,也可以另存檔案,並用lua file.lua
執行看看。
第一個程式 - Hello, World
第一個程式?當然從打印出Hello, World開始。
Hello, World是指在電腦螢幕顯示「Hello, World!」(你好,世界!)字串的電腦程式。相關的程式通常都是每種電腦程式語言最基本、最簡單的程式,也會用作示範一個程式語言如何運作。同時它亦可以用來確認一個程式語言的編譯器、程式開發環境及運行環境是否已經安裝妥當。 – 維基百科
print "Hello, World"
Hello, World
不過這也太簡單了,來換個方式試試。
給個名字 - Hello, Lua
【30天Lua重拾筆記03】開發環境配置
開發環境配置
接著,來配置一下開發環境。主要會介紹三個開發環境,當然你想使用純文本編輯器也可以,我就是使用Emacs。
我會建議初學的人只使用代碼高亮的功能就好,最多…就使用到查找文件說明。 儘管今天介紹的配置都包含自動補全、格式美化、定義跳轉等等功能。但初學的人應該更關注在於其語法。(Lua語法也蠻自由的就是)
ZeroBrane
ZeroBrane是一個相當完整的Lua IDE,你幾乎可以直接下載下來使用。
其已經包含數個Lua版本,並可以輕易切換。
他也包含許多IDE應該要有的功能:
- 跳轉到函式定義
- 自動補全
- 除錯器
【30天Lua重拾筆記02】Lua的實現與選擇
Lua的實現與選擇
Lua的意思是葡萄牙文的「月亮」,其LOGO和其他相關也多與月亮有關。在開始使用學習Lua之前,比須先了解Lua的幾個版本與實際實現。
就像Python 2和Python 3有很大不同,Python 3各版本間又有些許不同。有些在Python 3.9能用的語法或功能,不一定可以在Python 3.5使用一樣。
Lua目前已經到了5.4版本,本系列內容也會以Lua 5.4為主。Lua 5.4於2020年釋出,所以還非常的新,有許多實現實際未達到這個標準。但Lua 5.1、5.3也已經使用多年,穩定度是可見的。就算是其餘版本的實現,也具有一定可用性。Lua設計極小,就某種程度上而言,甚至可以相對輕鬆的撰寫自己的實現。類似的於C語言,但是沒有C語言煩人的指標概念。所以Lua的實現也不少,其中著名的有:
極高效即時編譯的Lua實現。相容於Lua 5.1語法。
LuaJIT is compatible to the Lua 5.1 language standard. It doesn't support the implicit arg parameter for old-style vararg functions from Lua 5.0.
用於嵌入式設備開發的Lua,提供許多已經寫好的內容。(沒記錯的話也是使用Lua 5.1標準,沒用過)
Java版的實現,支援Lua 5.2的語法。在GitHub上有另一個Fork的版本,兩個有些差異。
JS實現的版本。其他JS實現的版本還有:Moonshine、lua.vm.js、Starlight。這是一個很新,也有點有趣的項目。我會在談談它。
C#的實現。
【30天Lua重拾筆記01】-認識Lua
認識Luna
盧娜(Luna,又寫作露娜或路娜)是羅馬神話中的月亮女神。「Luna」在法語和義大利語中也有月亮或月神的意思。在希臘神話中她的對應者為塞勒涅。盧娜也常常和黛安娜或赫卡忒混淆在一起。在羅馬的阿文提諾山上建有供奉她的神廟。 — 維基百科
錯棚了…
不是她 😅
再來一次 - 認識Lua
Lua是葡萄牙文的月亮,是一個輕量、快速、容易學習且容易嵌入的程式語言。其目標本就是成為一個很容易嵌入其它語言中使用的語言。其精簡的核心只包含一些最基本的功能,啟動速度非常之快。 儘管如此,透過最基本的功能,甚至可以實現多種編程範式。
可嵌入的程式語言
30天成爲Laravel萌新(目錄)
這是第二次參加「iT 邦幫忙鐵人賽」。上次報了兩個主題,只有一個完賽。這次報了三個主題……還好全都完賽了。雖然開賽前,對於一個主題先寫了近十天左右的文章,但在賽中,趕稿壓力還是頗大的。
30天成爲Laravel萌新(目錄)
「30天成爲Laravel萌新」是我最重要的一個系列,報名在Modern Web主題下,並同步發表於又LAG隨性筆記。下面列出每天文章連結:
- 30天成爲Laravel萌新(第0天) - 前言
- 30天成爲Laravel萌新(第1天) - 認識Laravel
- 30天成爲Laravel萌新(第2天) - 安装 Laravel
- 30天成爲Laravel萌新(第3天) - 使用laradock建立開發環境(上)
- 30天成爲Laravel萌新(第4天) - 使用laradock建立開發環境(下)
- 30天成爲Laravel萌新(第5天) - Laradock的工作空間容器
- 30天成爲Laravel萌新(第6天) - 配置專案
- 30天成爲Laravel萌新(第7天) - 認識artisan
- 30天成爲Laravel萌新(第8天) - 路由&頁面模板(1)
- 30天成爲Laravel萌新(第9天) - 路由&頁面模板(2)
- 30天成爲Laravel萌新(第10天) - 路由&頁面模板(3)
30天成爲Laravel萌新(第30-1天) - 總結
這是第二次參加鐵人賽。這個主題是我決定參賽出就已經定好,總算寫完了。從介紹、安裝、配置,使用laradock
、artisan
,路由、模板,Parsdown
(Markdown),再到控制器、資料庫,以及另我以些驚豔的Pagiantion
,然後多語系支援、紀錄檔,客製化錯誤頁面,檔案上傳與表單驗證,到最後登入驗證。
在這過程中,一學習到不少,很充實。(還有下次不要在一次報三個主題了…)
不過,Laravel官方文檔其實相當豐富完整,這30天的文章,頂多只能當作而外的參考而已,但願對想學習Laravel的人還是有幫助。
最後,你可以在這裡看到跟Laravel有關的文章。或是看看另外兩個系列文章( 又LAG的EOS.IO技術筆記 和 有點玩鬧性質的 又LAG的ML學習筆記 )。
30天成爲Laravel萌新(第30天) - 登入驗證
這部份在Django時,明明是最先學的,在Laravel卻放到了最後☺
要使用Laravel提供的會員系統,相當容易,只須要:
artisan make:auth
artisan migrate
然後瀏覽http://localhost/register 註冊帳號,或是http://localhost/login 登入帳號。仔細一看,會多了這些檔案:
new file: app/Http/Controllers/HomeController.php
new file: resources/views/auth/login.blade.php
new file: resources/views/auth/passwords/email.blade.php
new file: resources/views/auth/passwords/reset.blade.php
new file: resources/views/auth/register.blade.php
new file: resources/views/auth/verify.blade.php
new file: resources/views/home.blade.php
new file: resources/views/layouts/app.blade.php
modified: routes/web.php
其中routes/web.php
多了:
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
現在瀏覽http://localhost/home 也有畫面了。
30天成爲Laravel萌新(第29天) - 表單驗證
昨天的程式碼有一些註解的內容,先取消註解試試。
resources/views/images/upload.blade.php部份內容
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
routes/web.php處理post('/images/upload')
請求的部份內容:
Validator::make($request->all(), [
'file' => 'required|image',
])->validate();
驗證表單資料
Validator::make($request->all(), [
'file' => 'required|image',
])->validate();
差不多等價於:
$validate = $request->validate([
'file' => 'required|image',
]);
$request
有validate()
的方法。還可以直接建立一個驗證器,繼承Request
:
php artisan make:request ImageUpload
上面執行完後,現在,在app/Http/Requests/
目錄下多了ImageUpload.php
,內容改成下面這樣:
30天成爲Laravel萌新(第28天) - 上傳檔案
Laravel要上傳檔案非常的簡單,今天就來簡單帶個範例吧!
建立檔案目錄連結
首先,現用Artisan建立目錄連結。
artisan storage:link
上面命令會建立storage/app/public
目錄,並將目錄同樣綁定到public/storage
。這讓於此目錄下的內容,可以透過http://localhost/storage/<FILE NAME>
存取。在public
目錄下的檔案,基本都可以直接透過瀏覽器存取。
上傳頁面
同樣以一個簡單的上傳頁面作為範例。先建立resources/views/images/upload.blade.php
:
@extends("base",['title'=>'上傳圖片'])
@section('title', '上傳圖片')
@section('body')
<form action="{{route('image.upload')}}" method="post" enctype="multipart/form-data">
@csrf
<!--
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
-->
<input name="file" type="file" accept="image/*" value=""/>
<input name="" type="submit" value="上傳"/>
</form>
@endsection
30天成爲Laravel萌新(第27天) - 本地化、多語言支援(Localization)
@extends("base",['title'=>'哎呀,找不著頁面'])
@section('title', '哎呀,找不著頁面')
@section('body')
<h1>{{__('404 error message')}}</h1>
<b>哎呀,找不著頁面</b>
<p>
<ul>
<li><a href="/">點擊我,回到首頁</a></li>
<li><a href="{{route('blog/post.index')}}">我的網誌</a></li>
</ul>
</p>
@endsection
開始
在昨天簡單建立的錯誤頁面中,又再一次偷渡了一個東西。對,就是那個有點怪怪的{{__('404 error message')}}
,實際上這等價於@lang('404 error message')
。透過lang()
函式的方式,也可以在控制器做變換。lang()
會根據給定的字串,以及設定(app/config.php
)中的locale
和faker_locale
轉換為對應的語言。預設會轉換為locale
所設定的,而備用語言可由faker_locale
設定。當兩者都不存在時,會直接輸出字串。還記得我們把app/config.php
部份內容改成這樣嗎?
'locale' => 'zh-TW',
'fallback_locale' => 'en',
不過,不管是locale
還是faker_locale
,都還找不著{{__('404 error message')}}
要轉換成的文字,所以現在會直接生成404 error message
。
30天成爲Laravel萌新(第26天) - 客製化404錯誤頁面
HTTP協議上存在許多狀態碼,其中400系列、500系列錯誤可能是最常見到。200、300很難被注意到。恐怕又以404錯誤、403錯誤、500錯誤、503錯誤最常見。一個好的錯誤提示頁面,可以帶來很好的使用體驗。你可以看看別人怎麼設計(以及2018年不可錯過的創意404報錯設計),甚至更有404 PAGE TEMPLATE (https://www.404pagefree.com/) 可以直接下載404錯誤頁面模板(這網址真有趣),裡頭模板多以CC-BY 3.0姓名標示授權,可用於商業用途。
那麼在Laravel要怎麼客製化這些錯誤提示頁面?以404錯誤頁面為例,可以建立resources/views/errors/404.blade.php
檔案,寫入以下內容:
@extends("base",['title'=>'哎呀,找不著頁面'])
@section('title', '哎呀,找不著頁面')
@section('body')
<h1>{{__('404 error message')}}</h1>
<b>哎呀,找不著頁面</b>
<p>
<ul>
<li><a href="/">點擊我,回到首頁</a></li>
<li><a href="{{route('blog/post.index')}}">我的網誌</a></li>
</ul>
</p>
@endsection
同樣的…CSS不是本系列重點……
30天成爲Laravel萌新(第25天) - 紀錄檔
使用Logging
現在,要再回到laradock來。不過同樣的,Lavavel儲存紀錄檔的地方也是在storage/logs
。這裡會儲存使用Log::info()
所寫下的紀錄。
docker-compose exec workspace bash -c "tail -f storage/logs/最新的log"
如果使用Linux的話,也可以直接:
tail -f storage/logs/最新的log
當然你也可以直接把紀錄檔,用文字編輯器開啟。不過上述會這麼做,是因為如次可以更簡單的在開發時除錯。有時候,Laravel錯誤訊息頁面提供的訊息並非一目瞭然,會需要用到printf
方式除錯(我假設…你不動gdb
,但用過最基本的除錯手法…)。在這裡,使用一系列Log::
的方法,其中,Log::debug()
就對應除錯資訊。這和我曾經寫過的瀏覽器console.log()外的一些其他用法 有些像。
30天成爲Laravel萌新(第24天) - 頁面清單 下篇(使用Pagnation和刪除按鈕)
就先來看看使用Laravel提供Pagination能夠多麼簡單。
首先,控制器只需要使用BlogPost::paginate($limit)
查詢就好,並將查詢結果直接傳遞給視圖。
public function index()
{
// 如果想要將每頁顯示數量變成可以調整的,
// index需要傳入 (Request $request),並由下方方式取得$limit傳入paginate
// $limit = $request->query('limit', 30);
$posts = BlogPost::paginate(30);
return view("blog/index",[
"posts"=>$posts,
]);
}
BlogPost::paginate($limit)
會自動根據Request的page
參數取得存取的頁面,自動計算offset
,並根據給予的每頁顯示數量限制來查詢。甚至連index()
都不需要傳入Request $request
。傳給視圖的也少了不少。說到視圖,立馬來看看變得多簡潔:
@section('body')
<div class="container">
<ul>
@foreach ($posts as $post)
<li>
<a href="{{route('blog/post.show',['id'=>$post->id])}}">{{ $post->title }}</a>
</li>
@endforeach
</ul>
</div>
{{ $posts->links() }}
@endsection
列出文章清單的部份,大致什麼太大變化。但是頁面頁數選項不再那麼複雜,沒錯,只需要$post->links()
就好。他會自動生成符合Bootstrap的Pagination。在瀏覽器接受到的HTML樣子可能如下:
呵呵,根本不需要寫這麼麻煩…也太簡單了吧!
30天成爲Laravel萌新(第23天) - 頁面清單 上篇
因為重點不在於CSS,不在於Bootstrap。 其實大可以將頁面弄的很美觀。在Laravel專案資料夾中,其實有一個給
npm
使用的package.json
檔案。 不過還是來把重點放在Laravel上。
使用Pagnation前
Laravel提供一個超級簡單的方式,來處理分頁問題。不過在使用前,來看看原本要怎麼實現index()
。不但要取得頁數,並計算檢索數量,然後從資料庫透過offset()
、linit()
的方式取得資料。
public function index(Request $request)
{
$offset = $request->query("offset", 0);
$limit = $request->query("limit", 30);
$page = $request->query("page");
if($page){
$offset = ($page - 1) * $limit;
}else{
$page = ($offset / $limit) + 1;
}
$request->merge([
"offset" => $offset,
"page" => $page,
]);
$posts = BlogPost::orderBy('activity_id','ASC')
->offset($offset)
->limit($limit)
->get();
return view("blog/index",[
"posts"=>$posts,
"page"=>$page,
"total_pages" => $total_pages,
]);
}
30天成爲Laravel萌新(第22天) - 資源控制器(Resource Controller) 下篇
最終,我決定將index()
和destroy()
另外寫。一個是寫完create()
和edit()
,destroy()
也就不怎麼難。但是index()
意外也能有豐富內容能寫…(加上有另一種用法我還不太會)
總體來說,這篇程式還是有些趕工粗糙….
重點觀念
@method
的使用 相當於隱藏欄位_method
。@csrf
的使用
建立頁面模板
同樣的,先建立一個編輯頁面的模板resource/views/blog/edit.blade.php
:
@extends("base",['title'=>'編輯文章'])
@section('title', '編輯文章')
@section('body')
<form method="post" action="{{($type=="edit") ?
route("blog/post.update", ["id"=>$id]) :
route("blog/post.store")}}">
@csrf
@method(($type=="edit")? "patch" : "post")
<label for="title">標題:</label>
<input name="title" type="text" value="{{$title}}" id="title" />
<br/>
<label for="content">內容:</label>
<textarea cols="30" id="content" name="content" rows="10">{{$content}}</textarea>
<br/>
<input name="" type="submit" value="儲存"/>
</form>
@endsection
30天成爲Laravel萌新(第21天) - 資源控制器(Resource Controller) 中篇
現在,要正式把控制器與資料庫連結起來。順便偷埋之後兩個主題☻。
引入所需使用的套件
首先,得先把之前建立好的Model引入。另外,我們在偷偷引入一個Illuminate\Support\Facades\Log
。
use Illuminate\Support\Facades\Log;
use App\BlogPost;
use Parsedown;
完成CRUD操作
Create / Store
public function store(Request $request)
{
$title = $request->input("titile", "未命名文章");
$content = $request->input("content");
$post = new BlogPost;
$post->title = $title;
$post->content = $content;
$post->save();
Log::info("Store New Blog Post: id = $post->id");
return redirect()->action(
'Blog\PostController@show', ['id' => $post->id]
);
}
可以透過Request
的input
方法,取得POST來的資料,還可以補上預設參數。這邊取得標題與內容後,建立一個新的PostBlog
實體存入資料庫。
Read / Show
public function show($id){
$post = BlogPost::find($id);
if(! $post){
abort(404);
}
$content = $post->content;
{
$Parsedown = new Parsedown();
$content = $Parsedown->text($content);
}
return view("blog.post", [
"title" => $post->titile,
"content" => $content,
]);
}
讀取與之前控制器的內容差不多。不同的是,需要先從資料庫尋找資料,如果找不到就回傳404找不到錯誤頁面。之後會對該頁面進行修改。現在http://localhost/blog/post/12 將會顯示404錯誤頁面,只有http://localhost/blog/post/1 才會出現之前填入的內容。
30天成爲Laravel萌新(第20天) - 資源控制器(Resource Controller) 上篇
CRUD & RESTful
所謂CRUD是Create、Read、Update、Delete。昨天已經從資料庫模型(Model)的角度看過基本操作了,今天要將些操作加入到控制器(Controller)。
C | Create
R | Read
U | Update
D | Delete
另外,還需要另外知道的一件事情是RESTful。RESTful並不是硬性規定,只是一種在HTTP請求上的一種慣例設計。通常HTTP請求方法有GET
、POST
、PUT
、DELETE
、PATCH
,以及HEAD
、CONNECT
、OPTIONS
、TRACE
。以下節錄自MDN:
GET
GET 方法請求展示指定資源。使用 GET 的請求只應用於取得資料。
HEAD
HEAD 方法請求與 GET 方法相同的回應,但它沒有回應主體(response body)。
POST
POST 方法用於提交指定資源的實體,通常會改變伺服器的狀態或副作用(side effect)。
PUT
PUT 方法會取代指定資源所酬載請求(request payload)的所有表現。
DELETE
DELETE 方法會刪除指定資源.
CONNECT
CONNECT 方法會和指定資源標明的伺服器之間,建立隧道(tunnel)。
OPTIONS
OPTIONS 方法描述指定資源的溝通方法(communication option)。
TRACE
TRACE 方法會與指定資源標明的伺服器之間,執行迴路返回測試(loop-back test)。
PATCH
PATCH 方法套用指定資源的部份修改。
其實也就差不多對應了CRUD的操作:
CRUD | RESTful |
---|---|
Create | POST / PUT |
Read | GET |
Update | PATCH / PUT |
Delete | Delete |
30天成爲Laravel萌新(第19天) - Model的基本操作
ORM是物件關聯對映(Object Relational Mapping)。在Larvel裡提供的 Eloquent ORM能讓開發者以一個簡單、美觀的方式操作關聯式資料庫。今天就來寫一點點基本的操作,更多可以查看文件,以及Laravel資料庫相關文件。
Select All
在SQL,可能會用SELECT * FROM <TABLE>;
來取得所有資料,儘管在資料量龐大時,這樣操作並不好,但是Laravel同樣有對應的操作:
use \App\BlogPost
$posts = App\BlogPost::all();
foreach ($posts as $post) {
echo $post->title;
}
條件查詢Where
條件查詢也非常長使用到:
$post = App\BlogPost::where('id', 1)->get();
// ->orderBy('name', 'desc') // 還可以加以設定以下參數
// ->take(10)
// ->get();
用where
自然很容易從SQL轉換了解。不過,Laravel有更簡單的方式針對主鍵做查詢:
$post = App\BlogPost::find(1);
// 等同於
$post = App\BlogPost::where('active', 1)->first();
30天成爲Laravel萌新(第18天) - 建立Model
前幾天建立了Blog Post在資料庫的Schema,並且遷移(Migration)進資料庫,建立相關的資料表(blog_post)。為了讓Larave提供的ORM系統,以一個美觀、簡易的方式操作資料庫,還得建立BlogPost Model類別。
當然也可以使用artisan建立一個樣板:
artisan make:model BlogPost
然後會有app/BlogPost.php
的檔案
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogPost extends Model
{
//
}
這次,只需要簡單改成下面的樣子就好:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogPost extends Model
{
protected $table = "blog_post";
// protected $primaryKey = 'id';
// public $timestamps = false;
}
30天成爲Laravel萌新(第17天) - 資料庫管理工具(下)‧使用DBeaver
今天要在介紹一個,我用過後就喜歡上的資料庫管理工具–DBeaver。
DBeaver的兩個版本
DBeaver有社區版和企業版兩種。社區版可以免費使用,並且跨平台,透過JDBC支援多種資料庫。此外還會自動檢測資料庫版本,自動尋找相對應的Drive。
我手動配置過phpMyAdmin過。同樣直接下載安裝的情況下,DBeaver簡單好多。
DBeaver的界面很漂亮,我還蠻喜歡的。其中還有一個功能測試時非常好用–產生假資料。這個和前幾天亂數短文很像,不過DBeaver會針對欄位型態產生資料。資料庫測試的模擬數據生成(Mock Data Generation)也有一些限制,有些資料類型還是無法產生。
連線資料庫並填入資料
說了這麼多,相信已經下載安裝好了吧?該像昨天一樣,來連接資料庫,並塞入文章吧!
30天成爲Laravel萌新(第16天) - 資料庫管理工具(上)‧使用phpMyAdmin
今天,要用 phpMyAdmin 來看看昨天建立的資料表,我會順面補充一些之前做的設定。
查看Docker容器狀態
我會假設使用的是laradock,如果你使用的是XAMPP可能會簡單一些。
那麼,首先先看看運行的容器:
docker-compose ps
我們之前有啟動phpMyAdmin的服務,那麼輸出應該包含下面內容:
Name Command State Ports
---------------------------------------------------------------------------------------------------------------
laradock_phpmyadmin_1 /docker-entrypoint.sh apac ... Up 0.0.0.0:8080->80/tcp
其中注意到 Ports 的部份,這意思是綁定(bind)電腦8080端口到容器的80端口。所以我們可以透過瀏覽 http://localhost:8080 來使用phpMyAdmin。
登入phpMyAdmin
這裡伺服器要輸入mariadb
,帳號和密碼分別是:default
和secret
。如果你是啟用MySQL的話,伺服器就改成mysql
。
30天成爲Laravel萌新(第15天) - 建立資料庫Migration
要把文章存入資料庫?那麼對於傳統關聯式資料庫,需要先建立資料表Schema。你可以透過明天要介紹的資料庫管理工具,也可以透過今天來來說的使用 Artisan 建立資料表。
使用Artisan建立資料表
Artisan可以快速的建立控制器基本模板,同樣也可以建立資料庫相關模板:
artisan make:migration create_blog_post_table
透過上面命令,會在database/migrations
新增相關檔案,檔名由<日期><時間>_create_blog_post_table
組成。前面時間相關的在某些情況下會很重要,後面介紹的 artiasn 命令會依序建立資料表,所以如果資料表有相互依賴就有影像。現在我們開啟該檔案看看:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateBlogPostTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('blog_post', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('blog_post');
}
}
其中最重要兩行:
$table->bigIncrements('id');
$table->timestamps();
30天成爲Laravel萌新(第14天) - 控制器(Controller)
回頭看看,看,怎麼Route的內容變得這麼長阿!仔細一看,還做了路由以外的事情,像是修改變數內容等等。是不是該把Hangle Function獨立出來?有什麼辦法?Laravel的控制器(Controller)可以幫助。
新增Controller
我們可以透過Artisan來快速新增一個標準的控制器(Controller):
artisan make:controller Blog/ExamplePostController
在app/Http/Controllers/Blog
資料夾下會多一個ExamplePostController.php
的檔案,內容大致如下:
<?php
namespace App\Http\Controllers\Blog;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class ExamplePostController extends Controller
{
//
}
Blog
這個新建立的資料夾,實際對應於昨日group
路由當中的'namespace' => 'Blog'
參數。並且在上面生成的程式中use App\Http\Controllers\Controller;
,也是因為這個新建立的模組是在App\Http\Controllers\Blog
底下的命名空間,因此要引入使用App\Http\Controllers\Controller
。關於PHP的模組系統,就不多做解釋了,只是相關的Blog
字串如不想使用,要做相對應的調整。
之後會改用資源控制器。這邊單純要做個對照,之後可以刪除不使用。
30天成爲Laravel萌新(第13天)- 簡易Blog頁面(下)
昨天利用亂數文章(Lorem ipsum)產生了@section('body')
的預設內容。今天,要在Route把變數$content
設成類似的亂數文章。
中文亂數文章
Lorem最多的資源大多還是英文,不過中文也有不少資源可以使用。此外,有些情況使用設計好的預設文字可能會更適合。今天,就只是想來用用而已XD。透過亂數假文產生器 Chinese Lorem Ipsum可以產生中文亂數文章,也可以用於產生標題。現在來簡單的把$title
設成日的及度加機子魚年
。$content
設成:
身主一?是字對一日國地包最中感行物評民活於力!進上城大、很會英我:的樣基覺型家海當期是著:算像系大心樣十因天特企以加想點以後許,念落是客我什在白實文種運明市下只能當作力美間要速包企以統推多,收什火投,顧外。
指的司之身鄉然說小人發,我雜人轉英臺區實說麼後男會友大,阿產效第續明造識竟自沒大上有。到全藝地港為精件連需,過聯排可然更友我後們時?一風了組麼兒比、方來出科!車卻讀力風人父資國個年愛成故作自功:原用中有、港看車我高心打!親北受活女微特利動果的無何片經物……象巴約得?色時值壓五……過點子得提圖陽可計,來麼室拉的合天!人觀難一手樹以防黃精也需被那部我冷不不連痛以哥多人事營件、目熱而夫加……紀味曾蘭不好空農後電題熱頭國:戲識者是相大覺光,雙命影心可那?前區孩增,物時。
產明物力區有王真?院見很原,經總萬官方,生回。
海在康代界積實兒變:她哥出邊。善快寶,死面大,紀著資小成去資戲面和的發行的止的接來,南生不能野:政生企性班們教求給學就得受再球也生克決的金長冷成。
標題、文章內容當然還無意義wwww
調整排版
恩~沒分段了?這是因為HTML會忽略空白,如過要換行還需要使用<br/>
。當然你可以在變數內容這麼加,然後在模板將顯示的部份改為{!! $content !!}
,或是也可以用<pre>
來做,像是把@section('body')
改成:
@section('body')
<h1>{{$title}}</h1>
<pre>{{$content}}</pre>
@endsection
30天成爲Laravel萌新(第12天)- 簡易Blog頁面(上)
今天要來建立一個部落格文章的極簡陋顯示頁面。
建立路由
在routes/web.php
檔案中添加以下內容:
Route::group(['prefix' => 'blog',
'as' => 'blog/',
'namespace' => 'Blog', ],
function(){
Route::get('/post/{post_id}', function($post_id){
$title = "Example Title";
$content = "Example Content";
return view('blog.post', [
"title" => $title,
'content' => $content,
]);
});
});
儘管其實完全不需要用到group
,不過未來可以像其他blog系統一樣,除了有文章頁面(post),還可以有其他獨立頁面(page)。另外,添加blog的前綴,可以將其他前綴用於其他作用,或是最後在做個轉址。總之,先選了個靈活的寫法。
其中,好好注意$title
和$content
這兩個參數,這兩個參數隨後會在多次變動修改,今天主要先把頁面顯示出來。
部落格文章頁面模板
接者建立resources/views/blog/post.blade.php
檔案(當然需要先建立resources/views/blog
資料夾)。並且上頭原本路由區用的view也可以寫為blog/post
。將這份檔案簡單填入以下內容:
@extends("base",['title'=>$title]) {{-- 第二個參數可以傳遞變數給父模板,但是父模板改用插槽方式,須改用以下方式 --}}
@section('title', $title)
@section('body')
<h1>{{$title}}</h1>
{{$content}}
@endsection
這個頁面模板繼承了基礎頁面(base
),並填入頁面內容。不過,還沒有基礎也面阿?現在來建立resources/views/base.blade.php
,並寫下以下內容:
30天成爲Laravel萌新(第11天) - 路由&頁面模板(4)
使用Artisan顯示目前路由狀態
首先,先來說說怎麼看目前路由狀態。透過artisan route:list
列出目前路由狀態:
+--------+----------------------------------------+--------------+------+---------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------------------------------------+--------------+------+---------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | hello | | Closure | web |
| | GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS | hello-world | | Closure | web |
| | GET|HEAD | hello/{name} | | Closure | web |
+--------+----------------------------------------+--------------+------+---------+--------------+
首先要注意到的是Method
和URI
,這也是在定義路由最基礎的部份,URL
在路由定義時就稱作PATH
。我們還可以將路由命名(Name
);至於Action
與以後會提到的控制器(Controller
)有關,這裡顯示Closure
表示路由由handler function
處理請求;最後Middleware
預計本系列文章不會提到,又興趣可以去看官方文件。
api/user
定義在routes/api.php
中。
基礎路由方法
基本路由方法有:
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
基本上就對應了REST的GET
、POST
、PUT
、PATCH
、DELETE
、OPTIONS
的請求方法。此外還可以透過Route::any($uri, $callback);
來直接處理請求,而不管請求方法;或是使用Route::match([$method,...], $uri, $callback);
來處理特定請求方法。
※ 請求方法 是HTTP請求裡頭的一個欄位:METHOD。
30天成爲Laravel萌新(第10天) - 路由&頁面模板(3)
回去看了一下文件,才發現之前有好多沒用到過 畢竟只學了2周的時間。不過有些設計挺有趣的。
今天來看一下Blade模板語言中的一些(我覺得)重要的功能。
印出變數
昨天已經看過怎麼顯示變數了,使用{{$variable}}
。今天要特提的是:怎麼給變數一個預設值。
{{isset($variable) ? $variable : "Default Value"}}
{{ $variable or 'Default' }}
不過在5.8、6.x以後似乎改成這樣:
{{$variable ?? 'Default'}}
我不清楚爲什麼把這麼方便的功能給去除掉了。 6.x之後的用法並沒有在官網的文件特別提,會發現單純是範例中有類似程式碼。
上面其實還相當於
@isset ($variable)
{{$variable}}
@else
Default
@endisset
另外還有@empty
@empty($variable)
Default
@else
{{$variable}}
@endempty
30天成爲Laravel萌新(第9天) - 路由&頁面模板(2)
繼續昨天
Route::get('/hello/{name}', function ($name) {
return '<h1>Hello, '.$name.'</h1>';
});
透過路徑接受了參數傳了進來,再加以加工輸出回去。不過這麼寫不怎麼美觀, 因為這違反了 「 只做一件事情 」的原則,不但處理了路由請求,還傳遞參數並渲染頁面。
今天透過模板系統,要來把路由請求處理與頁面設計分離。
首先,把上面程式修改為以下內容
Route::get('/hello/{name}', function ($name) {
return view("hello-name", [
"name" => $name,
]);
});
接著在新增一個模板檔案resources/views/hello-name.blade.php
<h1>Hello, {{$name}}</h1>
然後瀏覽http://localhost/hello/Daniel 看看,應該與之前畫面並無差異。
30天成爲Laravel萌新(第8天) - 路由&頁面模板(1)
接下來幾天,會交叉介紹路由(Route)和頁面模板(View/Blade)。幾經思考,這兩者關係十分密切,不太好單獨撰寫。
關於路由(Route),我們曾經在第六天短暫看到過。
<?php
Route::get('/', function () {
return '<h1>Hello</h1>';
});
現在我們看看routes
資料裡面的內容:
- api.php
- channels.php
- console.php
- web.php
我們修改的是web.php
的內容,這也是最主要訂立路由的檔案。api.php
的檔案實際上也不太有差別,不過api.php
底下定義的路由,預設會前置增加api/
的前綴,是設計用於提供HTTP API路由定義的檔案。
瀏覽 http://localhost/ 以後,就會顯示Hello World,如果你嘗試右鍵瀏覽網頁原始碼(view-source:http://localhost/)就會看到<h1>Hello</h1>
。
在回頭看看網址,除了localhost
外,後面的就是路徑/
,認知道這個斜線是很重要的,因為Apache2和Nginx對於結尾斜線的認知有些不同。接著再在wep.php
增加以下入由看看:
30天成爲Laravel萌新(第7天) - 認識artisan
Laravel有「 為網頁藝術家創造的框架 」的美譽,他的工具名字也很有意思 artisan ,意為 工匠 ,與藝術家(artist)一樣,是與藝術(art)有關的字。
artisan 可以用來顯示路由狀態、遷移資料庫、產生基本樣板程式碼、調整文件結構狀態等等。而且之前已經看過,就是我們用來產生專案文件密鑰(key)的artisan key:generate
。
不過,如果使用laradock
進入workspace
的docker容器的話,可能會找不著指令。artisan
詳細使用方式,會在未來有需要時在做說明,今天,就先來簡單看一下。
如果使用laradock
進入workspace
的docker容器的話,找不著指令嗎? 透過下面命令切換到/var/www
目錄下在試試。
cd /var/www
列出所有artisan子命令
不同版本的artisan
有可能存在使用差異。今天主要說明怎麼快速了解指令如何使用。
首先先學著者麼列出所有能使用的工具:
artisan
或是
artisan list
30天成爲Laravel萌新(第6天) - 配置專案
在前三天已經安裝好Laravel的基本環境。今天算是一個分水嶺,不管你採用哪種方式建立開發環境,都應該已經得到一個預設好的Laravel工作目錄。在此我不會解釋目錄結構,有興趣可以自行參閱文檔。不過是先留意一下幾個文件與目錄:
- artisan
- config/
- database/
- public/
- resources/
- routes/
- storage/
之後有用到會在加以說明。而今天,首先要進行專案的配置,也就是設定(config/)。沒錯,目錄 config 就是儲存相關配置的目錄。不過在此,我們還得先編輯 .env 檔案。你可能會找不著這份檔案,別擔心,目錄下有個 .env.example ,將其複製並重新命名即可。
接著我們找到以下內容:
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
目前最爲重要的是關於APP和DB的設定。在發佈階段,APP_URL
會需要調整,不過我們之後在說說。而目前預設情況僅有自己的電腦能夠瀏覽。其次是APP_DEBG
,在發佈階段需要改為false
。 此外,還會注意到一個APP_KEY
的設定,如果其為空,請執行以下命令:
php artisan key:generate
接着是資料庫的設定,這裏需要改成資料庫的連結設定。如果使用MariaDB和MySQL,維持mysql
就好。(新版本的MariaDB可能有問題)
30天成爲Laravel萌新(第5天) - Laradock的工作空間容器
在開始配置Laravel的環境設定檔案之前(.env
),先來帶大伙看看laradocke最
重要的一個容器workspace
。
關於workspace容器
workspace是laradock連結各個容器的重要容器,還記得我們昨天這麼做嗎:
docker-compose exec workspace composer create-project --prefer-dist laravel/laravel tutorial_blog 5.8.*
docker-compose exec
的格式是docker-compose exec <CONTAINER> <COMMAND>
。可是,可沒有吧workspace
啟動(up
)起來呀!我們像下面啟動了nginx
、mariaDB
、phpmyadmin
而已阿。
docker-compose up -d nginx mariadb phpmyadmin
是的,laradock會自動啟動workspace
這個容器。並且注意到後面的COMMAND
實際就是在第2天用來初始話laravel專案的命令。在下完這的命令以後,會在原本建立的 laravel-tutorial 目錄下多一個 tutorial_blog 目錄。並且裡面有基本Laravel的環境。我們會在幾天後來設定這個環境,現在,來我們專注於 workspace 這個容器。
我們同用用docker-compose exec
來進到 workspace 容器裡面。
docker-compose exec -u laradock workspace /bin/bash
或是用docker exec
來進到裡頭。不過使用docker exec
你還會需要知道真正的容器名字。所以命令可能像是下面這樣:
docker exec -it -u laradock laradock_workspace_1 /bin/bash
我們使用 laradock 登入workspace容器(-u/--user
)。laradock 預設用於開發的使用者帳號,如果不加上這個選項,會使用最高管理權限登入(root)。透過使用這個帳號登入,未來可能可以省去一些不必要的麻煩(ex:宿主機和虛擬機掛載目錄的權限問題)。
30天成爲Laravel萌新(第4天) - 使用laradock建立開發環境(下)
建立專案目錄
本次專案目錄預計會有以下內容:
- laravel-tutorial
- laradock
- .laradock
- tutorial_blog
laradock 和 .laradock 先不管他們。先建立 laravel-tutorial 目錄,並在該目錄鍵入:
git clone https://github.com/Laradock/laradock.git
cd laradock
git checkout v7.15
以上會安裝laradock,並切換到7.15版本。
設定laradock
在建立服務容器前,要先做設定。
首先, 複製 env-example 為 .env,然後找到以下設定並變更:
APP_CODE_PATH_HOST=../tutorial_blog
...
...
...
DATA_PATH_HOST=../.laradock/data
APP_CODE_PATH_HOST
指定專案目錄(下一步驟建立),DATA_PATH_HOST
則是未來資料儲存的位置,包含資料庫儲存位置。
接着建立並啓動環境:
30天成爲Laravel萌新(第3天) - 使用laradock建立開發環境(上)
除了使用composer
以外,還可以使用 laradock 、Homestead、Valet、Laragon。Homestead是基於 Vagrant ,如果您已安裝VirtualBox,可以使用看看。而接下來說明laradock的使用方式。
Laradock 環境需求
laradock是基於docker的一個快速建立laravel的開發環境工具,理所當然的你會需要docker,此外你還會需要 docker-compose,以下列出範例使用的版本:
軟體 | 版本 |
---|---|
docker | 18.09 |
docker-compose | 1.25 |
git | 2.7.4 |
透過使用laradock,可以很快速的在Apache2、Nginx;MySQL、MariaDB;甚至是在PHP不同版本之間做切換。
30天成爲Laravel萌新(第2天) - 安装 Laravel
環境需求
這不是全部強制的,只是接下來一個月的時間,會以以下環境為範例:
軟體 | 版本 |
---|---|
Laravel | 5.8.18 |
Nginx | 1.14.0 |
MariaDB | 10.3.15 |
PHP | 7.2.19 |
如同前言所說,雖然Laravel已經釋出6.0版,但接下來將會以5.8為主。此外,也可以使用Apache網頁伺服器,儘管有些設定不同;至於資料庫也可以使用MySQL。並且,以上也都不是強制的,Laravel對於多個網頁伺服器、資料庫接受度良好,所以當然也可以使用PostgreSQL和其他支援PHP的網頁伺服器。
(雖然上面這樣列出,不過最後有可能會用Apache+MySQL再測試一次)
使用XAMPP
明天,我會介紹另外一個快速建置環境的方式,我會更推薦使用該方式。
如果你是Windows,可以直接安裝XAMPP,只是在之後如果遇到問題,請注意一下各個組件的版本。更多可以參考XAMPP的網站。
安裝Laravel
Composer是PHP的一個包管理器,儘管不是必要的,不過可以大量簡化Laravel的安裝程序,並且也可以加以安裝其他組件。因此,需要先確定Composer已經安裝好,並設定好環境。你可以透過 命令提示字元 或其他 Shell 的環境輸入composer -V
,正確安裝完會顯示版本資訊。
安裝Laravel:
composer global require laravel/installer
30天成爲Laravel萌新(第1天) - 認識Laravel
原本,我是想寫下Laravel的介紹,但是…可能有些單調。因為有些經驗實在是 不太多,就算是看別人寫的關於Laravel的特色,和過往PHP開發到底又怎樣差異, 也還是對我而言有些無感。因此決定從我自身角度來介紹Laravel。
Laravel又被人稱為 為網頁藝術家創造的框架 。那是因為,相較於以前PHP 將頁面資料與邏輯代碼混合寫在一起的 義大利麵寫法 , Laravel是類似 Django這樣廣義的MVC框架。也就是將頁面資料與邏輯代碼分開。
那摸到底為神ㄇ要用Laravel? 我簡單列出以下以點:
- 首先,PHP還沒有死亡。實際上PHP在許多地方還是可以看的到。著名的 WordPress、Drupal都是用PHP寫的。在許多與網頁相關應用方面,PHP成熟且 易用,這也讓使用PHP多了一個理由。過去寫過很短的PHP還活 著 可以去參考一下wwww。
- 相比WordPress的易用、易上手,Laravel提供更高度的彈性。
- 遵守Laravel一些基本的開發原則,原始碼更容易維護。(就是有些人一樣能 寫成義大利麵….)
- Laravel提供多個極為好用的基本可選用功能。包含身份驗證、資料驗證、資 料庫分離、ORM、資源控制器、上傳等等等。
30天成爲Laravel萌新(第0天) - 前言
今年有一段時間,短暫的1~2周,因為一些原因學習了Laravel,這次我事後的學習筆記。
Laravel是一個流行的PHP開發框架。不同於熱門的Drupal、WordPress.org, Laravel更像是Node.js的Express.js、Python的Django等等廣義的MVC框架。當然,他也可以快速的發展成CMS。
Laravel目前也發展到6.0,不過接下來幾天的內容會以5.8為主。Laravel也有完整的開發文檔。儘管有些翻譯仍然不完全,但也有多種語言的翻譯。首先,介紹一些學習資源:
- Laravel 官方網站
- Laravel.tw 中文站 文檔最新只到5.3版本
- Laravel中文學院
預計未來內容撰寫的方向:
- 關於Laravel
- 建立Laravel開發環境
- 使用Composer安装
- 使用laradock建立環境
- 認識artisan
- Route - MVC
- 會員系統
- Template
- Controller
- 文章資料庫 - Database Schema
- 商品資料庫 - 驗證資料
- 上傳檔案
- API
Tag: java
JShell? 程式語言越來越像Lisp
JShell
JShell是在JAVA 9之後引入進來。 我是在翻JAVA 12(OpenJDK)工具的時後看到的,雖然好像長期支援(LTS)版本是JAVA 11… 算了,反正目前系統裝有JAVA 8(1.8)和手動下載的JAVA 12。
每個版本之前是有差異的。同樣都是編譯成class檔案,但在不同JRE下可能無法執行。上學期就有學弟妹來問,結果A編譯的B不能執行,只能由B來編譯分享給A。(向後兼容)
TK 之後再來說些這幾天看了幾個開源專案後對於這版本的事情的想法。
現在說回來 JShell。
Tag: javascript
你可能都不瞭解的JS變數祕密 - 一文了解無宣告、var、let、const變數細節
本文優先發表於ALPHAcamp
前言
這類問題我被問到不止一次。不得不說JS的變數蠻特別的。新手菜鳥會問,連老鳥也都常搞錯。
更甚者…近日更是聽到一個自稱有五年經驗的軟體工程師稱: var
宣告的變數是全域變數
我知道我身邊的朋友,也有不少可能不清楚,或是沒探究這麼深入,相關文章有但不多。 於是乎…感覺我再想拖延,也應該把這篇文章寫出來。
這篇文章對於你寫更好的JS並沒有太多幫助,有許多部分平常根本不太會用到。但卻是非常基礎的概念。儘管不知道,通常按照當前常見規範,程式碼亦不會太糟糕。
這篇文章主要是從一份回覆修改而來。
變數的生存範圍
無關鍵字賦值、var
宣告、let
宣告最大的差別在於生存區域的不同。無關鍵字賦值 - 這意味著全域變數的宣告,當然你在全域範圍使用var
/let
宣告也是全域的。只是無關鍵字可能引發意外的情況,像是你預期變數應該是函數區域的:
function printG(){
g = 1
console.log(`printG: `, g)
}
printG() // => printG: 1
console.log(`Global G:`, g) // => Global G: 1
上例中全域情況也取得到在printG
函數裏定義的全域變數。這相當於你顯式定義g
於全域:
什麼是jQuery?前端框架盛行還需要JavaScript函式庫嗎?
- 本篇首發於ALPHACamp Blog(2020-03-25)
- 2020-04-05 更新
前端框架盛行還需要JavaScript函式庫嗎?
如果你想透過本篇文章,直截了當的知道該不該使用jQuery,可能要讓你失望了。
很難說到底應不應該使用jQuery,可能在某些情況下使用jQuery仍是最好的選擇。即使是現在的流行的三大前端框架,也是依照情況與需求使用。
了解jQuery到底是什麼、前端框架是什麼,會比要不要使用jQuery更有意義。本篇帶你看看我了解的jQuery前世今生
瀏覽器發展歷史上出現過幾次大混戰。儘管後來有了W3C(全球資訊網協會/World Wide Web Consortium)進行標準的制定,各個瀏覽器廠商仍開發各自瀏覽器的「特異功能」吸引使用者與開發者。這些特異功能促使了瀏覽器標準的進步,也照成了開發上的困難。
為解決網路應用中不同平台、技術和開發者帶來的不相容問題,保障網路資訊的順利和完整流通,全球資訊網協會制定了一系列標準並督促網路應用開發者和內容提供者遵循這些標準。1
2006年1月,jQuery
推出了第一個版本,隨後變成為了最受歡迎的JavaScript函式庫之一2。附帶一題,我們現在熟悉的HTML5標準是在2014年正式定稿推出3,即使是現在,每年瀏覽器的標準也是快速地在變動。在後文我會在說明為何開頭我想提到這兩件事情。
什麼是jQuery? 從常見功能來看
jQuery之所以受歡迎,是因為他提供了一系列簡單方便,而且兼容的操作,省去開發人員的部份煩惱。這裡舉幾個簡單的例子:
選取器
現代瀏覽器標準分成三個部份:
- HTML
- CSS
- ECMAScript / JavaScript
其中第三個就是常聽到的JS/Javascript。目前更正式的名稱是ECMAScript,也是今天主要jQuery主要的基礎。不同於瀏覽器腳本(Script)的標準,CSS2在以前就提供豐富易用的選擇器,讓設計人員撰寫樣式表(stylesheet)
7天搞懂JS進階議題(day07-1)-系列目錄
寫了這麼久的JS,你還在物件之前的時代嗎?只有資料、函式可以用,破破的抽象化,不會難以維護?儘管JS起初並不以物件導向設計,但透過原形鏈設計,其仍然可以具有好維護的物件導向特色。本系列從最基礎的this,深入ES6之後的class。
系列目錄
- 第0天 - Oh…開始了……
- 第1天 - this & bind: 你不能不知道的
- 第2天 - new & factory: 如何建立一個新物件
- 第3天 - Function & Object: 關於Prototype Chain繼承
- 第4天 - Class & constructor: 吃語法糖別噎到
- 第5天 - getter & setter: 屬性描述器
- 第6天 - yield & yield*: 生成器
- 第7天 - Symbol & Proxy: 以前沒有的
- 第8天 - Closure & Private:番外短篇 隱私成員
各章節簡介
(第1天) this & bind: 你不能不知道的
物件導向必不可少的,就是如何引用參考自己。
要是自己的錢包都拿不出來,你要怎買個冰棒?
(第2天) new & factory: 如何建立一個新物件
既然是要來物件導向,當然要先來學怎麼建立物件。本節帶你看看如何建立一個新的物件。
(第3天) Function & Object: 關於Prototype Chain繼承
在class
之前,必須了解的prototype chain。
JavaScript 是個沒有實做 class 關鍵字的動態語言,所以會對那些基於類別(class-based)語言(如 Java 或 C++)背景出身的開發者來說會有點困惑。(在 ES2015 有提供 class 關鍵字,但那只是個語法糖,JavaScript 仍然是基於原型(prototype-based)的語言)。
(第4天) Class & constructor: 吃語法糖別噎到
7天搞懂JS進階議題(day08) Closure & Private:番外短篇 隱私成員
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
這篇是臨時起意補的一篇短篇,用於示例如何模擬私有屬性。儘管這可能不是JS主流設計思想方法,但知道相信對你沒壞事。
在第5天-getter & setter: 屬性描述器,曾經看過這樣的例子:
var 神崎家 = class {
constructor(name){
this.__name = `神崎・${name}`;
}
static born(name){
return new 神崎家(name)
}
set name(new_name){
this.__name = `神崎・${new_name}`;
}
get name(){
let first_name = this.__name.substr(0,2),
last_name = this.__name.substr(3, this.__name.length + 1);
return `${first_name}・H・${last_name}`
}
introduce(){
console.log(`Hi~ My name is ${this.name}`)
}
rename(new_name){
this.name = new_name;
return this.name;
}
}
可能會有人問…為什麼__name
不直接這樣寫就好:
7天搞懂JS進階議題(day07)-Symbol & Proxy: 以前沒有的
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
最後一天,來看兩個特別的類別–Symbol
和Proxy
。
以前的物件(object)
key只能是字串
在以前,物件的key一定要是基礎字串,不過因為JS語法糖的關係可以不用加引號:
var obj = {
"name": "World",
}
// 等價於
var obj = {
name: "World",
}
如果不是呢?
7天搞懂JS進階議題(day06)-yield & yield*: 生成器
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
今天會往物件導向外頭邁出一步。是的,到昨天已經差不多把JS物件導向介紹的差不多了。那今天的主題是什麼呢?生成器(generator)。這個類型的建立與使用,和普通的JS類別有些不同,來看看吧!
生成器(generator)
什麼是生成器(generator)?簡單說就是一個 序列工廠 ,你跟他要東西他就給你東西,直到原料不足無法生產。
7天搞懂JS進階議題(day05)-getter & setter: 屬性描述器
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
有了物件然後呢?
今天來說說關於成員(field/attribute/member)背後的屬性。
getter & setter
有一個家族– 神崎家 ,生了一個小孩叫 アリア ,但生下後被 りこ 偽裝,被發現後重新命名為 アリア。
如有雷同存屬巧合
7天搞懂JS進階議題(day04)-Class & Constructor: 吃語法糖別噎到
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
現在你應該已經有發車前的基礎準備了。繫緊安全帶,撈思跡要踩油門加速了!
關於class
這個關鍵字,JS將其作為保留字好一段時間,直至ES6標準的制定,再經過瀏覽器漫長的實做,至今才有class
的語法糖可以使用。
7天搞懂JS進階議題(day03)-Function & Object: 關於Prototype Chain繼承
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
Prototype
有人說:「在寫JVM語言前,你必須先是Java程式開發人員」。
在寫TypeScript前,你還是得先會JavaScript。
[在進階一點(誤)] 在寫任何程式語言前,比必須先會組合語言
在class
之前,必須了解的prototype chain。
好拉,上面引言最後一個是來亂的,但是在正式開始寫JS的之前,你還是比需要有 prototype chain 的概念。
JavaScript 是個沒有實做 class 關鍵字的動態語言,所以會對那些基於類別(class-based)語言(如 Java 或 C++)背景出身的開發者來說會有點困惑。(在 ES2015 有提供 class 關鍵字,但那只是個語法糖,JavaScript 仍然是基於原型(prototype-based)的語言)。1
本節不會立馬就進到原形鏈,在那之前,會先來看看Python、Ruby這類OOP裡同樣有的東西。如果你不是從那來的,可以直接到原形鏈去看。但建議多少還是看一點,有些在JS還蠻常用的。
7天搞懂JS進階議題(day02)-new & factory: 如何建立一個新物件
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
既然是要來物件導向,當然要先來學怎麼建立物件。本節帶你看看如何建立一個新的物件。
直接建立
obj0 = {
name: "World",
hello(){console.log(`Hello, ${this.name}`)},
}
obj0.hello();
工廠模式
可以直接建立物件,也可以透過工廠模式建立並初始化物件。
7天搞懂JS進階議題(day01)-This & Bind: 你不能不知道的
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
this
物件導向必不可少的,就是如何引用參考自己。
要是自己的錢包都拿不出來,你要怎買個冰棒?
寫過C++、Java對於this
這個關鍵字應該不陌生,雖然JS的this
有著很大的不同,但再說明之前,為了來自其他地方的同鞋,容我再多提幾個相對應的例子。
來自Python、Ruby、Rust的朋友
你們可能習慣看到的是self
。
※ Note: Python可以不使用self
;Rust必須顯示宣告self
或&self
;Ruby則比較像是JS是[隱含宣告](#隱含宣告 vs 明確宣告)self
來自VB和VB.NET的朋友
你們會看到的是Me
、My
。
7天搞懂JS進階議題(day00)-Oh...開始了......
這系列文章內容會是什麼?
本篇整理了7項ES6之後的相關進階特性(this可能除外),包含:
- this & bind
- Prototype chain
- class & object
- getter, setter
- attr
- Symbol & Proxy
- yield
概述
寫了這麼久的JS,你還在物件之前的時代嗎?只有資料、函式可以用,破破的抽象化,不會難以維護?
儘管JS起初並不以物件導向設計,但透過原形鏈設計,其仍然可以具有好維護的物件導向特色。
目前已經被證實的是,物件導向程式設計推廣了程式的靈活性和可維護性,並且在大型專案設計中廣為應用。 — from Wikipedia
本系列從最基礎的this
,深入ES6之後的class
。包含:
this
物件導向必不可少。
關於隱式宣告與顯示宣告、特殊變數。
prototype
在class
之前,必須了解的prototype chain
class class和new。從constructor開始。
getter, setter 有了物件然後呢?關於成員(field/attribute/member)。
【微更】你可能沒看過得Python - Callable(續)
【微更】Callable的實現
之前我實現了Callable Class,當時對於CallableWrapper
的實現如下:
class CallableWrapper:
def __init__(self, wrap):
self.wrap = wrap
def __call__(self, f=None, *args):
if f == None:
return self.wrap
return CallableWrapper(f(self.wrap, *args))
這個實現有一些缺憾,CallableWrapper
的__call__
回傳值,也是CallableWrapper
。而CallableWrapper
本身預期被呼叫,這導致結果不能直接使用,需要多給一次空呼叫(result()
)。舉例來說:
arr = CallableWrapper([1,2,3,4,5,6,])
arr # => <__main__.CallableWrapper object at 0x7f2102f00668>
# arr(sum) + 10 # You can't do this, because CallableWrapper can't add integer
arr(sum)() + 10
上例中,並不能直接寫arr(sum) + 10
,要寫arr(sum)() + 10
。這感覺有點脫褲子放屁阿…
基於此想法,我改寫成:
Tag: jdk
JShell? 程式語言越來越像Lisp
JShell
JShell是在JAVA 9之後引入進來。 我是在翻JAVA 12(OpenJDK)工具的時後看到的,雖然好像長期支援(LTS)版本是JAVA 11… 算了,反正目前系統裝有JAVA 8(1.8)和手動下載的JAVA 12。
每個版本之前是有差異的。同樣都是編譯成class檔案,但在不同JRE下可能無法執行。上學期就有學弟妹來問,結果A編譯的B不能執行,只能由B來編譯分享給A。(向後兼容)
TK 之後再來說些這幾天看了幾個開源專案後對於這版本的事情的想法。
現在說回來 JShell。
Tag: jquery
什麼是jQuery?前端框架盛行還需要JavaScript函式庫嗎?
- 本篇首發於ALPHACamp Blog(2020-03-25)
- 2020-04-05 更新
前端框架盛行還需要JavaScript函式庫嗎?
如果你想透過本篇文章,直截了當的知道該不該使用jQuery,可能要讓你失望了。
很難說到底應不應該使用jQuery,可能在某些情況下使用jQuery仍是最好的選擇。即使是現在的流行的三大前端框架,也是依照情況與需求使用。
了解jQuery到底是什麼、前端框架是什麼,會比要不要使用jQuery更有意義。本篇帶你看看我了解的jQuery前世今生
瀏覽器發展歷史上出現過幾次大混戰。儘管後來有了W3C(全球資訊網協會/World Wide Web Consortium)進行標準的制定,各個瀏覽器廠商仍開發各自瀏覽器的「特異功能」吸引使用者與開發者。這些特異功能促使了瀏覽器標準的進步,也照成了開發上的困難。
為解決網路應用中不同平台、技術和開發者帶來的不相容問題,保障網路資訊的順利和完整流通,全球資訊網協會制定了一系列標準並督促網路應用開發者和內容提供者遵循這些標準。1
2006年1月,jQuery
推出了第一個版本,隨後變成為了最受歡迎的JavaScript函式庫之一2。附帶一題,我們現在熟悉的HTML5標準是在2014年正式定稿推出3,即使是現在,每年瀏覽器的標準也是快速地在變動。在後文我會在說明為何開頭我想提到這兩件事情。
什麼是jQuery? 從常見功能來看
jQuery之所以受歡迎,是因為他提供了一系列簡單方便,而且兼容的操作,省去開發人員的部份煩惱。這裡舉幾個簡單的例子:
選取器
現代瀏覽器標準分成三個部份:
- HTML
- CSS
- ECMAScript / JavaScript
其中第三個就是常聽到的JS/Javascript。目前更正式的名稱是ECMAScript,也是今天主要jQuery主要的基礎。不同於瀏覽器腳本(Script)的標準,CSS2在以前就提供豐富易用的選擇器,讓設計人員撰寫樣式表(stylesheet)
Tag: js
你可能都不瞭解的JS變數祕密 - 一文了解無宣告、var、let、const變數細節
本文優先發表於ALPHAcamp
前言
這類問題我被問到不止一次。不得不說JS的變數蠻特別的。新手菜鳥會問,連老鳥也都常搞錯。
更甚者…近日更是聽到一個自稱有五年經驗的軟體工程師稱: var
宣告的變數是全域變數
我知道我身邊的朋友,也有不少可能不清楚,或是沒探究這麼深入,相關文章有但不多。 於是乎…感覺我再想拖延,也應該把這篇文章寫出來。
這篇文章對於你寫更好的JS並沒有太多幫助,有許多部分平常根本不太會用到。但卻是非常基礎的概念。儘管不知道,通常按照當前常見規範,程式碼亦不會太糟糕。
這篇文章主要是從一份回覆修改而來。
變數的生存範圍
無關鍵字賦值、var
宣告、let
宣告最大的差別在於生存區域的不同。無關鍵字賦值 - 這意味著全域變數的宣告,當然你在全域範圍使用var
/let
宣告也是全域的。只是無關鍵字可能引發意外的情況,像是你預期變數應該是函數區域的:
function printG(){
g = 1
console.log(`printG: `, g)
}
printG() // => printG: 1
console.log(`Global G:`, g) // => Global G: 1
上例中全域情況也取得到在printG
函數裏定義的全域變數。這相當於你顯式定義g
於全域:
7天搞懂JS進階議題(day07-1)-系列目錄
寫了這麼久的JS,你還在物件之前的時代嗎?只有資料、函式可以用,破破的抽象化,不會難以維護?儘管JS起初並不以物件導向設計,但透過原形鏈設計,其仍然可以具有好維護的物件導向特色。本系列從最基礎的this,深入ES6之後的class。
系列目錄
- 第0天 - Oh…開始了……
- 第1天 - this & bind: 你不能不知道的
- 第2天 - new & factory: 如何建立一個新物件
- 第3天 - Function & Object: 關於Prototype Chain繼承
- 第4天 - Class & constructor: 吃語法糖別噎到
- 第5天 - getter & setter: 屬性描述器
- 第6天 - yield & yield*: 生成器
- 第7天 - Symbol & Proxy: 以前沒有的
- 第8天 - Closure & Private:番外短篇 隱私成員
各章節簡介
(第1天) this & bind: 你不能不知道的
物件導向必不可少的,就是如何引用參考自己。
要是自己的錢包都拿不出來,你要怎買個冰棒?
(第2天) new & factory: 如何建立一個新物件
既然是要來物件導向,當然要先來學怎麼建立物件。本節帶你看看如何建立一個新的物件。
(第3天) Function & Object: 關於Prototype Chain繼承
在class
之前,必須了解的prototype chain。
JavaScript 是個沒有實做 class 關鍵字的動態語言,所以會對那些基於類別(class-based)語言(如 Java 或 C++)背景出身的開發者來說會有點困惑。(在 ES2015 有提供 class 關鍵字,但那只是個語法糖,JavaScript 仍然是基於原型(prototype-based)的語言)。
(第4天) Class & constructor: 吃語法糖別噎到
7天搞懂JS進階議題(day08) Closure & Private:番外短篇 隱私成員
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
這篇是臨時起意補的一篇短篇,用於示例如何模擬私有屬性。儘管這可能不是JS主流設計思想方法,但知道相信對你沒壞事。
在第5天-getter & setter: 屬性描述器,曾經看過這樣的例子:
var 神崎家 = class {
constructor(name){
this.__name = `神崎・${name}`;
}
static born(name){
return new 神崎家(name)
}
set name(new_name){
this.__name = `神崎・${new_name}`;
}
get name(){
let first_name = this.__name.substr(0,2),
last_name = this.__name.substr(3, this.__name.length + 1);
return `${first_name}・H・${last_name}`
}
introduce(){
console.log(`Hi~ My name is ${this.name}`)
}
rename(new_name){
this.name = new_name;
return this.name;
}
}
可能會有人問…為什麼__name
不直接這樣寫就好:
7天搞懂JS進階議題(day07)-Symbol & Proxy: 以前沒有的
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
最後一天,來看兩個特別的類別–Symbol
和Proxy
。
以前的物件(object)
key只能是字串
在以前,物件的key一定要是基礎字串,不過因為JS語法糖的關係可以不用加引號:
var obj = {
"name": "World",
}
// 等價於
var obj = {
name: "World",
}
如果不是呢?
7天搞懂JS進階議題(day06)-yield & yield*: 生成器
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
今天會往物件導向外頭邁出一步。是的,到昨天已經差不多把JS物件導向介紹的差不多了。那今天的主題是什麼呢?生成器(generator)。這個類型的建立與使用,和普通的JS類別有些不同,來看看吧!
生成器(generator)
什麼是生成器(generator)?簡單說就是一個 序列工廠 ,你跟他要東西他就給你東西,直到原料不足無法生產。
7天搞懂JS進階議題(day05)-getter & setter: 屬性描述器
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
有了物件然後呢?
今天來說說關於成員(field/attribute/member)背後的屬性。
getter & setter
有一個家族– 神崎家 ,生了一個小孩叫 アリア ,但生下後被 りこ 偽裝,被發現後重新命名為 アリア。
如有雷同存屬巧合
7天搞懂JS進階議題(day04)-Class & Constructor: 吃語法糖別噎到
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
現在你應該已經有發車前的基礎準備了。繫緊安全帶,撈思跡要踩油門加速了!
關於class
這個關鍵字,JS將其作為保留字好一段時間,直至ES6標準的制定,再經過瀏覽器漫長的實做,至今才有class
的語法糖可以使用。
7天搞懂JS進階議題(day03)-Function & Object: 關於Prototype Chain繼承
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
Prototype
有人說:「在寫JVM語言前,你必須先是Java程式開發人員」。
在寫TypeScript前,你還是得先會JavaScript。
[在進階一點(誤)] 在寫任何程式語言前,比必須先會組合語言
在class
之前,必須了解的prototype chain。
好拉,上面引言最後一個是來亂的,但是在正式開始寫JS的之前,你還是比需要有 prototype chain 的概念。
JavaScript 是個沒有實做 class 關鍵字的動態語言,所以會對那些基於類別(class-based)語言(如 Java 或 C++)背景出身的開發者來說會有點困惑。(在 ES2015 有提供 class 關鍵字,但那只是個語法糖,JavaScript 仍然是基於原型(prototype-based)的語言)。1
本節不會立馬就進到原形鏈,在那之前,會先來看看Python、Ruby這類OOP裡同樣有的東西。如果你不是從那來的,可以直接到原形鏈去看。但建議多少還是看一點,有些在JS還蠻常用的。
7天搞懂JS進階議題(day02)-new & factory: 如何建立一個新物件
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
既然是要來物件導向,當然要先來學怎麼建立物件。本節帶你看看如何建立一個新的物件。
直接建立
obj0 = {
name: "World",
hello(){console.log(`Hello, ${this.name}`)},
}
obj0.hello();
工廠模式
可以直接建立物件,也可以透過工廠模式建立並初始化物件。
7天搞懂JS進階議題(day01)-This & Bind: 你不能不知道的
本系列文章討論JS 物件導向設計相關的特性。 不含CSS,不含HTML!
建議先有些JS基礎再繼續閱讀。
你也可以看看從零開始遲來的Web開發筆記
雖然是「7天寫作松」挑戰,但同樣可以視為系列後續文章
No CSS! No HTML! No Browser!
Just need programming language
this
物件導向必不可少的,就是如何引用參考自己。
要是自己的錢包都拿不出來,你要怎買個冰棒?
寫過C++、Java對於this
這個關鍵字應該不陌生,雖然JS的this
有著很大的不同,但再說明之前,為了來自其他地方的同鞋,容我再多提幾個相對應的例子。
來自Python、Ruby、Rust的朋友
你們可能習慣看到的是self
。
※ Note: Python可以不使用self
;Rust必須顯示宣告self
或&self
;Ruby則比較像是JS是[隱含宣告](#隱含宣告 vs 明確宣告)self
來自VB和VB.NET的朋友
你們會看到的是Me
、My
。
7天搞懂JS進階議題(day00)-Oh...開始了......
這系列文章內容會是什麼?
本篇整理了7項ES6之後的相關進階特性(this可能除外),包含:
- this & bind
- Prototype chain
- class & object
- getter, setter
- attr
- Symbol & Proxy
- yield
概述
寫了這麼久的JS,你還在物件之前的時代嗎?只有資料、函式可以用,破破的抽象化,不會難以維護?
儘管JS起初並不以物件導向設計,但透過原形鏈設計,其仍然可以具有好維護的物件導向特色。
目前已經被證實的是,物件導向程式設計推廣了程式的靈活性和可維護性,並且在大型專案設計中廣為應用。 — from Wikipedia
本系列從最基礎的this
,深入ES6之後的class
。包含:
this
物件導向必不可少。
關於隱式宣告與顯示宣告、特殊變數。
prototype
在class
之前,必須了解的prototype chain
class class和new。從constructor開始。
getter, setter 有了物件然後呢?關於成員(field/attribute/member)。
Tag: jshell
JShell? 程式語言越來越像Lisp
JShell
JShell是在JAVA 9之後引入進來。 我是在翻JAVA 12(OpenJDK)工具的時後看到的,雖然好像長期支援(LTS)版本是JAVA 11… 算了,反正目前系統裝有JAVA 8(1.8)和手動下載的JAVA 12。
每個版本之前是有差異的。同樣都是編譯成class檔案,但在不同JRE下可能無法執行。上學期就有學弟妹來問,結果A編譯的B不能執行,只能由B來編譯分享給A。(向後兼容)
TK 之後再來說些這幾天看了幾個開源專案後對於這版本的事情的想法。
現在說回來 JShell。
Tag: keyboard-shortcut
【工欲善其事,必先利其器】那些你可能不知道的Firefox Browser快速鍵
「工欲善其事,必先利其器 」現在人們最常使用的工具之一,大概就是瀏覽器。能夠了解工具的使用,對於生活處理事物的方式,也會有很明顯的幫助。過去我也研究寫過「Windows快速鍵」、「Libre Office使用方式」、「Google搜尋的隱藏技巧」、「簡單的Jupyter Notebook操作學習心得 」,或許未來會更新更新,再公開來分享給大家。
以前沒有直接寫的習慣。全寫在ODT裡面。 很久沒碰Windows了,還不知道有沒有改很多,大概也很多忘了。Libre Office可能也要更新下作法。 重點是,我不是很記得內容寫了啥?直接公開總覺得會有點害羞(。◕‿◕。)。
這個分享主要是在Linux Mint 19.2上做測試的。或許不同作業系統上有不同操作方式。可能要自己在嘗試看看。
題外話
透過DuckDuckGo和Google搜尋,能夠得到的快速答案也蠻不一樣的,就我看來,DuckDuckGo的更為準確。
參考資料
儘管以設計思考的角度來看,工具的設計應該要讓人好理解、好使用。但很難有如此完美的設計,因此一份完整的使用手冊非常必要。
Firefox Browser是Mozilla的Firefox系列產品的其中一個主力產品。這個系列包含Firefox Browser、Send、Monitor、Lockwise和收購的Pocket。
本篇文章主要參考http://mzl.la/16NAhwz ,以及https://moztw.org/firefox/support/keyboard/ (該頁面在後來測試中無法瀏覽)。你也可以參考以下內容(儘管似乎有部份陳舊):
重要快速鍵
Tag: keycloak
【用Keycloak學習身份驗證與授權】系列目錄
本系列同樣發表於iThome體人賽 - 用Keycloak學習身份驗證與授權。
本頁後面還有一些小後記喔~
- 【用Keycloak學習身份驗證與授權01】Quick Start(1)
- 【用Keycloak學習身份驗證與授權02】Quick Start(2)
- 【用Keycloak學習身份驗證與授權03】淺談身份驗證與授權(1)
- 【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
- 【用Keycloak學習身份驗證與授權05】什麼是Keycloak
- 【用Keycloak學習身份驗證與授權06】Keycloak的替代品
- 【用Keycloak學習身份驗證與授權07】什麼是OAuth
- 【用Keycloak學習身份驗證與授權08】OAuth 2
- 【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
- 【用Keycloak學習身份驗證與授權10】深入OAuth 2
【用Keycloak學習身份驗證與授權33】Device Code(4)
這次應用使用PySide
來實現界面;qrcode
來產生需要的QR Code;並使用requests
來與身份驗證與授權伺服器的API溝通。現在透過pip
進行安裝需要的packages。
pip install PySide6 requests qrcode
其實本來可以考慮用electron.js,但是基於一些考量,最後決定使用PySide。
在昨天,透過Qt Designer建立了兩個需要的使用者界面,今天來實現邏輯部分。
建立Widget
在之前所設計的ui檔案分別是:example-device-code-app.ui
和login-dialog.ui
。這部分會分別將這兩份載入到類別內使用。所以同樣來建立兩個Widgets:ExampleDeviceCodeApp
和loginDialog
。
【用Keycloak學習身份驗證與授權32】Device Code(3)
本文接續device code(2)
現在已經知道了Device Code的登入流程了,那麼實際應用起來是怎麼樣的呢? 本片來實現一個可以使用Device Code Flow登入的應用。
使用者界面設計
首先,與「快速開始」應用相同,同樣需要一個顯式使用者資訊的地方,以及登入與登出的按鈕。
是的非常簡單。但悄悄先回到RCF8628,有一部分描述使用者界面的範本。該界面建議包含:操作說明、登入連接和user_code
。
【用Keycloak學習身份驗證與授權31】Open ID Connect & Social Login(2)
Keycloak Open-Id Connect
其實除了使用GitHub等社群帳號登入外,Keycloak也可以作爲Open-Id登入的提供者(Provider)。接著需要使用Keycloak本身來實現社群帳號登入,因爲這樣子可以看到更多細節。
建立新的Realm
現在,需要一個新的帳號系統。你可以在建立一個Keycloak伺服器,或是建立一個新的Realm。不同Realm的帳號系統是獨立不相干擾的,所以這裏就先建議一個新的Realm – G00gle
。
【用Keycloak學習身份驗證與授權30】Open ID Connect & Social Login(1)
因為略過了一些JWT格式細節分析。所以這部分也有部分不會好好提到
到目前爲止,爲何不同應用可以使用同一個帳號登入,已經在說明Client解釋過。這是在相關系統的應用下,那麼…沒直接相關的系統呢?譬如:使用Google登入。像這種使用不同帳號系統登入的方式,在Keycloak分成兩種。第三方系統登入,這篇僅會說明與 OAuth / Open-Id 相關的一種。如果你使用過Firebase、Auth0等服務,或是看過使用Google、Facebook、Microsoft、GitHub帳號登入的應用,對就是這類。這種社群帳號登入(Social Login)的方式,與前幾天提到的內容相關,而且可以在Keycloak實現。
鐵人賽只會實現而已,一些細節和更多的範例並不會提到。 (雖然原本就計劃寫)
Social Login 社群帳號登入
以GitHub帳號登入
【用Keycloak學習身份驗證與授權29】JWT權杖格式介紹(1)
總覺得…直接開始說明什麼是JWT格式來著。但感覺這樣會很無聊,不如我們從已經拿到的Token來看吧!
至今爲止,除了存取權杖(access_token
)、更新權杖(refresh_token
)外,還拿到過識別權杖(id_token
)。仔細看三者,都有兩個「.
」可以將權杖分成三個部份。
這些權杖都可以透過JWT.io去解析。總之先透過Password Grant Flow取得access_token
和refresh_token
,或是透過「快速開始」應用取得id_token
。
【用Keycloak學習身份驗證與授權28】Role
在帳號系統下,除了帳號本身與帳號群組外,通常還存在一個非常重要的部分–角色(Role),更有基於角色的存取授權方式(RBAC)。
寫到有點累了,沒意外的話之後是會提到RBAC
帳號如果代表一個人,這個人可能有多個角色身份。可能是個老師、主任、校長;可能是爸媽、叔姨;可能是員工、部長、處長、老闆,且可能有一群人擁有同一種角色。角色和帳號群組有點像,但在Keycloak是兩個概念。除此之外,在Keycloak還分成兩類型角色– Realm Roles 和 Client Roles 。
建立 Realm Roles
首先,你可以建立Realm共用的角色,像是員工、老闆等等較爲通用的角色。
點選在 Realm 選單下的 Roles ,然後再點選 Add Role :
【用Keycloak學習身份驗證與授權27】User & Claim & Profile
接著來看看爲什麼更新帳號資訊,在「快速開始」會有那些變化。
這與client scope和claim有關。關於後者之後會在詳細說說,而目前就先了解一下這個現象發生的原因。
首先,在我們取得token
的時候曾申明需要的scope
爲openid profile email
。其中profile
這個scope爲這次變化的主要原因。
來到Keycloak管理選單下的 Client Scopes ,然後找到 profile 。
接著將頁籤切換到 Mappers , 你會看見一堆與 User Attribute 有關的設定。
【用Keycloak學習身份驗證與授權26】User & Group
帳號(User)
基本訊息
接著來看看與帳號有關的設定。
在之前,已經建立過一帳號–bob
。過去學習實驗,也都以bob驗證身份。接著我們要來更新一下這個帳號。
首先看一下基本訊息:
來添加一些資訊:
- Email:
bob@fake.email
- First Name:
Bob
- Last Name:
Lee
- Email Verified:
ON
此外,可以要求使用者在必須做一些事情,譬如:驗證信箱、更新密碼、更新個人資訊等。
再次登入到應用–「快速開始」,可以看到有一些訊息也有些不同了。
【用Keycloak學習身份驗證與授權24】Clients
Client與一些安全相關的設定
在OAuth架構下的Client(客戶端)可以想象成是一個一個的應用程式。到目前爲止也已經建立過幾個Client:
這些Client有著自己的規則、資源、授權方式等。
可以複寫一些Realm的設定,包含產生存取權杖的方式。像是認爲RS256
簽名不夠,需要使用到RS512
:
【用Keycloak學習身份驗證與授權23】Realm
Realm,中文或許會翻作「域」,但基本很像是程式開發上,語言層面提供的包(package
)或是命名空間(namespace
)。或者可能可以更貼切的說是工作空間(workspace)。
你可以想象就像是一個企業、部門或是其他組織。有著相同的一些規範,同事們在同樣地工作空間生活、工作。但不同的企業、部門或是其他組織,可能會有類似的規範,但兩者不互相影響。
會特別有這個概念,是因爲Keycloak是可以建立多個Realm的。也就是,在同一間公司內,不同部門都可以有自己的Realm,制定部門自己的管理規範。或是特別爲外部客戶建立一個Realm,並制定特殊規範。
不同的Realm內,有著自己的帳號系統、密碼規範政策等。利用這個特性,之後也會用來更清楚的理解Open-Id。
你也可以同樣簡單視爲一個帳號資料庫、身份驗證伺服器。特別的是在會話成立期間,可以不需要再進行一次驗證,而這部分,會在提到Client時在多做說明。
如何建立一個Realm
要建立一個Realm是非常簡單。在之前也建立過「quick-start」這個Realm。也幾乎就只需要給個名字而已。
【用Keycloak學習身份驗證與授權22】Keycloak使用基本概念:前導
【用Keycloak學習身份驗證與授權21】在Flow這段小旅途外的風景
在這一小段路中介紹了Password Flow、Implicit Flow、Code Flow、Refresh Token Flow、Client Credentials Flow、PKCE、Device Code Flow。有些模式已經被發現可能有潛在風險,有些模式無法單獨使用。這或許還不是全部,至少到現在為止都還沒有提到過金融級應用Flow–CIBA。
Client Initiated Backchannel Authentication Profile(CIBA)
本小節也不會詳細介紹CIBA(Client Initiated Backchannel Authentication)。儘管CIBA現在階段還只是草案(Draft),但在Keycloak v15版本中已經可以使用。大概也已經確實有一些應用使用。
為什麼你不該繼續使用Implicit Flow?
在談到Implicit Flow時候,提到過:
將存取權杖暴露在使用者面前也不是非常好的做法
【用Keycloak學習身份驗證與授權20】Device Code(2)
光要完成這個範例就花了幾乎整整一天
做完後決定…來拆篇這第二部份,將有部份內容會在【實戰篇】展開。 今天就先來看看成果。
成果發表
【用Keycloak學習身份驗證與授權19】Device Code(1)
+----------+ +----------------+
| |>---(A)-- Client Identifier --->| |
| | | |
| |<---(B)-- Device Code, ---<| |
| | User Code, | |
| Device | & Verification URI | |
| Client | | |
| | [polling] | |
| |>---(E)-- Device Code --->| |
| | & Client Identifier | |
| | | Authorization |
| |<---(F)-- Access Token ---<| Server |
+----------+ (& Optional Refresh Token) | |
v | |
: | |
(C) User Code & Verification URI | |
: | |
v | |
+----------+ | |
| End User | | |
| at |<---(D)-- End user reviews --->| |
| Browser | authorization request | |
+----------+ +----------------+
Figure 1: Device Authorization Flow
The device authorization flow illustrated in Figure 1 includes the
following steps:
(A) The client requests access from the authorization server and
includes its client identifier in the request.
(B) The authorization server issues a device code and an end-user
code and provides the end-user verification URI.
(C) The client instructs the end user to use a user agent on another
device and visit the provided end-user verification URI. The
client provides the user with the end-user code to enter in
order to review the authorization request.
Device Code Flow這個與前面幾個特別不一樣。在之前,以往都是從登入開始,然後跳轉頁面回到App(Client)。也就是通常先有的是前端通訊,然後才是後端通信。
【用Keycloak學習身份驗證與授權18】PKCE
+-------------------+
| Authz Server |
+--------+ | +---------------+ |
| |--(A)- Authorization Request ---->| | |
| | + t(code_verifier), t_m | | Authorization | |
| | | | Endpoint | |
| |<-(B)---- Authorization Code -----| | |
| | | +---------------+ |
| Client | | |
| | | +---------------+ |
| |--(C)-- Access Token Request ---->| | |
| | + code_verifier | | Token | |
| | | | Endpoint | |
| |<-(D)------ Access Token ---------| | |
+--------+ | +---------------+ |
+-------------------+
Figure 2: Abstract Protocol Flow
PKCE模式
說穿了PKCE是基於Code flow的安全強化版。在整個過程前後添加了兩個動作–產生code_verifier
和code_challenge
,並在最後透過code_challenge
驗證code_verifier
。其目的有很大程度是為了建立前端通訊與後端通訊的關聯。
原先風險
那麼先來看看原本發生了什麼問題。
【用Keycloak學習身份驗證與授權17】Client Credentials
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
Figure 6: Client Credentials Flow
嘗試 Client Credentials flow
Client Credentials,這個模式有點特別。除了前面看到的它可能與其他模式並用以外,最特別的是,單純使用它,完全不需要資源擁有者參予。總之先來看看:
你可以使用RESTfer嘗試看看:
grant_type: client_credentials
client_id: oauth_tools
client_secret: <之前所產生的secret>
或是同樣可以透過OAuth.Tools嘗試看看。
【用Keycloak學習身份驗證與授權16】Refresh Token
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
Figure 2: Refreshing an Expired Access Token
The flow illustrated in Figure 2 includes the following steps:
(A) The client requests an access token by authenticating with the
authorization server and presenting an authorization grant.
(B) The authorization server authenticates the client and validates
the authorization grant, and if valid, issues an access token
and a refresh token.
(C) The client makes a protected resource request to the resource
server by presenting the access token.
(D) The resource server validates the access token, and if valid,
serves the request.
(E) Steps (C) and (D) repeat until the access token expires. If the
client knows the access token expired, it skips to step (G);
otherwise, it makes another protected resource request.
(F) Since the access token is invalid, the resource server returns
an invalid token error.
使用refresh_token
取得access_token
接著是使用Refresh Token換取Access Token的流程。這大概是所有中最簡單的一個模式之一了。
但因爲先決條件是取得可用的 Refresh Token ,所以無法單獨存在。在RCF6749相關的流程圖中,關注的是G、H的部分。
至於一開始有什麼方式取得Refresh Token就非常的多。在已經介紹的密碼模式和code模式都有可能返回refresh_token
。
【用Keycloak學習身份驗證與授權15】Authorization Code
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Note: The lines illustrating steps (A), (B), and (C) are broken into
two parts as they pass through the user-agent.
Figure 3: Authorization Code Flow
Authorization Code是在 RFC6749第一個提到的流程,所以有時又被視爲 標準流程(Standard Flow) 。
它與前兩個流程很不一樣,分成 前端通訊(frontchannel) 和 後端通訊(Backchannel) 。不過,其實反倒是前兩個是所有模式裡的怪胎,在隱含模式下,後端通信並在前端通訊;在密碼模式下,根本不存在前端通信,資源擁有者需要高度信任客戶端(說穿了在前端通信下,資源擁有者也是高度信賴瀏覽器或代理(User-Agent))。
【用Keycloak學習身份驗證與授權14】Implicit (Legacy)
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
Note: The lines illustrating steps (A) and (B) are broken into two
parts as they pass through the user-agent.
Figure 4: Implicit Grant Flow
如果說password適用於原生應用環境(Native Application)下的話,接著就是適用於純前端環境。 在現在前後分離架構的情況,前端與後端連接並不緊密,甚至前端幾乎就可以視爲一個完整的應用。 因此將前端視爲授權框架下的「客戶端(Client)」也就不會太難理解。
【用Keycloak學習身份驗證與授權13】Password Grant (Legacy)
首先,先來看看直接使用帳號密碼授權的。
是的, OAuth 是有一個模式支援直接使用帳號密碼的。 與萬能鑰匙不太一樣的是,授權的結果仍然是由授權伺服器的權杖和資源伺服器決定。 儘管透過中央授權控制可以限制存取權杖可以做些什麼,但畢竟直接使用帳號密碼並不是特別好, 故在其他模式下都不適用時,才應該再考慮此模式。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Figure 5: Resource Owner Password Credentials Flow
事前準備
安裝RESTer
【用Keycloak學習身份驗證與授權12】Flows這一小段路上路前注意事項
其實我原本是想要 RESTer 幹到底的哈😜。
今天有一點是插話的。考慮到接下來幾天的內容,所使用到的工具會有點多樣,所以行前做個提醒。
首先,你最好了解:
- HTTP Request / Response
- HTTP API (Web API)
- JSON
- BASE64
諾對於Postman這類工具有所熟悉再好不過。但接者幾天會使用:
- RESTer
- curl
有一些情況會直接使用。 - python
主要用於格式化JSON。
除此之外,如果熟悉Bash
的話同樣也有助於理解所有內容。此外還有可能會使用到 OAuth Tools 、 jwt.io 。(JWT的部分更有可能出現在之後關於Open-Id內容前後)
但其實,以上並非全部都是必須。最重要的是希望你能夠學習到OAuth本身的部分。
【用Keycloak學習身份驗證與授權11】OAuth 2
終於要來談談OAuth裡定義的細節了~
目前OAuth 2.0 一共定義了7種流程(flow)。在未來本系列可能稱之爲模式,不同模式適用於不同情況、不同環境。 就是因爲如此,OAuth才有高彈性的優勢。
OAuth 2.0 的可擴展性和模塊化是其最大的優勢之一,因為這使得該協議適用於各種環境。然而,正是這種靈活性導致不同的實現之間存在基本的相容性問題。當開發人員想在不同的系統上實現 OAuth 時,它提供的眾多自定義選項容易使人困惑。
本系列會介紹的模式包含:
- Password Grant (密碼模式)
- Implicit (隱含模式)
- Authorization Code (Code模式)
- Refresh Token
- Client Credentials (特殊密碼模式)
- PKCE
- Device Code
儘管 Implicit 和 Password Grant 被標記爲傳奇的(Legacy),但有時候仍然可能會使用到。重要的是你應該知道什麼情況應該使用什麼模式。同時記住,即使一個系統按照規範正確地實現了 OAuth,也不意味著該系統在實踐中就是安全的。
「OAuth 2.0 實戰」有一章決策圖可以幫助你決定使用什麼模式。但本系列應該不會提供。
【用Keycloak學習身份驗證與授權10】深入OAuth 2
喔不,其實今天還不會真正提到OAuth 2.0的深度內容。今天要來談談的是取得資源的細節。
使用帳號密碼,假裝自己是用戶
首先先試著想想看,如果你想要寫一支程式代替你處理某些事情。譬如:收信、發信。 更詳細的說,你寫了一個信件的客戶端(如:Thunderbird、Outlook)。 然後你會需要告訴這支程式你信箱的登入帳號密碼,由他去代替你收信、寄信。這個樣子就像是你把你所有的祕密都交給了它, 交給了它那把萬能鑰匙,而你完全信任這支程式。
其實這種狀況還真不少見。尤其在於你所申請的帳號,和使用的客戶端服務實際就是同一個時,這種行爲在正常不過。
但當它們是不同服務時,就可能出現問題了。你還能信任你提交的密碼不會被誤用嗎?不可能發生?
你可能有Gmail的帳號,你會很正常的使用Gmail的服務。但你知道Gmail除了自己本身外,它還可以幫你收其他信箱嗎?
比如說你還有ymail的帳號,但你更喜歡Gmail的界面,所以你希望使用Gmail來處理yamil的信件。這時候其實你就是告訴了Gmail 關於yamil的帳號密碼。相對的,也就是你應該是信任的Google的服務。
【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
再談身份驗證與授權
現在,讓我們再一次把視線放到「身份驗證」和「存取控制」這些名詞身上。 在入門篇的「淺談身份驗證與授權」已經相當程度的解釋過各個名詞。 不過今天將要更關注在身份驗證與存取控制的細節上。
對於一個應用來說,最重要的是它的 業務邏輯 。 除了業務邏輯本身,為完成所需的工作,會需要取得必要之資源。這可能是一份檔案, 鏡頭、麥克風資源等不同種形式。
在 取得資源 過程中,也會有另外一層業務邏輯,也可能本身就是另一隻程式服務,對所需取得的資源,進行 存取控制 。
最後,爲了判斷是否具有存取該項資源的權限,有可能有必要進行 身份驗證或授權 。
【用Keycloak學習身份驗證與授權08】OAuth 2
這是入門篇的最後一天了,今天不會寫什麼內容,但來帶大家看個入門概念可用的工具 – OAuth 2.0 Playground。
OAuth 2.0定義了幾個flow,可用於不同情境下,由於後續會有更多詳細說明,所以今天只會帶大家初步認識,嚐鮮看看。
註冊帳號
點選 register a client and a user。別擔心,這是個隨時可以廢棄的帳號。你完全不用真的去記他,他也不會要求你提供什麼資訊。
然後你會得到一組帳號密碼。然後點選「open in new window」之後就可以按下「continue」。
【用Keycloak學習身份驗證與授權07】什麼是OAuth
先來回憶一下,何爲「授權」。試想像有一座宅邸,裏頭有無數房間。而你作爲這座宅邸的管家,擁有一把萬能鑰匙,可以開始宅邸內所有門扉。 此外,這把萬能鑰匙還有一個作用,就是產生出開啓特定門扉的鑰匙。 你可以產生出的鑰匙交給其他人,其他人就可以自由進出特定房間。這個動作就是「授權」。
OAuth 是一個開放標準的 授權協議 ,它允許 軟體應用 代表 資源擁有者 訪問資源擁有者的 資源 1。
OAuth是什麼?
【用Keycloak學習身份驗證與授權05】什麼是Keycloak
終於要來好好介紹一下甚麼是Keycloak了~
收先先來看一下Keycloak的基本資訊:
- 名稱: Keycloak
- 開發使用的程式語言: Java
- 公用: 單點登入驗證與授權工具
- 許可協議: Apache License 2.0
- 公開倉庫: https://github.com/keycloak/keycloak
- 官方網站: https://www.keycloak.org
- 撰寫當下最新版本: 15.0.2 (2021年8月20日)
在 快速開始 提到過起始畫面有一些細節:
【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
實際上,在昨天已經將多數基礎都已經解釋過了,不過我想到還有一些東西可以再多做補充的。
對啦! 擔心彈藥不足,把一篇拆成兩篇來啦!👻
沒有身份識別的存取控制
在我們拆分的整個流程中分成:身份識別、身份驗證、授權、存取控制。但現在,你將Web App登出後再登入一次,你會發現「授權」的部分不見了! 但我們不會立刻來討論這個部分。先來說說身份識別。
不覺得,身份識別在整個流程之中非常雞肋嗎?也就只是將你這個「自然人」與系統中存在的「帳號」對應起來。 也確實如此,在這樣的拆分中,身份識別對於存取控制並不是必要的。在後來已MAC爲基礎發展的存取控制框架,也多不直接與帳號相關。
別擔心,之後會提到什麼是MAC(強制存取控制, Mandatory Access Control)。
不過還有一個更直接沒了這個流程的例子。在以「單人使用」作爲設計的系統之中,我們只需要拿到鑰匙就可以進行存取。
什麼?你說現在還有這種系統嗎?其實還真不少呢,加密上鎖過的壓縮檔案,上鎖的部落格文章。還有授權之後的流程,可能也不包含身份識別。
【用Keycloak學習身份驗證與授權03】淺談身份驗證與授權(1)
在「快速開始」的單元中,實際上已經完成了所有身份識別、身份驗證、授權和一些存取控制的流程。
在今天,將會來說說,這些看是相同,卻又有一些不同的名詞。
名詞認識
那麼首先,就先來認識各個名詞的英文吧!
名詞 | 英文 | 簡短說明 |
---|---|---|
身份識別 | Identification | 讓系統知道你是誰 |
身份驗證 | Authentication | 讓系統相信你是誰 |
授權 | Authorization | 允許他人存取某項資源 |
存取控制 | Access Control | 檢驗是否有資格存取某項資源 |
而我認爲軟體開發上,最重要的是 存取控制 。這直接關係到系統上的資源,但卻無法只存在存取控制,勢必需要身份驗證與授權的配合。這也是爲什麼我們很難將這些分開來看。
已「快速開始」爲例
現在,讓我們把視線重新放回剛弄好的「快速開始」。在這個範例中,已經多少碰觸到了每一塊的概念。首先是身份識別:
【用Keycloak學習身份驗證與授權02】Quick Start(2)
昨天,已經完成了一部分配置,且也已經可以建立帳號並登入了。
不過,這只能算是半套,而今天要在來完成另外半套。
你可以按照昨天的做法,重新建立一個新的Client。
只是注意在建立的時候,「Root URL」改爲: http://localhost:4200
。
今天,我們要自己實現一個前端網頁去的Web App,然後綁定Keycloak去做登入。
前置要求:
- 用Keycloak建立一個Client
- 網頁開發基礎知識(HTML/CSS/JavaScript)
- TypeScript的部分知識
- Angular知識(非必要)
調整Keycloak的Client配置
前面說過,Keycloak的Client實際上並不是真正的Client Application,只是做了一些關聯。 今天就要來 快速開始 個自己的Web App。而首先,需要先調整Client的關聯:
- 選擇「Clients」
- 找到昨天建立的「my-quick-start-app」,然後點選「Edit」
這此調整主要做兩個修改:
【用Keycloak學習身份驗證與授權01】Quick Start(1)
開始之前~2🎃。開完笑的~
但是想了許久,總覺的就這麼直接開始解釋各個名詞不太好。
想找個範例又有諸多擔心。
不如…先來快速開始做個範例!
快速開始將分成兩天。 今天會先跑過一次簡單的流程,明天才會寫一點程式。
這兩天看完後,依照需求,你甚至可以開始開發自己的應用。
那我們從Keycloak開始吧!
今天的前置需求:
- 只要裝好docker就好囉~
- 阿!對,你還要安裝個瀏覽器。
(不過你拿什麼在看本系列文章呢?)
透過Docker建立一個Keycloak應用
docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:15.0.2
這麼一條指令就可以開始這系列多數內容了(吧)。現在Keycloak會聆聽本機的8080 port。嘗試用瀏覽器開啓 http://localhost:8080 後,你應該會看到以下畫面:
【用Keycloak學習身份驗證與授權00】開始之前
這系列文章將帶大家探討軟體開發上,那些身份驗證與授權的相關議題。此外的話題還有身份識別、存取控制。 以目前諸多流行應用都以非單人使用的狀況之下,身份驗證與授權,幾乎是每位開發者都會遇到的題目。 不管你是串接OAuth、管理資源、寫後臺界面,甚至在最初應用的設計,幾乎都會扯上邊。 在業務邏輯之外,這或許會是相當重要的一部分。
關於身份驗證與授權,每一個部分都非常重要,也都可以分開來看,卻也非常難分開來看。
Because these three techniques are so closely related in most real applications, it is difficult to talk about them separate from one another. In particular, authentication and authorization are, in most actual implementations, inextricable. 1
就如同在Oracle上可查詢到的相關資料,這些部分儘管代表者不同概念,但彼此非常相關,實在很難分開來看。雖然如此,每一個部分都是非常龐大的內容,而本系列將會着重於授權控制與存取控制。在此之上會在探討近來已經非常普遍的OAuth 2.0、Open-Id、單點登入(SSO)和基於角色的存取控制(RBAC)
Tag: kotlin
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
Tag: laravel
30天成爲Laravel萌新(目錄)
這是第二次參加「iT 邦幫忙鐵人賽」。上次報了兩個主題,只有一個完賽。這次報了三個主題……還好全都完賽了。雖然開賽前,對於一個主題先寫了近十天左右的文章,但在賽中,趕稿壓力還是頗大的。
30天成爲Laravel萌新(目錄)
「30天成爲Laravel萌新」是我最重要的一個系列,報名在Modern Web主題下,並同步發表於又LAG隨性筆記。下面列出每天文章連結:
- 30天成爲Laravel萌新(第0天) - 前言
- 30天成爲Laravel萌新(第1天) - 認識Laravel
- 30天成爲Laravel萌新(第2天) - 安装 Laravel
- 30天成爲Laravel萌新(第3天) - 使用laradock建立開發環境(上)
- 30天成爲Laravel萌新(第4天) - 使用laradock建立開發環境(下)
- 30天成爲Laravel萌新(第5天) - Laradock的工作空間容器
- 30天成爲Laravel萌新(第6天) - 配置專案
- 30天成爲Laravel萌新(第7天) - 認識artisan
- 30天成爲Laravel萌新(第8天) - 路由&頁面模板(1)
- 30天成爲Laravel萌新(第9天) - 路由&頁面模板(2)
- 30天成爲Laravel萌新(第10天) - 路由&頁面模板(3)
30天成爲Laravel萌新(第30-1天) - 總結
這是第二次參加鐵人賽。這個主題是我決定參賽出就已經定好,總算寫完了。從介紹、安裝、配置,使用laradock
、artisan
,路由、模板,Parsdown
(Markdown),再到控制器、資料庫,以及另我以些驚豔的Pagiantion
,然後多語系支援、紀錄檔,客製化錯誤頁面,檔案上傳與表單驗證,到最後登入驗證。
在這過程中,一學習到不少,很充實。(還有下次不要在一次報三個主題了…)
不過,Laravel官方文檔其實相當豐富完整,這30天的文章,頂多只能當作而外的參考而已,但願對想學習Laravel的人還是有幫助。
最後,你可以在這裡看到跟Laravel有關的文章。或是看看另外兩個系列文章( 又LAG的EOS.IO技術筆記 和 有點玩鬧性質的 又LAG的ML學習筆記 )。
30天成爲Laravel萌新(第30天) - 登入驗證
這部份在Django時,明明是最先學的,在Laravel卻放到了最後☺
要使用Laravel提供的會員系統,相當容易,只須要:
artisan make:auth
artisan migrate
然後瀏覽http://localhost/register 註冊帳號,或是http://localhost/login 登入帳號。仔細一看,會多了這些檔案:
new file: app/Http/Controllers/HomeController.php
new file: resources/views/auth/login.blade.php
new file: resources/views/auth/passwords/email.blade.php
new file: resources/views/auth/passwords/reset.blade.php
new file: resources/views/auth/register.blade.php
new file: resources/views/auth/verify.blade.php
new file: resources/views/home.blade.php
new file: resources/views/layouts/app.blade.php
modified: routes/web.php
其中routes/web.php
多了:
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
現在瀏覽http://localhost/home 也有畫面了。
30天成爲Laravel萌新(第29天) - 表單驗證
昨天的程式碼有一些註解的內容,先取消註解試試。
resources/views/images/upload.blade.php部份內容
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
routes/web.php處理post('/images/upload')
請求的部份內容:
Validator::make($request->all(), [
'file' => 'required|image',
])->validate();
驗證表單資料
Validator::make($request->all(), [
'file' => 'required|image',
])->validate();
差不多等價於:
$validate = $request->validate([
'file' => 'required|image',
]);
$request
有validate()
的方法。還可以直接建立一個驗證器,繼承Request
:
php artisan make:request ImageUpload
上面執行完後,現在,在app/Http/Requests/
目錄下多了ImageUpload.php
,內容改成下面這樣:
30天成爲Laravel萌新(第28天) - 上傳檔案
Laravel要上傳檔案非常的簡單,今天就來簡單帶個範例吧!
建立檔案目錄連結
首先,現用Artisan建立目錄連結。
artisan storage:link
上面命令會建立storage/app/public
目錄,並將目錄同樣綁定到public/storage
。這讓於此目錄下的內容,可以透過http://localhost/storage/<FILE NAME>
存取。在public
目錄下的檔案,基本都可以直接透過瀏覽器存取。
上傳頁面
同樣以一個簡單的上傳頁面作為範例。先建立resources/views/images/upload.blade.php
:
@extends("base",['title'=>'上傳圖片'])
@section('title', '上傳圖片')
@section('body')
<form action="{{route('image.upload')}}" method="post" enctype="multipart/form-data">
@csrf
<!--
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
-->
<input name="file" type="file" accept="image/*" value=""/>
<input name="" type="submit" value="上傳"/>
</form>
@endsection
30天成爲Laravel萌新(第27天) - 本地化、多語言支援(Localization)
@extends("base",['title'=>'哎呀,找不著頁面'])
@section('title', '哎呀,找不著頁面')
@section('body')
<h1>{{__('404 error message')}}</h1>
<b>哎呀,找不著頁面</b>
<p>
<ul>
<li><a href="/">點擊我,回到首頁</a></li>
<li><a href="{{route('blog/post.index')}}">我的網誌</a></li>
</ul>
</p>
@endsection
開始
在昨天簡單建立的錯誤頁面中,又再一次偷渡了一個東西。對,就是那個有點怪怪的{{__('404 error message')}}
,實際上這等價於@lang('404 error message')
。透過lang()
函式的方式,也可以在控制器做變換。lang()
會根據給定的字串,以及設定(app/config.php
)中的locale
和faker_locale
轉換為對應的語言。預設會轉換為locale
所設定的,而備用語言可由faker_locale
設定。當兩者都不存在時,會直接輸出字串。還記得我們把app/config.php
部份內容改成這樣嗎?
'locale' => 'zh-TW',
'fallback_locale' => 'en',
不過,不管是locale
還是faker_locale
,都還找不著{{__('404 error message')}}
要轉換成的文字,所以現在會直接生成404 error message
。
30天成爲Laravel萌新(第26天) - 客製化404錯誤頁面
HTTP協議上存在許多狀態碼,其中400系列、500系列錯誤可能是最常見到。200、300很難被注意到。恐怕又以404錯誤、403錯誤、500錯誤、503錯誤最常見。一個好的錯誤提示頁面,可以帶來很好的使用體驗。你可以看看別人怎麼設計(以及2018年不可錯過的創意404報錯設計),甚至更有404 PAGE TEMPLATE (https://www.404pagefree.com/) 可以直接下載404錯誤頁面模板(這網址真有趣),裡頭模板多以CC-BY 3.0姓名標示授權,可用於商業用途。
那麼在Laravel要怎麼客製化這些錯誤提示頁面?以404錯誤頁面為例,可以建立resources/views/errors/404.blade.php
檔案,寫入以下內容:
@extends("base",['title'=>'哎呀,找不著頁面'])
@section('title', '哎呀,找不著頁面')
@section('body')
<h1>{{__('404 error message')}}</h1>
<b>哎呀,找不著頁面</b>
<p>
<ul>
<li><a href="/">點擊我,回到首頁</a></li>
<li><a href="{{route('blog/post.index')}}">我的網誌</a></li>
</ul>
</p>
@endsection
同樣的…CSS不是本系列重點……
30天成爲Laravel萌新(第25天) - 紀錄檔
使用Logging
現在,要再回到laradock來。不過同樣的,Lavavel儲存紀錄檔的地方也是在storage/logs
。這裡會儲存使用Log::info()
所寫下的紀錄。
docker-compose exec workspace bash -c "tail -f storage/logs/最新的log"
如果使用Linux的話,也可以直接:
tail -f storage/logs/最新的log
當然你也可以直接把紀錄檔,用文字編輯器開啟。不過上述會這麼做,是因為如次可以更簡單的在開發時除錯。有時候,Laravel錯誤訊息頁面提供的訊息並非一目瞭然,會需要用到printf
方式除錯(我假設…你不動gdb
,但用過最基本的除錯手法…)。在這裡,使用一系列Log::
的方法,其中,Log::debug()
就對應除錯資訊。這和我曾經寫過的瀏覽器console.log()外的一些其他用法 有些像。
30天成爲Laravel萌新(第24天) - 頁面清單 下篇(使用Pagnation和刪除按鈕)
就先來看看使用Laravel提供Pagination能夠多麼簡單。
首先,控制器只需要使用BlogPost::paginate($limit)
查詢就好,並將查詢結果直接傳遞給視圖。
public function index()
{
// 如果想要將每頁顯示數量變成可以調整的,
// index需要傳入 (Request $request),並由下方方式取得$limit傳入paginate
// $limit = $request->query('limit', 30);
$posts = BlogPost::paginate(30);
return view("blog/index",[
"posts"=>$posts,
]);
}
BlogPost::paginate($limit)
會自動根據Request的page
參數取得存取的頁面,自動計算offset
,並根據給予的每頁顯示數量限制來查詢。甚至連index()
都不需要傳入Request $request
。傳給視圖的也少了不少。說到視圖,立馬來看看變得多簡潔:
@section('body')
<div class="container">
<ul>
@foreach ($posts as $post)
<li>
<a href="{{route('blog/post.show',['id'=>$post->id])}}">{{ $post->title }}</a>
</li>
@endforeach
</ul>
</div>
{{ $posts->links() }}
@endsection
列出文章清單的部份,大致什麼太大變化。但是頁面頁數選項不再那麼複雜,沒錯,只需要$post->links()
就好。他會自動生成符合Bootstrap的Pagination。在瀏覽器接受到的HTML樣子可能如下:
呵呵,根本不需要寫這麼麻煩…也太簡單了吧!
30天成爲Laravel萌新(第23天) - 頁面清單 上篇
因為重點不在於CSS,不在於Bootstrap。 其實大可以將頁面弄的很美觀。在Laravel專案資料夾中,其實有一個給
npm
使用的package.json
檔案。 不過還是來把重點放在Laravel上。
使用Pagnation前
Laravel提供一個超級簡單的方式,來處理分頁問題。不過在使用前,來看看原本要怎麼實現index()
。不但要取得頁數,並計算檢索數量,然後從資料庫透過offset()
、linit()
的方式取得資料。
public function index(Request $request)
{
$offset = $request->query("offset", 0);
$limit = $request->query("limit", 30);
$page = $request->query("page");
if($page){
$offset = ($page - 1) * $limit;
}else{
$page = ($offset / $limit) + 1;
}
$request->merge([
"offset" => $offset,
"page" => $page,
]);
$posts = BlogPost::orderBy('activity_id','ASC')
->offset($offset)
->limit($limit)
->get();
return view("blog/index",[
"posts"=>$posts,
"page"=>$page,
"total_pages" => $total_pages,
]);
}
30天成爲Laravel萌新(第22天) - 資源控制器(Resource Controller) 下篇
最終,我決定將index()
和destroy()
另外寫。一個是寫完create()
和edit()
,destroy()
也就不怎麼難。但是index()
意外也能有豐富內容能寫…(加上有另一種用法我還不太會)
總體來說,這篇程式還是有些趕工粗糙….
重點觀念
@method
的使用 相當於隱藏欄位_method
。@csrf
的使用
建立頁面模板
同樣的,先建立一個編輯頁面的模板resource/views/blog/edit.blade.php
:
@extends("base",['title'=>'編輯文章'])
@section('title', '編輯文章')
@section('body')
<form method="post" action="{{($type=="edit") ?
route("blog/post.update", ["id"=>$id]) :
route("blog/post.store")}}">
@csrf
@method(($type=="edit")? "patch" : "post")
<label for="title">標題:</label>
<input name="title" type="text" value="{{$title}}" id="title" />
<br/>
<label for="content">內容:</label>
<textarea cols="30" id="content" name="content" rows="10">{{$content}}</textarea>
<br/>
<input name="" type="submit" value="儲存"/>
</form>
@endsection
30天成爲Laravel萌新(第21天) - 資源控制器(Resource Controller) 中篇
現在,要正式把控制器與資料庫連結起來。順便偷埋之後兩個主題☻。
引入所需使用的套件
首先,得先把之前建立好的Model引入。另外,我們在偷偷引入一個Illuminate\Support\Facades\Log
。
use Illuminate\Support\Facades\Log;
use App\BlogPost;
use Parsedown;
完成CRUD操作
Create / Store
public function store(Request $request)
{
$title = $request->input("titile", "未命名文章");
$content = $request->input("content");
$post = new BlogPost;
$post->title = $title;
$post->content = $content;
$post->save();
Log::info("Store New Blog Post: id = $post->id");
return redirect()->action(
'Blog\PostController@show', ['id' => $post->id]
);
}
可以透過Request
的input
方法,取得POST來的資料,還可以補上預設參數。這邊取得標題與內容後,建立一個新的PostBlog
實體存入資料庫。
Read / Show
public function show($id){
$post = BlogPost::find($id);
if(! $post){
abort(404);
}
$content = $post->content;
{
$Parsedown = new Parsedown();
$content = $Parsedown->text($content);
}
return view("blog.post", [
"title" => $post->titile,
"content" => $content,
]);
}
讀取與之前控制器的內容差不多。不同的是,需要先從資料庫尋找資料,如果找不到就回傳404找不到錯誤頁面。之後會對該頁面進行修改。現在http://localhost/blog/post/12 將會顯示404錯誤頁面,只有http://localhost/blog/post/1 才會出現之前填入的內容。
30天成爲Laravel萌新(第20天) - 資源控制器(Resource Controller) 上篇
CRUD & RESTful
所謂CRUD是Create、Read、Update、Delete。昨天已經從資料庫模型(Model)的角度看過基本操作了,今天要將些操作加入到控制器(Controller)。
C | Create
R | Read
U | Update
D | Delete
另外,還需要另外知道的一件事情是RESTful。RESTful並不是硬性規定,只是一種在HTTP請求上的一種慣例設計。通常HTTP請求方法有GET
、POST
、PUT
、DELETE
、PATCH
,以及HEAD
、CONNECT
、OPTIONS
、TRACE
。以下節錄自MDN:
GET
GET 方法請求展示指定資源。使用 GET 的請求只應用於取得資料。
HEAD
HEAD 方法請求與 GET 方法相同的回應,但它沒有回應主體(response body)。
POST
POST 方法用於提交指定資源的實體,通常會改變伺服器的狀態或副作用(side effect)。
PUT
PUT 方法會取代指定資源所酬載請求(request payload)的所有表現。
DELETE
DELETE 方法會刪除指定資源.
CONNECT
CONNECT 方法會和指定資源標明的伺服器之間,建立隧道(tunnel)。
OPTIONS
OPTIONS 方法描述指定資源的溝通方法(communication option)。
TRACE
TRACE 方法會與指定資源標明的伺服器之間,執行迴路返回測試(loop-back test)。
PATCH
PATCH 方法套用指定資源的部份修改。
其實也就差不多對應了CRUD的操作:
CRUD | RESTful |
---|---|
Create | POST / PUT |
Read | GET |
Update | PATCH / PUT |
Delete | Delete |
30天成爲Laravel萌新(第19天) - Model的基本操作
ORM是物件關聯對映(Object Relational Mapping)。在Larvel裡提供的 Eloquent ORM能讓開發者以一個簡單、美觀的方式操作關聯式資料庫。今天就來寫一點點基本的操作,更多可以查看文件,以及Laravel資料庫相關文件。
Select All
在SQL,可能會用SELECT * FROM <TABLE>;
來取得所有資料,儘管在資料量龐大時,這樣操作並不好,但是Laravel同樣有對應的操作:
use \App\BlogPost
$posts = App\BlogPost::all();
foreach ($posts as $post) {
echo $post->title;
}
條件查詢Where
條件查詢也非常長使用到:
$post = App\BlogPost::where('id', 1)->get();
// ->orderBy('name', 'desc') // 還可以加以設定以下參數
// ->take(10)
// ->get();
用where
自然很容易從SQL轉換了解。不過,Laravel有更簡單的方式針對主鍵做查詢:
$post = App\BlogPost::find(1);
// 等同於
$post = App\BlogPost::where('active', 1)->first();
30天成爲Laravel萌新(第18天) - 建立Model
前幾天建立了Blog Post在資料庫的Schema,並且遷移(Migration)進資料庫,建立相關的資料表(blog_post)。為了讓Larave提供的ORM系統,以一個美觀、簡易的方式操作資料庫,還得建立BlogPost Model類別。
當然也可以使用artisan建立一個樣板:
artisan make:model BlogPost
然後會有app/BlogPost.php
的檔案
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogPost extends Model
{
//
}
這次,只需要簡單改成下面的樣子就好:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogPost extends Model
{
protected $table = "blog_post";
// protected $primaryKey = 'id';
// public $timestamps = false;
}
30天成爲Laravel萌新(第17天) - 資料庫管理工具(下)‧使用DBeaver
今天要在介紹一個,我用過後就喜歡上的資料庫管理工具–DBeaver。
DBeaver的兩個版本
DBeaver有社區版和企業版兩種。社區版可以免費使用,並且跨平台,透過JDBC支援多種資料庫。此外還會自動檢測資料庫版本,自動尋找相對應的Drive。
我手動配置過phpMyAdmin過。同樣直接下載安裝的情況下,DBeaver簡單好多。
DBeaver的界面很漂亮,我還蠻喜歡的。其中還有一個功能測試時非常好用–產生假資料。這個和前幾天亂數短文很像,不過DBeaver會針對欄位型態產生資料。資料庫測試的模擬數據生成(Mock Data Generation)也有一些限制,有些資料類型還是無法產生。
連線資料庫並填入資料
說了這麼多,相信已經下載安裝好了吧?該像昨天一樣,來連接資料庫,並塞入文章吧!
30天成爲Laravel萌新(第16天) - 資料庫管理工具(上)‧使用phpMyAdmin
今天,要用 phpMyAdmin 來看看昨天建立的資料表,我會順面補充一些之前做的設定。
查看Docker容器狀態
我會假設使用的是laradock,如果你使用的是XAMPP可能會簡單一些。
那麼,首先先看看運行的容器:
docker-compose ps
我們之前有啟動phpMyAdmin的服務,那麼輸出應該包含下面內容:
Name Command State Ports
---------------------------------------------------------------------------------------------------------------
laradock_phpmyadmin_1 /docker-entrypoint.sh apac ... Up 0.0.0.0:8080->80/tcp
其中注意到 Ports 的部份,這意思是綁定(bind)電腦8080端口到容器的80端口。所以我們可以透過瀏覽 http://localhost:8080 來使用phpMyAdmin。
登入phpMyAdmin
這裡伺服器要輸入mariadb
,帳號和密碼分別是:default
和secret
。如果你是啟用MySQL的話,伺服器就改成mysql
。
30天成爲Laravel萌新(第15天) - 建立資料庫Migration
要把文章存入資料庫?那麼對於傳統關聯式資料庫,需要先建立資料表Schema。你可以透過明天要介紹的資料庫管理工具,也可以透過今天來來說的使用 Artisan 建立資料表。
使用Artisan建立資料表
Artisan可以快速的建立控制器基本模板,同樣也可以建立資料庫相關模板:
artisan make:migration create_blog_post_table
透過上面命令,會在database/migrations
新增相關檔案,檔名由<日期><時間>_create_blog_post_table
組成。前面時間相關的在某些情況下會很重要,後面介紹的 artiasn 命令會依序建立資料表,所以如果資料表有相互依賴就有影像。現在我們開啟該檔案看看:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateBlogPostTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('blog_post', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('blog_post');
}
}
其中最重要兩行:
$table->bigIncrements('id');
$table->timestamps();
30天成爲Laravel萌新(第14天) - 控制器(Controller)
回頭看看,看,怎麼Route的內容變得這麼長阿!仔細一看,還做了路由以外的事情,像是修改變數內容等等。是不是該把Hangle Function獨立出來?有什麼辦法?Laravel的控制器(Controller)可以幫助。
新增Controller
我們可以透過Artisan來快速新增一個標準的控制器(Controller):
artisan make:controller Blog/ExamplePostController
在app/Http/Controllers/Blog
資料夾下會多一個ExamplePostController.php
的檔案,內容大致如下:
<?php
namespace App\Http\Controllers\Blog;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class ExamplePostController extends Controller
{
//
}
Blog
這個新建立的資料夾,實際對應於昨日group
路由當中的'namespace' => 'Blog'
參數。並且在上面生成的程式中use App\Http\Controllers\Controller;
,也是因為這個新建立的模組是在App\Http\Controllers\Blog
底下的命名空間,因此要引入使用App\Http\Controllers\Controller
。關於PHP的模組系統,就不多做解釋了,只是相關的Blog
字串如不想使用,要做相對應的調整。
之後會改用資源控制器。這邊單純要做個對照,之後可以刪除不使用。
30天成爲Laravel萌新(第13天)- 簡易Blog頁面(下)
昨天利用亂數文章(Lorem ipsum)產生了@section('body')
的預設內容。今天,要在Route把變數$content
設成類似的亂數文章。
中文亂數文章
Lorem最多的資源大多還是英文,不過中文也有不少資源可以使用。此外,有些情況使用設計好的預設文字可能會更適合。今天,就只是想來用用而已XD。透過亂數假文產生器 Chinese Lorem Ipsum可以產生中文亂數文章,也可以用於產生標題。現在來簡單的把$title
設成日的及度加機子魚年
。$content
設成:
身主一?是字對一日國地包最中感行物評民活於力!進上城大、很會英我:的樣基覺型家海當期是著:算像系大心樣十因天特企以加想點以後許,念落是客我什在白實文種運明市下只能當作力美間要速包企以統推多,收什火投,顧外。
指的司之身鄉然說小人發,我雜人轉英臺區實說麼後男會友大,阿產效第續明造識竟自沒大上有。到全藝地港為精件連需,過聯排可然更友我後們時?一風了組麼兒比、方來出科!車卻讀力風人父資國個年愛成故作自功:原用中有、港看車我高心打!親北受活女微特利動果的無何片經物……象巴約得?色時值壓五……過點子得提圖陽可計,來麼室拉的合天!人觀難一手樹以防黃精也需被那部我冷不不連痛以哥多人事營件、目熱而夫加……紀味曾蘭不好空農後電題熱頭國:戲識者是相大覺光,雙命影心可那?前區孩增,物時。
產明物力區有王真?院見很原,經總萬官方,生回。
海在康代界積實兒變:她哥出邊。善快寶,死面大,紀著資小成去資戲面和的發行的止的接來,南生不能野:政生企性班們教求給學就得受再球也生克決的金長冷成。
標題、文章內容當然還無意義wwww
調整排版
恩~沒分段了?這是因為HTML會忽略空白,如過要換行還需要使用<br/>
。當然你可以在變數內容這麼加,然後在模板將顯示的部份改為{!! $content !!}
,或是也可以用<pre>
來做,像是把@section('body')
改成:
@section('body')
<h1>{{$title}}</h1>
<pre>{{$content}}</pre>
@endsection
30天成爲Laravel萌新(第12天)- 簡易Blog頁面(上)
今天要來建立一個部落格文章的極簡陋顯示頁面。
建立路由
在routes/web.php
檔案中添加以下內容:
Route::group(['prefix' => 'blog',
'as' => 'blog/',
'namespace' => 'Blog', ],
function(){
Route::get('/post/{post_id}', function($post_id){
$title = "Example Title";
$content = "Example Content";
return view('blog.post', [
"title" => $title,
'content' => $content,
]);
});
});
儘管其實完全不需要用到group
,不過未來可以像其他blog系統一樣,除了有文章頁面(post),還可以有其他獨立頁面(page)。另外,添加blog的前綴,可以將其他前綴用於其他作用,或是最後在做個轉址。總之,先選了個靈活的寫法。
其中,好好注意$title
和$content
這兩個參數,這兩個參數隨後會在多次變動修改,今天主要先把頁面顯示出來。
部落格文章頁面模板
接者建立resources/views/blog/post.blade.php
檔案(當然需要先建立resources/views/blog
資料夾)。並且上頭原本路由區用的view也可以寫為blog/post
。將這份檔案簡單填入以下內容:
@extends("base",['title'=>$title]) {{-- 第二個參數可以傳遞變數給父模板,但是父模板改用插槽方式,須改用以下方式 --}}
@section('title', $title)
@section('body')
<h1>{{$title}}</h1>
{{$content}}
@endsection
這個頁面模板繼承了基礎頁面(base
),並填入頁面內容。不過,還沒有基礎也面阿?現在來建立resources/views/base.blade.php
,並寫下以下內容:
30天成爲Laravel萌新(第11天) - 路由&頁面模板(4)
使用Artisan顯示目前路由狀態
首先,先來說說怎麼看目前路由狀態。透過artisan route:list
列出目前路由狀態:
+--------+----------------------------------------+--------------+------+---------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------------------------------------+--------------+------+---------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | hello | | Closure | web |
| | GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS | hello-world | | Closure | web |
| | GET|HEAD | hello/{name} | | Closure | web |
+--------+----------------------------------------+--------------+------+---------+--------------+
首先要注意到的是Method
和URI
,這也是在定義路由最基礎的部份,URL
在路由定義時就稱作PATH
。我們還可以將路由命名(Name
);至於Action
與以後會提到的控制器(Controller
)有關,這裡顯示Closure
表示路由由handler function
處理請求;最後Middleware
預計本系列文章不會提到,又興趣可以去看官方文件。
api/user
定義在routes/api.php
中。
基礎路由方法
基本路由方法有:
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
基本上就對應了REST的GET
、POST
、PUT
、PATCH
、DELETE
、OPTIONS
的請求方法。此外還可以透過Route::any($uri, $callback);
來直接處理請求,而不管請求方法;或是使用Route::match([$method,...], $uri, $callback);
來處理特定請求方法。
※ 請求方法 是HTTP請求裡頭的一個欄位:METHOD。
30天成爲Laravel萌新(第10天) - 路由&頁面模板(3)
回去看了一下文件,才發現之前有好多沒用到過 畢竟只學了2周的時間。不過有些設計挺有趣的。
今天來看一下Blade模板語言中的一些(我覺得)重要的功能。
印出變數
昨天已經看過怎麼顯示變數了,使用{{$variable}}
。今天要特提的是:怎麼給變數一個預設值。
{{isset($variable) ? $variable : "Default Value"}}
{{ $variable or 'Default' }}
不過在5.8、6.x以後似乎改成這樣:
{{$variable ?? 'Default'}}
我不清楚爲什麼把這麼方便的功能給去除掉了。 6.x之後的用法並沒有在官網的文件特別提,會發現單純是範例中有類似程式碼。
上面其實還相當於
@isset ($variable)
{{$variable}}
@else
Default
@endisset
另外還有@empty
@empty($variable)
Default
@else
{{$variable}}
@endempty
30天成爲Laravel萌新(第9天) - 路由&頁面模板(2)
繼續昨天
Route::get('/hello/{name}', function ($name) {
return '<h1>Hello, '.$name.'</h1>';
});
透過路徑接受了參數傳了進來,再加以加工輸出回去。不過這麼寫不怎麼美觀, 因為這違反了 「 只做一件事情 」的原則,不但處理了路由請求,還傳遞參數並渲染頁面。
今天透過模板系統,要來把路由請求處理與頁面設計分離。
首先,把上面程式修改為以下內容
Route::get('/hello/{name}', function ($name) {
return view("hello-name", [
"name" => $name,
]);
});
接著在新增一個模板檔案resources/views/hello-name.blade.php
<h1>Hello, {{$name}}</h1>
然後瀏覽http://localhost/hello/Daniel 看看,應該與之前畫面並無差異。
30天成爲Laravel萌新(第8天) - 路由&頁面模板(1)
接下來幾天,會交叉介紹路由(Route)和頁面模板(View/Blade)。幾經思考,這兩者關係十分密切,不太好單獨撰寫。
關於路由(Route),我們曾經在第六天短暫看到過。
<?php
Route::get('/', function () {
return '<h1>Hello</h1>';
});
現在我們看看routes
資料裡面的內容:
- api.php
- channels.php
- console.php
- web.php
我們修改的是web.php
的內容,這也是最主要訂立路由的檔案。api.php
的檔案實際上也不太有差別,不過api.php
底下定義的路由,預設會前置增加api/
的前綴,是設計用於提供HTTP API路由定義的檔案。
瀏覽 http://localhost/ 以後,就會顯示Hello World,如果你嘗試右鍵瀏覽網頁原始碼(view-source:http://localhost/)就會看到<h1>Hello</h1>
。
在回頭看看網址,除了localhost
外,後面的就是路徑/
,認知道這個斜線是很重要的,因為Apache2和Nginx對於結尾斜線的認知有些不同。接著再在wep.php
增加以下入由看看:
30天成爲Laravel萌新(第7天) - 認識artisan
Laravel有「 為網頁藝術家創造的框架 」的美譽,他的工具名字也很有意思 artisan ,意為 工匠 ,與藝術家(artist)一樣,是與藝術(art)有關的字。
artisan 可以用來顯示路由狀態、遷移資料庫、產生基本樣板程式碼、調整文件結構狀態等等。而且之前已經看過,就是我們用來產生專案文件密鑰(key)的artisan key:generate
。
不過,如果使用laradock
進入workspace
的docker容器的話,可能會找不著指令。artisan
詳細使用方式,會在未來有需要時在做說明,今天,就先來簡單看一下。
如果使用laradock
進入workspace
的docker容器的話,找不著指令嗎? 透過下面命令切換到/var/www
目錄下在試試。
cd /var/www
列出所有artisan子命令
不同版本的artisan
有可能存在使用差異。今天主要說明怎麼快速了解指令如何使用。
首先先學著者麼列出所有能使用的工具:
artisan
或是
artisan list
30天成爲Laravel萌新(第6天) - 配置專案
在前三天已經安裝好Laravel的基本環境。今天算是一個分水嶺,不管你採用哪種方式建立開發環境,都應該已經得到一個預設好的Laravel工作目錄。在此我不會解釋目錄結構,有興趣可以自行參閱文檔。不過是先留意一下幾個文件與目錄:
- artisan
- config/
- database/
- public/
- resources/
- routes/
- storage/
之後有用到會在加以說明。而今天,首先要進行專案的配置,也就是設定(config/)。沒錯,目錄 config 就是儲存相關配置的目錄。不過在此,我們還得先編輯 .env 檔案。你可能會找不著這份檔案,別擔心,目錄下有個 .env.example ,將其複製並重新命名即可。
接著我們找到以下內容:
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
目前最爲重要的是關於APP和DB的設定。在發佈階段,APP_URL
會需要調整,不過我們之後在說說。而目前預設情況僅有自己的電腦能夠瀏覽。其次是APP_DEBG
,在發佈階段需要改為false
。 此外,還會注意到一個APP_KEY
的設定,如果其為空,請執行以下命令:
php artisan key:generate
接着是資料庫的設定,這裏需要改成資料庫的連結設定。如果使用MariaDB和MySQL,維持mysql
就好。(新版本的MariaDB可能有問題)
30天成爲Laravel萌新(第5天) - Laradock的工作空間容器
在開始配置Laravel的環境設定檔案之前(.env
),先來帶大伙看看laradocke最
重要的一個容器workspace
。
關於workspace容器
workspace是laradock連結各個容器的重要容器,還記得我們昨天這麼做嗎:
docker-compose exec workspace composer create-project --prefer-dist laravel/laravel tutorial_blog 5.8.*
docker-compose exec
的格式是docker-compose exec <CONTAINER> <COMMAND>
。可是,可沒有吧workspace
啟動(up
)起來呀!我們像下面啟動了nginx
、mariaDB
、phpmyadmin
而已阿。
docker-compose up -d nginx mariadb phpmyadmin
是的,laradock會自動啟動workspace
這個容器。並且注意到後面的COMMAND
實際就是在第2天用來初始話laravel專案的命令。在下完這的命令以後,會在原本建立的 laravel-tutorial 目錄下多一個 tutorial_blog 目錄。並且裡面有基本Laravel的環境。我們會在幾天後來設定這個環境,現在,來我們專注於 workspace 這個容器。
我們同用用docker-compose exec
來進到 workspace 容器裡面。
docker-compose exec -u laradock workspace /bin/bash
或是用docker exec
來進到裡頭。不過使用docker exec
你還會需要知道真正的容器名字。所以命令可能像是下面這樣:
docker exec -it -u laradock laradock_workspace_1 /bin/bash
我們使用 laradock 登入workspace容器(-u/--user
)。laradock 預設用於開發的使用者帳號,如果不加上這個選項,會使用最高管理權限登入(root)。透過使用這個帳號登入,未來可能可以省去一些不必要的麻煩(ex:宿主機和虛擬機掛載目錄的權限問題)。
30天成爲Laravel萌新(第4天) - 使用laradock建立開發環境(下)
建立專案目錄
本次專案目錄預計會有以下內容:
- laravel-tutorial
- laradock
- .laradock
- tutorial_blog
laradock 和 .laradock 先不管他們。先建立 laravel-tutorial 目錄,並在該目錄鍵入:
git clone https://github.com/Laradock/laradock.git
cd laradock
git checkout v7.15
以上會安裝laradock,並切換到7.15版本。
設定laradock
在建立服務容器前,要先做設定。
首先, 複製 env-example 為 .env,然後找到以下設定並變更:
APP_CODE_PATH_HOST=../tutorial_blog
...
...
...
DATA_PATH_HOST=../.laradock/data
APP_CODE_PATH_HOST
指定專案目錄(下一步驟建立),DATA_PATH_HOST
則是未來資料儲存的位置,包含資料庫儲存位置。
接着建立並啓動環境:
30天成爲Laravel萌新(第3天) - 使用laradock建立開發環境(上)
除了使用composer
以外,還可以使用 laradock 、Homestead、Valet、Laragon。Homestead是基於 Vagrant ,如果您已安裝VirtualBox,可以使用看看。而接下來說明laradock的使用方式。
Laradock 環境需求
laradock是基於docker的一個快速建立laravel的開發環境工具,理所當然的你會需要docker,此外你還會需要 docker-compose,以下列出範例使用的版本:
軟體 | 版本 |
---|---|
docker | 18.09 |
docker-compose | 1.25 |
git | 2.7.4 |
透過使用laradock,可以很快速的在Apache2、Nginx;MySQL、MariaDB;甚至是在PHP不同版本之間做切換。
30天成爲Laravel萌新(第2天) - 安装 Laravel
環境需求
這不是全部強制的,只是接下來一個月的時間,會以以下環境為範例:
軟體 | 版本 |
---|---|
Laravel | 5.8.18 |
Nginx | 1.14.0 |
MariaDB | 10.3.15 |
PHP | 7.2.19 |
如同前言所說,雖然Laravel已經釋出6.0版,但接下來將會以5.8為主。此外,也可以使用Apache網頁伺服器,儘管有些設定不同;至於資料庫也可以使用MySQL。並且,以上也都不是強制的,Laravel對於多個網頁伺服器、資料庫接受度良好,所以當然也可以使用PostgreSQL和其他支援PHP的網頁伺服器。
(雖然上面這樣列出,不過最後有可能會用Apache+MySQL再測試一次)
使用XAMPP
明天,我會介紹另外一個快速建置環境的方式,我會更推薦使用該方式。
如果你是Windows,可以直接安裝XAMPP,只是在之後如果遇到問題,請注意一下各個組件的版本。更多可以參考XAMPP的網站。
安裝Laravel
Composer是PHP的一個包管理器,儘管不是必要的,不過可以大量簡化Laravel的安裝程序,並且也可以加以安裝其他組件。因此,需要先確定Composer已經安裝好,並設定好環境。你可以透過 命令提示字元 或其他 Shell 的環境輸入composer -V
,正確安裝完會顯示版本資訊。
安裝Laravel:
composer global require laravel/installer
30天成爲Laravel萌新(第1天) - 認識Laravel
原本,我是想寫下Laravel的介紹,但是…可能有些單調。因為有些經驗實在是 不太多,就算是看別人寫的關於Laravel的特色,和過往PHP開發到底又怎樣差異, 也還是對我而言有些無感。因此決定從我自身角度來介紹Laravel。
Laravel又被人稱為 為網頁藝術家創造的框架 。那是因為,相較於以前PHP 將頁面資料與邏輯代碼混合寫在一起的 義大利麵寫法 , Laravel是類似 Django這樣廣義的MVC框架。也就是將頁面資料與邏輯代碼分開。
那摸到底為神ㄇ要用Laravel? 我簡單列出以下以點:
- 首先,PHP還沒有死亡。實際上PHP在許多地方還是可以看的到。著名的 WordPress、Drupal都是用PHP寫的。在許多與網頁相關應用方面,PHP成熟且 易用,這也讓使用PHP多了一個理由。過去寫過很短的PHP還活 著 可以去參考一下wwww。
- 相比WordPress的易用、易上手,Laravel提供更高度的彈性。
- 遵守Laravel一些基本的開發原則,原始碼更容易維護。(就是有些人一樣能 寫成義大利麵….)
- Laravel提供多個極為好用的基本可選用功能。包含身份驗證、資料驗證、資 料庫分離、ORM、資源控制器、上傳等等等。
30天成爲Laravel萌新(第0天) - 前言
今年有一段時間,短暫的1~2周,因為一些原因學習了Laravel,這次我事後的學習筆記。
Laravel是一個流行的PHP開發框架。不同於熱門的Drupal、WordPress.org, Laravel更像是Node.js的Express.js、Python的Django等等廣義的MVC框架。當然,他也可以快速的發展成CMS。
Laravel目前也發展到6.0,不過接下來幾天的內容會以5.8為主。Laravel也有完整的開發文檔。儘管有些翻譯仍然不完全,但也有多種語言的翻譯。首先,介紹一些學習資源:
- Laravel 官方網站
- Laravel.tw 中文站 文檔最新只到5.3版本
- Laravel中文學院
預計未來內容撰寫的方向:
- 關於Laravel
- 建立Laravel開發環境
- 使用Composer安装
- 使用laradock建立環境
- 認識artisan
- Route - MVC
- 會員系統
- Template
- Controller
- 文章資料庫 - Database Schema
- 商品資料庫 - 驗證資料
- 上傳檔案
- API
Tag: learning
Tag: leetcode
關於C#內建類型的怪異設計(LeetCode:reverse string)
前言
先說一下我的背景,可能會比較了解為什麼我會這樣想。我自己學過C++和Java,這兩個都不算是完全物件導向。相對來說,另外一個蠻愛使用的Python就連基本型態也是物件。至於C#,因為一些個人喜好的因素,並沒有去好好學習。不過也有好一段時間幫朋友除錯,除ASP.NET的錯誤。
之所以能夠幫忙,或快速上手,應該也與C++、Java、C#三者的歷史有關。在C++之後,推出了一個更物件導向的程式語言:Java,最基礎的單獨單位是class
,並且在JVM上運行,號稱可以跨平台、跨設備;隨後,Microsoft為了與Java競爭,並推廣自家的.Net Framework,學習了Java的設計,推出了C#。(早期更是有J#)
基礎型別&基礎類別
所以,三個是很相像的,更重要的是在物件導向的概念上。所以會一個,基本上另外兩個也就應該容易上手。不過C++從C遺留下來的基礎型態int
、char
、float
以及struct
等等,註定讓它不那麼物件。Java可能也有類似歷史因素,而為了處理這樣問題,Java將所有基礎型態做了包裝,一一對應1。
※ Java基本型態對應的類別
Java中有兩個型態系統,基本型態與類別型態,使用基本型態目的在於效率,然而更多時候,會使用類別建立實例,因為物件本身可以攜帶更多資訊,如果要讓基本型態像物件一樣操作,可以使用Long、Integer、Double、Float、Boolean、Byte等類別來包裹(Wrap)基本型態。 –Source: https://openhome.cc/Gossip/Java/Wrapper.html
為什麼會突然覺得C#設計的有點怪?
這主要是最近寫到一題LeetCode,要做字元陣列的反轉。如果使用C#的話,需要使用Array的類別方法。
public class Solution {
public void ReverseString(char[] s) {
Array.Reverse(s)
}
}
Tag: lisp
【閱讀筆記】Common Lisp相關好文閱讀筆記
我對Common Lisp的喜愛應該不用多說。我不知道他還可以帶給我多少驚喜。
節錄幾個Common Lisp文章的相關敘述。
超凡脫俗的極限 - Common Lisp 文/田春
在文中最後寫:
原文: 超凡脫俗的極限 文/田春 鏈接已失效。
我閱讀位置:https://open343.github.io/Writing/zh-cmn-Hant/Overworldly-Common-Lisp.html
這應該是我第二次看,第一次看應該是在原本連結處。
語法
中序表達式可以徹底避免運算符優先級,例如 C 語言的表達式 1+23 在 Lisp 中將寫成 (+ 1 ( 2 3)) ,其中的 + 和 * 都是普通函數的名稱,和其他用戶定義的函數沒有區別。 值得注意的是,小括號的使用並不是必須,只是 Lisp 讀取器的一種標識,完全可以定製。如果用戶喜歡用中括號甚至後序表達式來描述 Lisp 程序,也是有可能的,相關的方法請查詢 Common Lisp 的 get-macro-character 和 set-macro-character 函數。
高度賦予程式成員自由性
Common Lisp 是唯一的允許程序員控制從源代碼到目標程序的所有方面的編程語言 。典型的 Lisp 代碼的處理分為三個階段:讀取、編譯、加載以及執行,其中每個階段都允許程序員介入。
- 在讀取階段,用戶可以設置特殊的讀取宏,用簡潔的形式讀取用戶自定義的對象;
- 在編譯階段,通過定義宏可以執行任意代碼來生成被編譯器所讀取的代碼;
- 在程序加載階段,附加的代碼有機會被執行,例如全局變量的初始化;
- 而在最終的程序執行階段,Lisp 系統還仍然有機會繼續編譯和加載程序的其餘部分,例如補丁,因為包括 compile 和 load 在內的函數是語言規範的一部分。
在讀取階段有set-macro-character
等讀取宏(read macro,我更喜歡使用原文。宏或巨集都不太能表達其強大)。
在編譯階段有defmacro
、define-compiler-macro
等可以使用。
適合嵌入使用的程式語言 在Raspberry Pi上的編譯紀錄
幾乎所有應用都不可能只使用一個程式語言完成。
忘了是那看過的,現在想想,當時他所說的,或許不只是ABI、與C交互、或是組合語言,像是應用層面的HTML,或是通訊成面的HTTP等,應也可以視為程式語言裡的異語言。既然無可避免,那多少了解一些其他語言也是必須的(不過近期打算更進一步XD),不過我自己初衷很單純是「興趣」(原來是興趣使然的程式語言研究員阿)。
之前曾經找過適合作為嵌入式,嵌入到其他程式語言的腳本語言有哪些。
當然是受到最適合、也是以此為目標設計的Lua影響,但Lua及其簡單,就表達層面上,不適合作為複雜應用。
拿個簡單的例子來說,同為原形設計的物件導向程式語言–EMCAScript後來在ES6也有了class
的語法糖。
總之,Lua很簡單,執行效率也極高,這想點我想是相輔相成的,也沒比要為了語法糖提高其語言解析(parse)的難度。但同樣作為一個圖靈完全(Turing completeness)的程式語言,Lua必然也可以實現複雜應用。
但既然Lua已經幾乎達到其設計目標,那如果需要其他設計方法怎辦,作法之一是使用Python這類膠水語言(glue language,Lua也是),但不管是CPython、CRuby還是Node.js恐怕實現都有些過於「肥大」。繞個路的作法就是使用其他實現,接著帶大伙看看幾個我認為有潛力的語言實現。
嵌入式程式語言
Tag: lua
【30天Lua重拾筆記】系列目錄
本系列文章為 第12屆iT邦幫忙鐵人賽 參賽文章。
你可以同時於iT邦幫忙找到本系列目錄。
最全面的Lua入門學習…筆記草稿?No, No, No, No, No 在30天要所有東西提到貌似是不太可能了,但這將會是一個由淺入深的Lua參考筆記。會竟可能涵蓋所有Lua相關核心內容。
Lua非常小,有經驗的人甚至可以在幾小時內熟悉Lua核心基礎內容、幾周內使用進階功能。並且透過輕而小的Lua,或許可以從另一角度重視其他程式語言。是C、Lisp以外,我最為推薦學習的程式語言之一。
本系列包含內容:認識Lua、基礎型別、控制流程、進階概念、範例嵌入C/Java。從頭帶你了解Lua怎麼回事。
系列目錄
【30天Lua重拾筆記35】完賽感想與延伸閱讀
完賽感言
這系列文章在我3月當兵時就開始在規劃了,可是寫出來也還是和原本預計的差了蠻多的,看看我一開始預計撰寫的內容…
起初,我更是想說Lua這麼小,那應該可以非常完整的說明完所有部份吧!但越後面開始規劃每日的文章才發現…絕對會超過30篇。
而且更多時候是寫到一半,發現這邊不得不提到一下之後才會說明的部份。又或者是邊查資料邊寫,結果與預計寫的方向差了不少。又有些時候,因為對Lua已經有一些基礎了解,手打字追不上想寫的思路,或是大思路雖然有了,卻一直在某些地方腦袋打結…
這系列文章,有很多篇也比我預計寫的內容詳細了許多,比起最初可能只是各方面都提到一點點的筆記,儼然已經變成超過我預期內容的一些探討。自己撰寫收穫也頗多的,也還好目前並不算忙碌,才可以這樣做。若要給未來自己一些建議:
- 不管想到的是否完整、好壞,都可以先寫下來。
這次有許多已經先寫下來的小紀錄,最後都變成很常的文章或範例程式。有更多更是在其他文章撰寫下,也不停的修改。 - 不用多,先寫下小小的東西就好。當做種下一顆未來的種子,當未來有需要使用時,再來灌溉、發芽。
- 還是自己寫過、想過的東西,在未來更容以理解。
最後,感謝閱讀完本系列的文章的各位,本文章原本想要深入淺出,但中有諸多未能提及之處,只能在最後分享一些資源。
延伸閱讀
【30天Lua重拾筆記34】番外篇: Fengari - 一個JS實現的Lua,運行Lua在瀏覽器內吧!
幾年前關注過Moonshine和lua.vm.js,不過這兩個項目貌似沒什麼在更新了。Fengari這個這次到又是讓我為之一亮
Lua的實現真蠻多樣的,光是想讓Lua運行在瀏覽器就有不少,像是Moonshine、lua.vm.js、Starlight。有些使用JS;也有些利用了WASM、emscripten。Fengari是屬於前者的實現,是JS實現的版本。其除了可以在瀏覽器執行外,也提供了基於Node.js的執行器。這是一個蠻新興的項目,整體設計粗淺看來也相當不錯而且完整,今天會略微介紹一下,但建議可以先閱讀閱讀為什麼我們使用JS重寫了Lua?(英文)
Fengari是希臘文「月亮」的意思
於瀏覽器執行Lua
【30天Lua重拾筆記33】Java + Lua計算機
這是我前幾年作為學習/練習的例子。
看過與C交互後,接著來看看一個更實際應用的例子。不過不用C,來用Java。
為甚麼呢?Java自帶一個跨平台的視窗開發套組,本身也有豐富的函式庫可用,第三方函式庫也眾多,作為宿主語言是蠻好的標的。不過不直接使用原本的Lua,需要使用LuaJ。
其實最初只是因為Android App使用Java開發。而當時Android Studio編譯APK實在太慢,才會有為啥不能先用其他方式寫邏輯、開發,為啥不先嵌入一個腳本語言?才去做的一個練習,之後我使用的套件也確實有用於Android App開發上。
是的,如果你是正在開發Android,不管是以前主要用Java還是後來的Kotlin,今天應該都還是可以使用LuaJ。
LuaJ已經停滯好一段時間了,本次範例的為GitHub上另一個分支。基本上遵循Lua 5.2的規則,也就是本系列有部份是Lua 5.3、5.4的語法並不能使用於之。
目標
本次範例主要目標:一個含有GUI的簡易計算機。其包含:
- Java提供GUI界面,和部份功能。
- Lua處理邏輯。
- 一個擴展方法,能夠改變成品行為。
使用Lua撰寫邏輯
使用Lua的好處之一,在不確定要以和總平台開發前,是可以先使Lua撰寫邏輯,最後在嵌入到某個平台或框架。
【30天Lua重拾筆記32】進階議題: LuaRocks & LuaDist
LuaRocks
LuaRocks是類似npm、pip這樣的套件管理工具,你可以在上頭找到近4000個別人已經寫好的模組。
下載/安裝LuaRocks
在示例前,你需要先下載安裝好LuaRocks,若要下載其他版本,可以在這個頁面尋找看看。
在Linux上,你只需要將其解壓縮於可執行路徑底下即可。Windows可以參考官方說明。
使用LuaRocks
初始化環境
你可以不用做這一步。略過這一不的話,之後安裝得套件會存在於全局裡。有這樣的作法,主要是用於開發其他套件時使用。
mkdir example
cd example
luarocks init
搜尋套件
【30天Lua重拾筆記31】進階議題: 記憶體回收&弱表
TL;DR:
不要去修改預設值,除非你知道在做什麼
Lua會自己做記憶體回收,絕大多數時候不必為記憶體分配、管理而操心,而且通常它做的很好。但如果真的因為記憶體回收而影響到程式效率的執行,且你確定你有足夠的記憶體,你可以暫停讓Lua執行記憶體回收的操作:
停止收集記憶體垃圾
collectgarbage("stop")
collectgarbage("isrunning") --> false
或者,如果你是嵌入在C,可能會更傾向使用:
lua_gc(L, LUA_GCSTOP)
收集記憶體垃圾
如果要恢復,也只要:
collectgarbage("restart")
collectgarbage("isrunning") --> true
或是
【30天Lua重拾筆記30】進階議題: 與C交互(+Python)
Hello, Lua & C
現在,我們來嘗試從C去執行一個Lua程式,Lua程式就用最簡單的Hello,並命名為hello.lua
print "Hello"
然後來寫C程式 – hello_C.c
。
引入標頭檔
需要下載含有標頭檔和函式庫的版本
#include "lua.h"
#include "lauxlib.h"
建立Lua虛擬機
// new a lua VM
lua_State *L = luaL_newstate();
打開預設的所有函式庫
通常而言,不會全部開啟所有功能。
這只是範例,讓hello.lua
檔案擁有所有能力。
// open all libraries
luaL_openlibs(L);
執行Lua檔案
【30天Lua重拾筆記29】進階議題: 物件導向程式設計
與ECMAScript相同,採用原形設計的物件導向。你可以與7天搞懂JS進階議題相互服用,可能會有意想不到的效果。
模擬封裝
Lua並沒有直接保護內部資料的方法,你可能會需要使用 閉包 、 弱表 、 metatable 等來達成條件。
但今天只討論封裝裡最簡單的概念–集成,也是物件的基礎:將相關的物件、方法關連到一個結構裡面。沒錯,就是 table
,在本文中使用物件(object)一詞基本可視為同義。
Lua的
table
也確實很像是JS裡的Object
,但可能更像是Map
(一個ES6後出現的新類別)。
最基礎的資料結構概念是雜湊表。
建立物件1
建立物件(object
)就是建立表(table
),非常簡單:
coco = {
name = "桐生ココ",
age = 3501,
birth = {month = 6, day = 17},
slogan = "good morning mother f**ker",
say = function (self)
print(self.name .. ": " .. self.slogan)
end
}
coco:say() --> 桐生ココ: good morning mother f**ker
工廠模式
但我們總不能每次都這樣直接建立物件,或許可以由一個加工工廠(函數)來處理?
【30天Lua重拾筆記28】進階議題: Meta Programming
Meta Programming / 元程式設計
元程式設計(英語:Metaprogramming),又譯超程式設計,是指某類電腦程式的編寫,這類電腦程式編寫或者操縱其它程式(或者自身)作為它們的資料,或者在執行時完成部分本應在編譯時完成的工作。多數情況下,與手工編寫全部代碼相比,程式設計師可以獲得更高的工作效率,或者給與程式更大的靈活度去處理新的情形而無需重新編譯。 – 維基百科
簡單說,元程式設計,就是讓「程式能夠編寫程式」,改變程式運行的部份行為。Lua本身具有部份如此的能力,舉例來說,如果想要建立一組變數A-Z
,或許正常會這樣子寫:
A = 1
B = 2
C = 3
-- ....
Z = 26
但Lua可以有更聰明(hacking)的寫法:
for i=65, 65+25 do -- ASCII Code of A is 65
print(string.char(i))
_ENV[string.char(i)] = i - 64 -- 1 to 26
end
還記得_ENV的作用嗎?
【30天Lua重拾筆記27】進階議題: debug
Lua本身並沒有獨立的debugger相關工具,但他有一個強大的內置套件— debug。
打印調錯訊息traceback
debug = require "debug"
do
local ten = 0
function div10(n)
print(debug.traceback()) -- 打印調錯訊息
return n / ten
end
end
更新變數值
透過直接觀看函式內容,可以知道這個函式div10
並不符合預期。
do
local ten = 0
function div10(n)
return n / ten
end
end
所以需要改變ten
值,但這次不直接修改程式原始碼。我們得先了解怎麼取得閉包變數ten
:
【30天Lua重拾筆記26】進階議題: 錯誤處理
作為一個寄宿型的嵌入式語言,Lua設計更傾向由宿主語言(通常是C)處理錯誤。
但是可以在保護模式下,執行函式,並檢查函式是否執行成功。
很像是Go語言。這就是Lua的錯誤處理基本方法。
錯誤處理
作為一個寄宿型的嵌入式語言,Lua設計更傾向由宿主語言(通常是C)處理錯誤。
num = 10
str = "string"
print('Hello, Lua')
result = num / str -- Error: attempt to div a 'number' with a 'string'
print('here will not show, because error hapend before')
一般來說,並不傾向於Lua處理最後,因為數字與字串相除,所引發的錯誤。這個錯誤會持續引發至宿主語言,由宿主語言進行錯誤處裡。
【30天Lua重拾筆記25】進階議題: 模組化
Lua並沒有完整的模組系統,更多的是依賴模組開發者的設計。在Lua 5.1曾經有module()
的函數可用,但於Lua 5.2已經被移除。更多的需要使用_G
、_ENV
來使用,相關說明可以參考全局表與環境表。
module (name [, ...])
在require()
之前,需要先說一下如何載入程式檔案並執行。
載入檔案
對於模組化設計,最重要的一點是如何切割程式為不同檔案,接著就是如何串連不同檔案。在之前介紹過load
,Lua提供了另一個類似的方法loadfile
。
示例
首先先建立個 hello.lua 檔案,簡單就好:
print "Hello, World"
然後才是主程式 loadfile_hello_example.lua :
hello = loadfile("./hello.lua")
hello() --> Output: Hello, World
如果你分別於REPL環境打入這兩行指令,你會發現第一行指令並沒有任何輸出結果,只得到一個hello函數。只有在第二行執行函式時,才真正執行hello.lua程式檔案的內容。
執行檔案
【30天Lua重拾筆記24】中級議題: coroutine
coroutine
Lua提供coroutine
的函式庫,使其有能力編寫不同模式的程式。
thread create
你可以透過coroutine.create()
建立一個thread
。
t1 = coroutine.create(function() print("Hello, World") end)
print(type(t1)) -- Output: thread
Lua並不是多執行緒的,其thread
是輕量的。儘管Lua本身沒有異步(async)的寫法,但可以創造出異步的寫法。相對而言,值執行順序是明確許多的。
thread running
可以透過coroutine.resume()
去觸發一個thread
的執行:
coroutine.resume(t1) -- Output: Hello, World
傳入參數
可以對一個剛建立好的thread
傳入參數。
function hello(name)
while true do
coroutine.yield("Hello, " .. name)
end
end
t1 = coroutine.create(hello)
print(coroutine.status(t1)) -- suspended
print(coroutine.resume(t1, "Bob")) -- Output: Hello, Bob
print(coroutine.status(t1)) -- suspended
print(coroutine.resume(t1, "World")) -- Output: Hello, Bob
thread close
【30天Lua重拾筆記23】中級議題: 閉包
變數的查找
對於一個變數,Lua會先嘗試從當前詞法環境(Lexical)尋找,再從當前環境中尋找(
_ENV
)。
那的對於區塊變數呢??
function parentFunction()
local L1 = 100
local function childFunction()
print("here is child")
end
return childFunction
end
f1 = parentFunction(100)
f1() -- Output: here is child
上例中,區塊變數L1
沒有任何人可以存取的到,簡直消失在了時空夾縫之中。
詞法環境(Lexical)
詞法環境(Lexical)指的是程式編寫當下,執行區塊由內而外,可以見到的範圍。
隱藏區塊變數
在Lua變數預設值為nil
。透過查找規則,可以暫時隱藏一切區塊變數。
【30天Lua重拾筆記22】中級議題: 全局表(_G)、環境表(_ENV)
_G
和_ENV
在Lua有兩個特殊變量–_G
和_ENV
,其分別表示全局環境和當前環境。_G
在與C交互時,另有作用。但大致上你可以將兩者視為相同。實際上,在Lua環境建立之初,兩著也確實是相同的:
print(_G == _ENV) -- => Output: true
先前說過,Lua只有表(table
)這個複合結構。而_G
和_ENV
也是table
結構。_ENV
中包含一個_G
的key
,其值指向_G
,而起出_G
就是_ENV
。像是一個咬尾蛇(Ouroboros)
print(_ENV._G == _ENV) -- => Output: true
print(_G == _ENV) -- => Output: true
print(_G._G == _ENV) -- => Output: true
print(_G._G == _G) -- => Output: true
變數查找
對於一個變數,Lua會先嘗試從當前詞法環境(Lexical)尋找,在從當前環境中尋找(_ENV
)。這意味著你可以準備一個乾淨的執行環境執行函數。
【30天Lua重拾筆記21】基礎3: 再看pairs, ipairs
ipairs()
的行為
iparis會嘗試從索引1開始迭代表(陣列),直到其值為nil
。所以很像是:
arr = {1,2,3,4,5}
function my_ipairs(t --[[table]])
local i = 1
while t[i] ~= nil do
print(i, t[i])
i = i + 1
end
end
my_ipairs(arr)
1 1
2 2
3 3
4 4
5 5
也因此其如果有斷值,會在一半中止:
arr = {1,2,nil, 4, 5}
my_ipairs(arr)
1 1
2 2
pairs()
和next()
【30天Lua重拾筆記20】基礎3: 複合結構 - table
Lua只有一個原生的複合結構 – table
。實際上陣列是table
的特例。
陣列是table
的特例
arr = {1,2,3,4}
print(type(arr)) --> table
建立表(table
)
table
對應於:
程式語言 | 型別 |
---|---|
python | dict |
js | object or Map |
這個結構幾乎是現代高階語言必備的基礎。其又稱作hash-table
,是一個鍵值對(key/value)的結構。在Lua之中,其key可以是除了nil
和NaN
(Not a Number)以外的任何型別。
key值值域
obj = {} -- 建立一個空表
obj[1] = 1 -- 整數是合法的key值
obj[1.0] = 2 -- 浮點數是合法的key值
obj["string"] = 1 -- 字串是合法的key值
obj[math.huge] = 1 -- inf是合法的key值
--[[
要注意的是 obj[1] 和 obj[1.0]相同
其obj[1]和obj[1.0]最終值為2
--]]
----------------
print(obj[nil]) --> nil
obj[nil] = 1 --> Error: nil不是合法的key值,儘管取值不會出錯
print(obj[0/0]) --> nil
print(0/0) --> -nan
obj[0/0] = 1 --> Error: NaN不是合法的key值
value值值域
其value可以是nil
以外的值。
nil表示該key不存在,亦可視為刪除該key
obj[1] = nil -- 刪除`obj[1]`
print(obj[1]) --> nil --表示obj[1]不存在
【30天Lua重拾筆記19】基礎3: 陣列從1開始
建立陣列
關於陣列,其實也已經看過了。不過其實陣列還有兩個祕密,一個今天會揭露,另一個等等明天。
要建立一個陣列很簡單,很像C語言,只是把中括號[]
改成大括號{}
,像是:
programming_language = {
"C",
"C#",
"C++",
"Java",
"Swift",
"Python",
"Haskell",
}
陣列取值
陣列從1開始
與C語言一樣,使用下標運算取陣列之中的值, 要注意的是,Lua陣列從1開始
print(programming_language[1]) -- => Output: "C"
陣列長度
可以使用長度運算#
,取得陣列長度:
print(#programming_language) -- => Output: 7
迭代陣列
知道陣列取值方式,也知道陣列長度後,就可以來迭代陣列:
一般for迴圈方法
for i=1, #programming_language, 1 do
print(i, #programming_language[i], programming_language[i])
end
看我小巧思,每個值的長度,正好與其對應的索引值相同。
for-in + ipairs() 方法
可以寫的更簡單點:
【30天Lua重拾筆記18】基礎2: 應該知道的(總集+補充)
關於變數
- 值(value)有型別;變數(varible)沒有
- 基礎型別一共有8個
nil
boolean
number
string
function
userdata
thread
table
- table是唯一的複合型別(之後會提到)
- Lua 5.4之後,可以為變數標記屬性。Lua 5.4支援
const
和close
兩種屬性 - 變數預設是全局變數(實際上與特殊變數
_ENV
和_G
有關,之後會提到) - 變數值型別會自動轉型,但不該依賴
數字
- 整數和浮點數會自動轉換
- 整數有範圍,有溢位問題
- 浮點數也有精度丟失的問題
布林
- 只有
nil
和false
為假,其他為真 0
、空字串、空表亦為真
控制結構
- 有label &
goto
- label只屬於當前函式區塊
- 使用
elseif
,而非else if
(這點我覺得C就設計的比較漂亮)- 嵌套使用
if
(else if
),記得補上end
- 嵌套使用
- 非強求,但最好做好排版
【30天Lua重拾筆記17】基礎2: pcall, xpcall, load (eval, exec, apply)
eval / load
作為一個直譯的環境,幾乎一定會有一個與eval
等價的能力,不過在Lua叫做load
,與其他程式相同,這個功能是強大而應該小心使用的。下方程式會新建一個全局變數g1
,使這個宣告過程原本是一段字串。
load([[
g1 = 1 -- create a global variable g1
]])()
print(g1) -- => Output: 1
要注意的是,load
不回直接執行,其實其返回一個包裝函式:
f = load[[g2 = 2]]
print(type(f)) -- => function
print(g2) -- => Output: nil
f()
print(g2) -- => Output: 2
ECMAScript
類似的JS也有。
不過相較於JS,Lua其實有更安全的作法(以後會提到)。
eval('var g1 = 1')
console.log(g1) // => Output: 1
Python
Python有兩個函式做類似事情–eval
和exec
。不過eval
雖有返回值,但無法執行語句,只能執行表達式。所以本例中只能使用exec
。exec
永遠回傳None
,比起eval
更加強大。
【30天Lua重拾筆記16】基礎2: 多值返回&具名參數
回傳多值/多值返回
Lua函數可以返回多值。在我看來,這個特性是特殊的,只有少數語言真正做到多值返回。什麼意思?這表示在接收一個函數的返回值,可以輕易忽略掉主要值以外的結果。這些結果一般用於輔助,絕大多數時候,我們不關心、不需要,但有時候非常有作用。
先來看看Python裡的dict.get:
_d = {}
_v = _d.get("key", "value")
print(_v) # => Output: value
大多數時候,並不關心_v
的值是否真的從_d
來的,還是其實是個預設值。如果我們想要確定,那就要在多寫一次:
found = "key" in _d
我們可以替Lua寫一個相似的函數,但其還多返回一個用於解釋是否是有找到的值:
function get(dict, key, default)
if dict[key] ~= nil then
return dict[key], true
else
return default, false
end
end
其實可以在簡化一些:
function get(dict, key, default)
return (dict[key] or default), dict[key] ~= nil
end
這麼一來,平常可以這樣用:
【30天Lua重拾筆記15】基礎2: Label and Goto
Label & goto
這是一個強大的工具,要寫的漂亮並不容易,許多語言禁止了他。
Lua保有他。他很靈活,但你也應該慎重考慮是否真的應該使用。
而我認為,開發人員應該要是自由的,這才有創造的價值,這才能體現思考的藝術。就如同松本行弘說的:「語言體現思考的價值」
如果你的想法最初就是這樣想的,就先寫下來吧!「童子軍原則」別忘了逐漸改得更健壯。
Lua保有C/C++裡,goto
的能力,可以跳轉到函數區塊內任意可見的標籤(Label)。標籤的形式由兩個冒號夾成:
:: <Label Name> ::
跳出多層迴圈
BTW, Python 認為如果你需要使用到這個功能,表示你應該在拆分程式碼。
有了這強大的能力可以做啥?可以學像ES6那樣跳出外部迴圈:
九九乘法表只計算到6x4
(()=>{
outer:
for(let j = 2; j <= 9; j++){
let line_output = ""
for(let i = 1; i <= 9; i++){
line_output += `${j}x${i}=${j*i},\t`
if(i > 3 && j > 5){
console.log(line_output)
break outer
}
}
console.log(line_output)
}
})()
【30天Lua重拾筆記14】基礎2: 控制-while、repeat迴圈
print("鐵人賽開始")
for day=1,30,1 do
print("第" .. day .. "天: 每天寫文章")
end
print("鐵人賽結束")
for
迴圈適合用於次數明確的情況。例如鐵人賽30天發文不間斷,每天發一篇,發完30完賽。向這樣知道進步條件(每天一篇),中止條件(30篇),就非常適合用 for
迴圈。
Repeat/until迴圈
但有時候只知道中止條件,過程並不確定。像是在第n天,就寫n篇文章來囤稿,對我而言會是幾天完賽呢?
print("鐵人賽開始")
day = 1 -- 第幾天
completed = 0 -- 完成篇數
repeat
completed = completed + day
print("第" .. day .. "天: 每天寫文章,完成"..completed.."篇")
day = day + 1
until completed > 30
print("鐵人賽結束,完成篇數:"..completed) -- Output: 鐵人賽結束,完成篇數:36 -- (1+8)*8/2
鐵人賽開始
第1天: 每天寫文章,完成1篇
第2天: 每天寫文章,完成3篇
第3天: 每天寫文章,完成6篇
第4天: 每天寫文章,完成10篇
第5天: 每天寫文章,完成15篇
第6天: 每天寫文章,完成21篇
第7天: 每天寫文章,完成28篇
第8天: 每天寫文章,完成36篇
鐵人賽結束,完成篇數:36
需要每天寫文章,直到累積的文章超過30篇,也就完賽了。如果在第n天寫n篇,也只需要8天就完賽。
While迴圈
【30天Lua重拾筆記13】基礎2: 控制-For迴圈
相較於if
,Lua的for
迴圈有兩種,或說是三種。
進步的for迴圈
印出1-10:
for i = 1, 10, 1 do
print(i)
end
很類似C語言
#include <stdio.h>
int main(void){
int i = 0;
for(i = 1; i <= 10; i++){
printf("%d\n", i);
}
}
不同的是以逗點代替分號,中止條件相同時仍會在執行一次,進步(step)使用表達是,會自動相加並重新賦予值。
了解後也可來寫成10到1的程式:
for i = 10, 1, -1 do
print(i)
end
for-do
也是一個區塊,於一開始宣告的變數僅do-end
內部可見。這表示下方寫法中,進步條件指的是外部的i
,其值為1
。結果與輸出1-10相同。
i = 1
for i = 1, 10, i do
print(i)
end
pairs & ipairs初探 - for-in迴圈
for-in
迴圈用於迭代陣列(array
)或表格(table
)時。
Lua是沒有array型態的(還記得8種基礎型別嗎),關於這點之後會在說明。
至於什麼是table
?可以想像成是ES6裡的object
,或者更接近於Map
可以分別用ipairs
和pairs
來處理:
迭代陣列
【30天Lua重拾筆記12】基礎2: 控制 - 條件
分支條件控制 - if/elseif/else
Lua的分支控制條件就僅有這麼一組:if-then
/elseif-then
/else
/end
和其他語言一樣,elseif
可以出現多次。你想用else if
寫成巢狀也沒人會管你。他們兩個寫起來也真的蠻像的,除了你可能要多寫幾次end
:
if true then
print("if block")
elseif true then
print("elseif block")
else
print("else")
end
if true then
print("if block")
else if true then
print("elseif block")
else
print("else")
end
end
注意到最後有兩個end
。實際上我試故意將他這樣排版的,這樣兩個看起來比較像。
通常需要使用到巢狀分支,還是好好縮排比較好。
switch
雖然只有if
蠻簡單的,但有時候會想要使用switch
這類結構。沒事,可以模擬:
option = "one"
switch = {
["one"] = function () print "run one" end,
["tow"] = function () print "run two" end,
}
switch[option]() -- => run one
【30天Lua重拾筆記11】基礎1: 註釋
基礎2: 註釋
--[[
{
author = "lagagain",
date = 20200904,
title = "Comments of Lua code",
}
--]]
註釋是一段永遠不會被執行的區塊。你可以在註釋區塊隨意寫任何東西,而不必遵守任何Lua的語法規則。但更好的,註釋應該是作為程式碼的部份補充。至今為止的幾天,其實也過不少註釋。像是:
------------------
阿勒? 😲 這不是分隔線嗎?
單行註釋
是的,最基本的單行註釋以 --
開頭,直至行尾都會被視為註釋。也看過:
do
local a = 1
local b = 2
local sum = a + b -- sum = 1 + 2 => 3
end
或是
print("Hello, World") -- Output: Hello, World
本系列會部份以這樣方式去說明程式碼,程式碼的執行結果。不過在寫註釋時,最好是明確的補充程式碼的意義,像是:
【30天Lua重拾筆記10】基礎1: 類型 - 布林和nil
nil
nil
是Lua裡的一個特殊值,代表什麼也沒有。其型別也是nil
type(nil) -- => nil
布林
布林值只有true
和false
真值
只要不是nil
或是false
都為真, 包含0、空表、空字串 。
這與目前多數主流語言不同,需要特別注意!
【30天Lua重拾筆記09】基礎1: 類型 - 函數
函數 宣告
函數可以使用function
來做宣告,並以end
結束。
function hello()
print("Hello, World")
end
實際上這是Lua的語法糖,上面相當於:
hello = function()
print("Hello, World")
end
函數也是第一公民
在Lua,函數是第一公民,是基本型別,可以榜定於變數,也可以作為參數傳遞,或是回傳 值。
function genFun()
return function()
print("Hello, World")
end
end
function callFun(f)
f()
end
local _f = genFun() -- # get a function
callFun(_f) -- print("Hello, World")
函數、區域變數與閉包
函數同時也是一個區塊,可以保有區域環境變數,這樣的特性可以用來做閉包。
【30天Lua重拾筆記08】基礎1: 類型 - 字串
關於字串
與Python相同,字串是不可變得。但Lua字串於內部表示時,完全採用8-bits表示,包含0(\0
)。這也是為什麼在基礎1: 變數一篇,會特別說明\u{1F603}
的使用。
所以字串如果取其長度,可能和你想像的不一樣:
-- note: with UTF-8
print(string.len("我")) -- equal print(#"我") => 3
不過Lua是認得UTF-8的,其本身可以協助不支援UTF-8的程式語言(ex: C語言)。
-- note: with UTF-8
print(utf8.len("我")) -- => 1
C90包含<wchar.h>; C11有<uchar.h>。
儘管如此,不表示就是UTF-8寬字元(Wide character) 是電腦抽象術語(沒有規定具體實現細節),表示比8位元字元還寬的資料類型。不同於Unicode。 – 維基百科
因為Lua認得UTF-8,所以其原始碼最好以UTF-8儲存,否則可能執行會與想像不同。
字串的表示式
要表示字串在Lua有多種方式。
字面字串
簡單表示
【30天Lua重拾筆記07】基礎1: 類型 - 數字
整數與小數
數字(number)是Lua的基礎型別之一。Lua會自動判斷是整數還是小數,會自動轉換,無明確分界。
1.0 == 1
list = {"Bob", "Lua", "Luna", "Selene"}
list[1] == list[1.0]
type(1.0) -- => number
type(1) -- => number
雖然會自動轉換,不代表沒有區別
math.type(1.0) -- => float
math.type(1) -- => integer
溢位(overflow)
Lua 5.4使用64位元的整數和浮點數(雙精度浮點數double)。
math.type(9223372036854775807) -- => integer
math.type(9223372036854775808) -- => float
這表示Lua還是有可能溢位
9223372036854775807 + 1 -- => -9223372036854775808
精度丟失
浮點數的表示也有極限,可能會有精度丟失的問題
【30天Lua重拾筆記06】基礎1: 變數
變數名稱
Lua的變數名稱可以是底線(_
)或是任意字母([a-zA-Z]
)開頭,不能是數字或其他字元。之後的組成可以包含數字([0-9]
)、字母([a-zA-Z]
)或底線,並且是大小寫敏感,abc
、Abc
、ABC
可以作為三個不同的變數。
Lua作為嵌入型語言,使用最基本的ascii code。儘管其本帶有utf8函式庫,也可能有部份實現允許其他字元作為變數名稱,但不建議這樣做,並且在使用到非ascii字元時,最好以\u{1F603}
這樣方式來寫。像是:
print("\u{1F603}")
print('😃')
上面兩著等價,或是下面兩者亦相同。
print("\u{4f60}\u{597d}\u{ff0c}\u{4e16}\u{754c}\u{ff01}")
print("你好,世界!")
當然其實Lua是了解utf-8的,本系列也不會以上述方式撰寫,所以請確定儲存的程式檔案是以utf-8儲存,否則執行結果可能不如預期。
此外,Lua的底線開頭的變數,和Python一樣有時也代表一些意義,這會在說道metatable和物件導向程式設計時說明。
變數可見範圍
與多數程式語言一樣,分有全局變數和局部變數。不同的是,預設變數可見範圍是全局的(類似JS的var變數)。
g1 = 1
function f()
print(g1)
g2 = 2
end
f()
print(g2)
【30天Lua重拾筆記05】基礎1: 程式區塊(block、chunk)、排版
Lua的關鍵字
Lua的關鍵字並不多,就只有這麼幾個而已:
and | break | do | else | elseif | end |
false | for | function | goto | if | in |
local | nil | not | or | repeat | return |
then | true | until | while |
區塊(block)
有一些關鍵字會組合成程式區塊(block)。像是:
funciton
-end
do
-end
if-then
-end
while-do
-end
repeat
-until
夾於這些關鍵字中間的程式碼會成為一個環境區塊。
在區塊環境內,可以保有一些局部變數:
do
local name = "World"
print("Hello, "..name)
end
print(name)
Hello, World
nil
chunk
【30天Lua重拾筆記04】基礎1: Hello, Lua!
假設你已經選擇好並安裝 Lua的實現,且也準備好開發環境。使用過lua -v
沒問題後,就可以來試試看今天的入門示範程式。
你不必馬上了解今天的所有內容,將來都會說明到。今天只是快速的看看Lua程式長什麼樣子。
你可以執行lua
,在REPL交互式環境執行範例程式碼,也可以另存檔案,並用lua file.lua
執行看看。
第一個程式 - Hello, World
第一個程式?當然從打印出Hello, World開始。
Hello, World是指在電腦螢幕顯示「Hello, World!」(你好,世界!)字串的電腦程式。相關的程式通常都是每種電腦程式語言最基本、最簡單的程式,也會用作示範一個程式語言如何運作。同時它亦可以用來確認一個程式語言的編譯器、程式開發環境及運行環境是否已經安裝妥當。 – 維基百科
print "Hello, World"
Hello, World
不過這也太簡單了,來換個方式試試。
給個名字 - Hello, Lua
【30天Lua重拾筆記03】開發環境配置
開發環境配置
接著,來配置一下開發環境。主要會介紹三個開發環境,當然你想使用純文本編輯器也可以,我就是使用Emacs。
我會建議初學的人只使用代碼高亮的功能就好,最多…就使用到查找文件說明。 儘管今天介紹的配置都包含自動補全、格式美化、定義跳轉等等功能。但初學的人應該更關注在於其語法。(Lua語法也蠻自由的就是)
ZeroBrane
ZeroBrane是一個相當完整的Lua IDE,你幾乎可以直接下載下來使用。
其已經包含數個Lua版本,並可以輕易切換。
他也包含許多IDE應該要有的功能:
- 跳轉到函式定義
- 自動補全
- 除錯器
【30天Lua重拾筆記02】Lua的實現與選擇
Lua的實現與選擇
Lua的意思是葡萄牙文的「月亮」,其LOGO和其他相關也多與月亮有關。在開始使用學習Lua之前,比須先了解Lua的幾個版本與實際實現。
就像Python 2和Python 3有很大不同,Python 3各版本間又有些許不同。有些在Python 3.9能用的語法或功能,不一定可以在Python 3.5使用一樣。
Lua目前已經到了5.4版本,本系列內容也會以Lua 5.4為主。Lua 5.4於2020年釋出,所以還非常的新,有許多實現實際未達到這個標準。但Lua 5.1、5.3也已經使用多年,穩定度是可見的。就算是其餘版本的實現,也具有一定可用性。Lua設計極小,就某種程度上而言,甚至可以相對輕鬆的撰寫自己的實現。類似的於C語言,但是沒有C語言煩人的指標概念。所以Lua的實現也不少,其中著名的有:
極高效即時編譯的Lua實現。相容於Lua 5.1語法。
LuaJIT is compatible to the Lua 5.1 language standard. It doesn't support the implicit arg parameter for old-style vararg functions from Lua 5.0.
用於嵌入式設備開發的Lua,提供許多已經寫好的內容。(沒記錯的話也是使用Lua 5.1標準,沒用過)
Java版的實現,支援Lua 5.2的語法。在GitHub上有另一個Fork的版本,兩個有些差異。
JS實現的版本。其他JS實現的版本還有:Moonshine、lua.vm.js、Starlight。這是一個很新,也有點有趣的項目。我會在談談它。
C#的實現。
【30天Lua重拾筆記01】-認識Lua
認識Luna
盧娜(Luna,又寫作露娜或路娜)是羅馬神話中的月亮女神。「Luna」在法語和義大利語中也有月亮或月神的意思。在希臘神話中她的對應者為塞勒涅。盧娜也常常和黛安娜或赫卡忒混淆在一起。在羅馬的阿文提諾山上建有供奉她的神廟。 — 維基百科
錯棚了…
不是她 😅
再來一次 - 認識Lua
Lua是葡萄牙文的月亮,是一個輕量、快速、容易學習且容易嵌入的程式語言。其目標本就是成為一個很容易嵌入其它語言中使用的語言。其精簡的核心只包含一些最基本的功能,啟動速度非常之快。 儘管如此,透過最基本的功能,甚至可以實現多種編程範式。
可嵌入的程式語言
適合嵌入使用的程式語言 在Raspberry Pi上的編譯紀錄
幾乎所有應用都不可能只使用一個程式語言完成。
忘了是那看過的,現在想想,當時他所說的,或許不只是ABI、與C交互、或是組合語言,像是應用層面的HTML,或是通訊成面的HTTP等,應也可以視為程式語言裡的異語言。既然無可避免,那多少了解一些其他語言也是必須的(不過近期打算更進一步XD),不過我自己初衷很單純是「興趣」(原來是興趣使然的程式語言研究員阿)。
之前曾經找過適合作為嵌入式,嵌入到其他程式語言的腳本語言有哪些。
當然是受到最適合、也是以此為目標設計的Lua影響,但Lua及其簡單,就表達層面上,不適合作為複雜應用。
拿個簡單的例子來說,同為原形設計的物件導向程式語言–EMCAScript後來在ES6也有了class
的語法糖。
總之,Lua很簡單,執行效率也極高,這想點我想是相輔相成的,也沒比要為了語法糖提高其語言解析(parse)的難度。但同樣作為一個圖靈完全(Turing completeness)的程式語言,Lua必然也可以實現複雜應用。
但既然Lua已經幾乎達到其設計目標,那如果需要其他設計方法怎辦,作法之一是使用Python這類膠水語言(glue language,Lua也是),但不管是CPython、CRuby還是Node.js恐怕實現都有些過於「肥大」。繞個路的作法就是使用其他實現,接著帶大伙看看幾個我認為有潛力的語言實現。
嵌入式程式語言
Tag: machine-learning
深入了解scikit Learn裡TFIDF計算方式
TFIDF計算說明
參加今年iT鐵人賽時,曾經寫過簡單使用scikit-learn裡的TFIDF看看,並寫到scikit-learn裡tfidf計算方式與經典算法不同。後來在官方文件中找到說明,也簡單嘗試了一下。這次來做點分享。
在經典算法,TF是這樣計算:。不過scikit-learn是直接用
n_(i,j)
,也就是使用CountVectorizer
的結果。
IDF的部份,原本經典算法是:,為了避免分母為零,也經常會使用
log(n/(df(k)+1))
計算,也就是將分子+1
。scikit-learn裡面則分成兩種,預設使用smooth
的版本:log((n+1)/(df(k)+1))
,也就是分子分母都加一;另一種是經典原始版本,而外加上1:log(n/df(k))+1
。
最後sckit-lean會做標準化(normalize),所以最後結果會是normaliz(tf*idf)
。
嘗試實驗
引入套件
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
import math
from sklearn.preprocessing import normalize
實驗資料
d1 = 'a b d e d f a f e fa d s a b n'
d2 = 'a z a f e fa h'
d3 = 'a z a f e fa h'
對,基本上就是簡單使用scikit-learn裡的TFIDF看看所測試的方式,所以當然也要將stop_words=None
, token_pattern="(?u)\\b\\w+\\b"
而外設定,原因這裡就不多做說明了。
Tag: mcmc
Tag: oauth
【用Keycloak學習身份驗證與授權】系列目錄
本系列同樣發表於iThome體人賽 - 用Keycloak學習身份驗證與授權。
本頁後面還有一些小後記喔~
- 【用Keycloak學習身份驗證與授權01】Quick Start(1)
- 【用Keycloak學習身份驗證與授權02】Quick Start(2)
- 【用Keycloak學習身份驗證與授權03】淺談身份驗證與授權(1)
- 【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
- 【用Keycloak學習身份驗證與授權05】什麼是Keycloak
- 【用Keycloak學習身份驗證與授權06】Keycloak的替代品
- 【用Keycloak學習身份驗證與授權07】什麼是OAuth
- 【用Keycloak學習身份驗證與授權08】OAuth 2
- 【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
- 【用Keycloak學習身份驗證與授權10】深入OAuth 2
【用Keycloak學習身份驗證與授權33】Device Code(4)
這次應用使用PySide
來實現界面;qrcode
來產生需要的QR Code;並使用requests
來與身份驗證與授權伺服器的API溝通。現在透過pip
進行安裝需要的packages。
pip install PySide6 requests qrcode
其實本來可以考慮用electron.js,但是基於一些考量,最後決定使用PySide。
在昨天,透過Qt Designer建立了兩個需要的使用者界面,今天來實現邏輯部分。
建立Widget
在之前所設計的ui檔案分別是:example-device-code-app.ui
和login-dialog.ui
。這部分會分別將這兩份載入到類別內使用。所以同樣來建立兩個Widgets:ExampleDeviceCodeApp
和loginDialog
。
【用Keycloak學習身份驗證與授權32】Device Code(3)
本文接續device code(2)
現在已經知道了Device Code的登入流程了,那麼實際應用起來是怎麼樣的呢? 本片來實現一個可以使用Device Code Flow登入的應用。
使用者界面設計
首先,與「快速開始」應用相同,同樣需要一個顯式使用者資訊的地方,以及登入與登出的按鈕。
是的非常簡單。但悄悄先回到RCF8628,有一部分描述使用者界面的範本。該界面建議包含:操作說明、登入連接和user_code
。
【用Keycloak學習身份驗證與授權31】Open ID Connect & Social Login(2)
Keycloak Open-Id Connect
其實除了使用GitHub等社群帳號登入外,Keycloak也可以作爲Open-Id登入的提供者(Provider)。接著需要使用Keycloak本身來實現社群帳號登入,因爲這樣子可以看到更多細節。
建立新的Realm
現在,需要一個新的帳號系統。你可以在建立一個Keycloak伺服器,或是建立一個新的Realm。不同Realm的帳號系統是獨立不相干擾的,所以這裏就先建議一個新的Realm – G00gle
。
【用Keycloak學習身份驗證與授權30】Open ID Connect & Social Login(1)
因為略過了一些JWT格式細節分析。所以這部分也有部分不會好好提到
到目前爲止,爲何不同應用可以使用同一個帳號登入,已經在說明Client解釋過。這是在相關系統的應用下,那麼…沒直接相關的系統呢?譬如:使用Google登入。像這種使用不同帳號系統登入的方式,在Keycloak分成兩種。第三方系統登入,這篇僅會說明與 OAuth / Open-Id 相關的一種。如果你使用過Firebase、Auth0等服務,或是看過使用Google、Facebook、Microsoft、GitHub帳號登入的應用,對就是這類。這種社群帳號登入(Social Login)的方式,與前幾天提到的內容相關,而且可以在Keycloak實現。
鐵人賽只會實現而已,一些細節和更多的範例並不會提到。 (雖然原本就計劃寫)
Social Login 社群帳號登入
以GitHub帳號登入
【用Keycloak學習身份驗證與授權29】JWT權杖格式介紹(1)
總覺得…直接開始說明什麼是JWT格式來著。但感覺這樣會很無聊,不如我們從已經拿到的Token來看吧!
至今爲止,除了存取權杖(access_token
)、更新權杖(refresh_token
)外,還拿到過識別權杖(id_token
)。仔細看三者,都有兩個「.
」可以將權杖分成三個部份。
這些權杖都可以透過JWT.io去解析。總之先透過Password Grant Flow取得access_token
和refresh_token
,或是透過「快速開始」應用取得id_token
。
【用Keycloak學習身份驗證與授權28】Role
在帳號系統下,除了帳號本身與帳號群組外,通常還存在一個非常重要的部分–角色(Role),更有基於角色的存取授權方式(RBAC)。
寫到有點累了,沒意外的話之後是會提到RBAC
帳號如果代表一個人,這個人可能有多個角色身份。可能是個老師、主任、校長;可能是爸媽、叔姨;可能是員工、部長、處長、老闆,且可能有一群人擁有同一種角色。角色和帳號群組有點像,但在Keycloak是兩個概念。除此之外,在Keycloak還分成兩類型角色– Realm Roles 和 Client Roles 。
建立 Realm Roles
首先,你可以建立Realm共用的角色,像是員工、老闆等等較爲通用的角色。
點選在 Realm 選單下的 Roles ,然後再點選 Add Role :
【用Keycloak學習身份驗證與授權27】User & Claim & Profile
接著來看看爲什麼更新帳號資訊,在「快速開始」會有那些變化。
這與client scope和claim有關。關於後者之後會在詳細說說,而目前就先了解一下這個現象發生的原因。
首先,在我們取得token
的時候曾申明需要的scope
爲openid profile email
。其中profile
這個scope爲這次變化的主要原因。
來到Keycloak管理選單下的 Client Scopes ,然後找到 profile 。
接著將頁籤切換到 Mappers , 你會看見一堆與 User Attribute 有關的設定。
【用Keycloak學習身份驗證與授權26】User & Group
帳號(User)
基本訊息
接著來看看與帳號有關的設定。
在之前,已經建立過一帳號–bob
。過去學習實驗,也都以bob驗證身份。接著我們要來更新一下這個帳號。
首先看一下基本訊息:
來添加一些資訊:
- Email:
bob@fake.email
- First Name:
Bob
- Last Name:
Lee
- Email Verified:
ON
此外,可以要求使用者在必須做一些事情,譬如:驗證信箱、更新密碼、更新個人資訊等。
再次登入到應用–「快速開始」,可以看到有一些訊息也有些不同了。
【用Keycloak學習身份驗證與授權24】Clients
Client與一些安全相關的設定
在OAuth架構下的Client(客戶端)可以想象成是一個一個的應用程式。到目前爲止也已經建立過幾個Client:
這些Client有著自己的規則、資源、授權方式等。
可以複寫一些Realm的設定,包含產生存取權杖的方式。像是認爲RS256
簽名不夠,需要使用到RS512
:
【用Keycloak學習身份驗證與授權23】Realm
Realm,中文或許會翻作「域」,但基本很像是程式開發上,語言層面提供的包(package
)或是命名空間(namespace
)。或者可能可以更貼切的說是工作空間(workspace)。
你可以想象就像是一個企業、部門或是其他組織。有著相同的一些規範,同事們在同樣地工作空間生活、工作。但不同的企業、部門或是其他組織,可能會有類似的規範,但兩者不互相影響。
會特別有這個概念,是因爲Keycloak是可以建立多個Realm的。也就是,在同一間公司內,不同部門都可以有自己的Realm,制定部門自己的管理規範。或是特別爲外部客戶建立一個Realm,並制定特殊規範。
不同的Realm內,有著自己的帳號系統、密碼規範政策等。利用這個特性,之後也會用來更清楚的理解Open-Id。
你也可以同樣簡單視爲一個帳號資料庫、身份驗證伺服器。特別的是在會話成立期間,可以不需要再進行一次驗證,而這部分,會在提到Client時在多做說明。
如何建立一個Realm
要建立一個Realm是非常簡單。在之前也建立過「quick-start」這個Realm。也幾乎就只需要給個名字而已。
【用Keycloak學習身份驗證與授權22】Keycloak使用基本概念:前導
【用Keycloak學習身份驗證與授權21】在Flow這段小旅途外的風景
在這一小段路中介紹了Password Flow、Implicit Flow、Code Flow、Refresh Token Flow、Client Credentials Flow、PKCE、Device Code Flow。有些模式已經被發現可能有潛在風險,有些模式無法單獨使用。這或許還不是全部,至少到現在為止都還沒有提到過金融級應用Flow–CIBA。
Client Initiated Backchannel Authentication Profile(CIBA)
本小節也不會詳細介紹CIBA(Client Initiated Backchannel Authentication)。儘管CIBA現在階段還只是草案(Draft),但在Keycloak v15版本中已經可以使用。大概也已經確實有一些應用使用。
為什麼你不該繼續使用Implicit Flow?
在談到Implicit Flow時候,提到過:
將存取權杖暴露在使用者面前也不是非常好的做法
【用Keycloak學習身份驗證與授權20】Device Code(2)
光要完成這個範例就花了幾乎整整一天
做完後決定…來拆篇這第二部份,將有部份內容會在【實戰篇】展開。 今天就先來看看成果。
成果發表
【用Keycloak學習身份驗證與授權19】Device Code(1)
+----------+ +----------------+
| |>---(A)-- Client Identifier --->| |
| | | |
| |<---(B)-- Device Code, ---<| |
| | User Code, | |
| Device | & Verification URI | |
| Client | | |
| | [polling] | |
| |>---(E)-- Device Code --->| |
| | & Client Identifier | |
| | | Authorization |
| |<---(F)-- Access Token ---<| Server |
+----------+ (& Optional Refresh Token) | |
v | |
: | |
(C) User Code & Verification URI | |
: | |
v | |
+----------+ | |
| End User | | |
| at |<---(D)-- End user reviews --->| |
| Browser | authorization request | |
+----------+ +----------------+
Figure 1: Device Authorization Flow
The device authorization flow illustrated in Figure 1 includes the
following steps:
(A) The client requests access from the authorization server and
includes its client identifier in the request.
(B) The authorization server issues a device code and an end-user
code and provides the end-user verification URI.
(C) The client instructs the end user to use a user agent on another
device and visit the provided end-user verification URI. The
client provides the user with the end-user code to enter in
order to review the authorization request.
Device Code Flow這個與前面幾個特別不一樣。在之前,以往都是從登入開始,然後跳轉頁面回到App(Client)。也就是通常先有的是前端通訊,然後才是後端通信。
【用Keycloak學習身份驗證與授權18】PKCE
+-------------------+
| Authz Server |
+--------+ | +---------------+ |
| |--(A)- Authorization Request ---->| | |
| | + t(code_verifier), t_m | | Authorization | |
| | | | Endpoint | |
| |<-(B)---- Authorization Code -----| | |
| | | +---------------+ |
| Client | | |
| | | +---------------+ |
| |--(C)-- Access Token Request ---->| | |
| | + code_verifier | | Token | |
| | | | Endpoint | |
| |<-(D)------ Access Token ---------| | |
+--------+ | +---------------+ |
+-------------------+
Figure 2: Abstract Protocol Flow
PKCE模式
說穿了PKCE是基於Code flow的安全強化版。在整個過程前後添加了兩個動作–產生code_verifier
和code_challenge
,並在最後透過code_challenge
驗證code_verifier
。其目的有很大程度是為了建立前端通訊與後端通訊的關聯。
原先風險
那麼先來看看原本發生了什麼問題。
【用Keycloak學習身份驗證與授權17】Client Credentials
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
Figure 6: Client Credentials Flow
嘗試 Client Credentials flow
Client Credentials,這個模式有點特別。除了前面看到的它可能與其他模式並用以外,最特別的是,單純使用它,完全不需要資源擁有者參予。總之先來看看:
你可以使用RESTfer嘗試看看:
grant_type: client_credentials
client_id: oauth_tools
client_secret: <之前所產生的secret>
或是同樣可以透過OAuth.Tools嘗試看看。
【用Keycloak學習身份驗證與授權16】Refresh Token
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
Figure 2: Refreshing an Expired Access Token
The flow illustrated in Figure 2 includes the following steps:
(A) The client requests an access token by authenticating with the
authorization server and presenting an authorization grant.
(B) The authorization server authenticates the client and validates
the authorization grant, and if valid, issues an access token
and a refresh token.
(C) The client makes a protected resource request to the resource
server by presenting the access token.
(D) The resource server validates the access token, and if valid,
serves the request.
(E) Steps (C) and (D) repeat until the access token expires. If the
client knows the access token expired, it skips to step (G);
otherwise, it makes another protected resource request.
(F) Since the access token is invalid, the resource server returns
an invalid token error.
使用refresh_token
取得access_token
接著是使用Refresh Token換取Access Token的流程。這大概是所有中最簡單的一個模式之一了。
但因爲先決條件是取得可用的 Refresh Token ,所以無法單獨存在。在RCF6749相關的流程圖中,關注的是G、H的部分。
至於一開始有什麼方式取得Refresh Token就非常的多。在已經介紹的密碼模式和code模式都有可能返回refresh_token
。
【用Keycloak學習身份驗證與授權15】Authorization Code
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Note: The lines illustrating steps (A), (B), and (C) are broken into
two parts as they pass through the user-agent.
Figure 3: Authorization Code Flow
Authorization Code是在 RFC6749第一個提到的流程,所以有時又被視爲 標準流程(Standard Flow) 。
它與前兩個流程很不一樣,分成 前端通訊(frontchannel) 和 後端通訊(Backchannel) 。不過,其實反倒是前兩個是所有模式裡的怪胎,在隱含模式下,後端通信並在前端通訊;在密碼模式下,根本不存在前端通信,資源擁有者需要高度信任客戶端(說穿了在前端通信下,資源擁有者也是高度信賴瀏覽器或代理(User-Agent))。
【用Keycloak學習身份驗證與授權14】Implicit (Legacy)
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
Note: The lines illustrating steps (A) and (B) are broken into two
parts as they pass through the user-agent.
Figure 4: Implicit Grant Flow
如果說password適用於原生應用環境(Native Application)下的話,接著就是適用於純前端環境。 在現在前後分離架構的情況,前端與後端連接並不緊密,甚至前端幾乎就可以視爲一個完整的應用。 因此將前端視爲授權框架下的「客戶端(Client)」也就不會太難理解。
【用Keycloak學習身份驗證與授權13】Password Grant (Legacy)
首先,先來看看直接使用帳號密碼授權的。
是的, OAuth 是有一個模式支援直接使用帳號密碼的。 與萬能鑰匙不太一樣的是,授權的結果仍然是由授權伺服器的權杖和資源伺服器決定。 儘管透過中央授權控制可以限制存取權杖可以做些什麼,但畢竟直接使用帳號密碼並不是特別好, 故在其他模式下都不適用時,才應該再考慮此模式。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Figure 5: Resource Owner Password Credentials Flow
事前準備
安裝RESTer
【用Keycloak學習身份驗證與授權12】Flows這一小段路上路前注意事項
其實我原本是想要 RESTer 幹到底的哈😜。
今天有一點是插話的。考慮到接下來幾天的內容,所使用到的工具會有點多樣,所以行前做個提醒。
首先,你最好了解:
- HTTP Request / Response
- HTTP API (Web API)
- JSON
- BASE64
諾對於Postman這類工具有所熟悉再好不過。但接者幾天會使用:
- RESTer
- curl
有一些情況會直接使用。 - python
主要用於格式化JSON。
除此之外,如果熟悉Bash
的話同樣也有助於理解所有內容。此外還有可能會使用到 OAuth Tools 、 jwt.io 。(JWT的部分更有可能出現在之後關於Open-Id內容前後)
但其實,以上並非全部都是必須。最重要的是希望你能夠學習到OAuth本身的部分。
【用Keycloak學習身份驗證與授權11】OAuth 2
終於要來談談OAuth裡定義的細節了~
目前OAuth 2.0 一共定義了7種流程(flow)。在未來本系列可能稱之爲模式,不同模式適用於不同情況、不同環境。 就是因爲如此,OAuth才有高彈性的優勢。
OAuth 2.0 的可擴展性和模塊化是其最大的優勢之一,因為這使得該協議適用於各種環境。然而,正是這種靈活性導致不同的實現之間存在基本的相容性問題。當開發人員想在不同的系統上實現 OAuth 時,它提供的眾多自定義選項容易使人困惑。
本系列會介紹的模式包含:
- Password Grant (密碼模式)
- Implicit (隱含模式)
- Authorization Code (Code模式)
- Refresh Token
- Client Credentials (特殊密碼模式)
- PKCE
- Device Code
儘管 Implicit 和 Password Grant 被標記爲傳奇的(Legacy),但有時候仍然可能會使用到。重要的是你應該知道什麼情況應該使用什麼模式。同時記住,即使一個系統按照規範正確地實現了 OAuth,也不意味著該系統在實踐中就是安全的。
「OAuth 2.0 實戰」有一章決策圖可以幫助你決定使用什麼模式。但本系列應該不會提供。
【用Keycloak學習身份驗證與授權10】深入OAuth 2
喔不,其實今天還不會真正提到OAuth 2.0的深度內容。今天要來談談的是取得資源的細節。
使用帳號密碼,假裝自己是用戶
首先先試著想想看,如果你想要寫一支程式代替你處理某些事情。譬如:收信、發信。 更詳細的說,你寫了一個信件的客戶端(如:Thunderbird、Outlook)。 然後你會需要告訴這支程式你信箱的登入帳號密碼,由他去代替你收信、寄信。這個樣子就像是你把你所有的祕密都交給了它, 交給了它那把萬能鑰匙,而你完全信任這支程式。
其實這種狀況還真不少見。尤其在於你所申請的帳號,和使用的客戶端服務實際就是同一個時,這種行爲在正常不過。
但當它們是不同服務時,就可能出現問題了。你還能信任你提交的密碼不會被誤用嗎?不可能發生?
你可能有Gmail的帳號,你會很正常的使用Gmail的服務。但你知道Gmail除了自己本身外,它還可以幫你收其他信箱嗎?
比如說你還有ymail的帳號,但你更喜歡Gmail的界面,所以你希望使用Gmail來處理yamil的信件。這時候其實你就是告訴了Gmail 關於yamil的帳號密碼。相對的,也就是你應該是信任的Google的服務。
【用Keycloak學習身份驗證與授權09】再談身份驗證與授權
再談身份驗證與授權
現在,讓我們再一次把視線放到「身份驗證」和「存取控制」這些名詞身上。 在入門篇的「淺談身份驗證與授權」已經相當程度的解釋過各個名詞。 不過今天將要更關注在身份驗證與存取控制的細節上。
對於一個應用來說,最重要的是它的 業務邏輯 。 除了業務邏輯本身,為完成所需的工作,會需要取得必要之資源。這可能是一份檔案, 鏡頭、麥克風資源等不同種形式。
在 取得資源 過程中,也會有另外一層業務邏輯,也可能本身就是另一隻程式服務,對所需取得的資源,進行 存取控制 。
最後,爲了判斷是否具有存取該項資源的權限,有可能有必要進行 身份驗證或授權 。
【用Keycloak學習身份驗證與授權08】OAuth 2
這是入門篇的最後一天了,今天不會寫什麼內容,但來帶大家看個入門概念可用的工具 – OAuth 2.0 Playground。
OAuth 2.0定義了幾個flow,可用於不同情境下,由於後續會有更多詳細說明,所以今天只會帶大家初步認識,嚐鮮看看。
註冊帳號
點選 register a client and a user。別擔心,這是個隨時可以廢棄的帳號。你完全不用真的去記他,他也不會要求你提供什麼資訊。
然後你會得到一組帳號密碼。然後點選「open in new window」之後就可以按下「continue」。
【用Keycloak學習身份驗證與授權07】什麼是OAuth
先來回憶一下,何爲「授權」。試想像有一座宅邸,裏頭有無數房間。而你作爲這座宅邸的管家,擁有一把萬能鑰匙,可以開始宅邸內所有門扉。 此外,這把萬能鑰匙還有一個作用,就是產生出開啓特定門扉的鑰匙。 你可以產生出的鑰匙交給其他人,其他人就可以自由進出特定房間。這個動作就是「授權」。
OAuth 是一個開放標準的 授權協議 ,它允許 軟體應用 代表 資源擁有者 訪問資源擁有者的 資源 1。
OAuth是什麼?
【用Keycloak學習身份驗證與授權05】什麼是Keycloak
終於要來好好介紹一下甚麼是Keycloak了~
收先先來看一下Keycloak的基本資訊:
- 名稱: Keycloak
- 開發使用的程式語言: Java
- 公用: 單點登入驗證與授權工具
- 許可協議: Apache License 2.0
- 公開倉庫: https://github.com/keycloak/keycloak
- 官方網站: https://www.keycloak.org
- 撰寫當下最新版本: 15.0.2 (2021年8月20日)
在 快速開始 提到過起始畫面有一些細節:
【用Keycloak學習身份驗證與授權04】淺談身份驗證與授權(2)
實際上,在昨天已經將多數基礎都已經解釋過了,不過我想到還有一些東西可以再多做補充的。
對啦! 擔心彈藥不足,把一篇拆成兩篇來啦!👻
沒有身份識別的存取控制
在我們拆分的整個流程中分成:身份識別、身份驗證、授權、存取控制。但現在,你將Web App登出後再登入一次,你會發現「授權」的部分不見了! 但我們不會立刻來討論這個部分。先來說說身份識別。
不覺得,身份識別在整個流程之中非常雞肋嗎?也就只是將你這個「自然人」與系統中存在的「帳號」對應起來。 也確實如此,在這樣的拆分中,身份識別對於存取控制並不是必要的。在後來已MAC爲基礎發展的存取控制框架,也多不直接與帳號相關。
別擔心,之後會提到什麼是MAC(強制存取控制, Mandatory Access Control)。
不過還有一個更直接沒了這個流程的例子。在以「單人使用」作爲設計的系統之中,我們只需要拿到鑰匙就可以進行存取。
什麼?你說現在還有這種系統嗎?其實還真不少呢,加密上鎖過的壓縮檔案,上鎖的部落格文章。還有授權之後的流程,可能也不包含身份識別。
【用Keycloak學習身份驗證與授權02】Quick Start(2)
昨天,已經完成了一部分配置,且也已經可以建立帳號並登入了。
不過,這只能算是半套,而今天要在來完成另外半套。
你可以按照昨天的做法,重新建立一個新的Client。
只是注意在建立的時候,「Root URL」改爲: http://localhost:4200
。
今天,我們要自己實現一個前端網頁去的Web App,然後綁定Keycloak去做登入。
前置要求:
- 用Keycloak建立一個Client
- 網頁開發基礎知識(HTML/CSS/JavaScript)
- TypeScript的部分知識
- Angular知識(非必要)
調整Keycloak的Client配置
前面說過,Keycloak的Client實際上並不是真正的Client Application,只是做了一些關聯。 今天就要來 快速開始 個自己的Web App。而首先,需要先調整Client的關聯:
- 選擇「Clients」
- 找到昨天建立的「my-quick-start-app」,然後點選「Edit」
這此調整主要做兩個修改:
【用Keycloak學習身份驗證與授權01】Quick Start(1)
開始之前~2🎃。開完笑的~
但是想了許久,總覺的就這麼直接開始解釋各個名詞不太好。
想找個範例又有諸多擔心。
不如…先來快速開始做個範例!
快速開始將分成兩天。 今天會先跑過一次簡單的流程,明天才會寫一點程式。
這兩天看完後,依照需求,你甚至可以開始開發自己的應用。
那我們從Keycloak開始吧!
今天的前置需求:
- 只要裝好docker就好囉~
- 阿!對,你還要安裝個瀏覽器。
(不過你拿什麼在看本系列文章呢?)
透過Docker建立一個Keycloak應用
docker run -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:15.0.2
這麼一條指令就可以開始這系列多數內容了(吧)。現在Keycloak會聆聽本機的8080 port。嘗試用瀏覽器開啓 http://localhost:8080 後,你應該會看到以下畫面:
【用Keycloak學習身份驗證與授權00】開始之前
這系列文章將帶大家探討軟體開發上,那些身份驗證與授權的相關議題。此外的話題還有身份識別、存取控制。 以目前諸多流行應用都以非單人使用的狀況之下,身份驗證與授權,幾乎是每位開發者都會遇到的題目。 不管你是串接OAuth、管理資源、寫後臺界面,甚至在最初應用的設計,幾乎都會扯上邊。 在業務邏輯之外,這或許會是相當重要的一部分。
關於身份驗證與授權,每一個部分都非常重要,也都可以分開來看,卻也非常難分開來看。
Because these three techniques are so closely related in most real applications, it is difficult to talk about them separate from one another. In particular, authentication and authorization are, in most actual implementations, inextricable. 1
就如同在Oracle上可查詢到的相關資料,這些部分儘管代表者不同概念,但彼此非常相關,實在很難分開來看。雖然如此,每一個部分都是非常龐大的內容,而本系列將會着重於授權控制與存取控制。在此之上會在探討近來已經非常普遍的OAuth 2.0、Open-Id、單點登入(SSO)和基於角色的存取控制(RBAC)
Tag: oop
關於C#內建類型的怪異設計(LeetCode:reverse string)
前言
先說一下我的背景,可能會比較了解為什麼我會這樣想。我自己學過C++和Java,這兩個都不算是完全物件導向。相對來說,另外一個蠻愛使用的Python就連基本型態也是物件。至於C#,因為一些個人喜好的因素,並沒有去好好學習。不過也有好一段時間幫朋友除錯,除ASP.NET的錯誤。
之所以能夠幫忙,或快速上手,應該也與C++、Java、C#三者的歷史有關。在C++之後,推出了一個更物件導向的程式語言:Java,最基礎的單獨單位是class
,並且在JVM上運行,號稱可以跨平台、跨設備;隨後,Microsoft為了與Java競爭,並推廣自家的.Net Framework,學習了Java的設計,推出了C#。(早期更是有J#)
基礎型別&基礎類別
所以,三個是很相像的,更重要的是在物件導向的概念上。所以會一個,基本上另外兩個也就應該容易上手。不過C++從C遺留下來的基礎型態int
、char
、float
以及struct
等等,註定讓它不那麼物件。Java可能也有類似歷史因素,而為了處理這樣問題,Java將所有基礎型態做了包裝,一一對應1。
※ Java基本型態對應的類別
Java中有兩個型態系統,基本型態與類別型態,使用基本型態目的在於效率,然而更多時候,會使用類別建立實例,因為物件本身可以攜帶更多資訊,如果要讓基本型態像物件一樣操作,可以使用Long、Integer、Double、Float、Boolean、Byte等類別來包裹(Wrap)基本型態。 –Source: https://openhome.cc/Gossip/Java/Wrapper.html
為什麼會突然覺得C#設計的有點怪?
這主要是最近寫到一題LeetCode,要做字元陣列的反轉。如果使用C#的話,需要使用Array的類別方法。
public class Solution {
public void ReverseString(char[] s) {
Array.Reverse(s)
}
}
Tag: open-id
【用Keycloak學習身份驗證與授權02】Quick Start(2)
昨天,已經完成了一部分配置,且也已經可以建立帳號並登入了。
不過,這只能算是半套,而今天要在來完成另外半套。
你可以按照昨天的做法,重新建立一個新的Client。
只是注意在建立的時候,「Root URL」改爲: http://localhost:4200
。
今天,我們要自己實現一個前端網頁去的Web App,然後綁定Keycloak去做登入。
前置要求:
- 用Keycloak建立一個Client
- 網頁開發基礎知識(HTML/CSS/JavaScript)
- TypeScript的部分知識
- Angular知識(非必要)
調整Keycloak的Client配置
前面說過,Keycloak的Client實際上並不是真正的Client Application,只是做了一些關聯。 今天就要來 快速開始 個自己的Web App。而首先,需要先調整Client的關聯:
- 選擇「Clients」
- 找到昨天建立的「my-quick-start-app」,然後點選「Edit」
這此調整主要做兩個修改:
Tag: pbm
圖片格式-[PBM]
前言
稍早前寫過一篇 圖片格式-[XPM] ,朋友們可以先去找來看看。這篇內容會比較短一點,部分東西會帶過去。
PBM格式簡單說明
PBM全名為: portable bitmap format,光是從字面意義上就可以知道,這是一個可攜的(portable)圖片格式。 與XPM相同,PBM支援存文本模式。不過比起只分版本,不如說PBM是一系列家族–PPM/PGM/PBM/PNM。不囉說,直接附上維基的表格:
檔案描述子 | 類型 | 編碼 | 副檔名 |
---|---|---|---|
P1 | 點陣圖 | ASCII | .pbm |
P2 | 灰度圖 | ASCII | .pgm |
P3 | 像素圖 | ASCII | .ppm |
P4 | 點陣圖 | 二進位 | .pbm |
P5 | 灰度圖 | 二進位 | .pgm |
P6 | 像素圖 | 二進位 | .ppm |
Linux可能會對pbm檔案這麼描述:
Netpbm image data, size = 33 x 32, bitmap, ASCII text
迷之音:“嘿,為什麼XPM就不給size了?”
二進位格式
Netpbm image data, size = 40 x 40, rawbits, bitmap
Tag: php
30天成爲Laravel萌新(目錄)
這是第二次參加「iT 邦幫忙鐵人賽」。上次報了兩個主題,只有一個完賽。這次報了三個主題……還好全都完賽了。雖然開賽前,對於一個主題先寫了近十天左右的文章,但在賽中,趕稿壓力還是頗大的。
30天成爲Laravel萌新(目錄)
「30天成爲Laravel萌新」是我最重要的一個系列,報名在Modern Web主題下,並同步發表於又LAG隨性筆記。下面列出每天文章連結:
- 30天成爲Laravel萌新(第0天) - 前言
- 30天成爲Laravel萌新(第1天) - 認識Laravel
- 30天成爲Laravel萌新(第2天) - 安装 Laravel
- 30天成爲Laravel萌新(第3天) - 使用laradock建立開發環境(上)
- 30天成爲Laravel萌新(第4天) - 使用laradock建立開發環境(下)
- 30天成爲Laravel萌新(第5天) - Laradock的工作空間容器
- 30天成爲Laravel萌新(第6天) - 配置專案
- 30天成爲Laravel萌新(第7天) - 認識artisan
- 30天成爲Laravel萌新(第8天) - 路由&頁面模板(1)
- 30天成爲Laravel萌新(第9天) - 路由&頁面模板(2)
- 30天成爲Laravel萌新(第10天) - 路由&頁面模板(3)
30天成爲Laravel萌新(第30-1天) - 總結
這是第二次參加鐵人賽。這個主題是我決定參賽出就已經定好,總算寫完了。從介紹、安裝、配置,使用laradock
、artisan
,路由、模板,Parsdown
(Markdown),再到控制器、資料庫,以及另我以些驚豔的Pagiantion
,然後多語系支援、紀錄檔,客製化錯誤頁面,檔案上傳與表單驗證,到最後登入驗證。
在這過程中,一學習到不少,很充實。(還有下次不要在一次報三個主題了…)
不過,Laravel官方文檔其實相當豐富完整,這30天的文章,頂多只能當作而外的參考而已,但願對想學習Laravel的人還是有幫助。
最後,你可以在這裡看到跟Laravel有關的文章。或是看看另外兩個系列文章( 又LAG的EOS.IO技術筆記 和 有點玩鬧性質的 又LAG的ML學習筆記 )。
30天成爲Laravel萌新(第30天) - 登入驗證
這部份在Django時,明明是最先學的,在Laravel卻放到了最後☺
要使用Laravel提供的會員系統,相當容易,只須要:
artisan make:auth
artisan migrate
然後瀏覽http://localhost/register 註冊帳號,或是http://localhost/login 登入帳號。仔細一看,會多了這些檔案:
new file: app/Http/Controllers/HomeController.php
new file: resources/views/auth/login.blade.php
new file: resources/views/auth/passwords/email.blade.php
new file: resources/views/auth/passwords/reset.blade.php
new file: resources/views/auth/register.blade.php
new file: resources/views/auth/verify.blade.php
new file: resources/views/home.blade.php
new file: resources/views/layouts/app.blade.php
modified: routes/web.php
其中routes/web.php
多了:
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
現在瀏覽http://localhost/home 也有畫面了。
30天成爲Laravel萌新(第29天) - 表單驗證
昨天的程式碼有一些註解的內容,先取消註解試試。
resources/views/images/upload.blade.php部份內容
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
routes/web.php處理post('/images/upload')
請求的部份內容:
Validator::make($request->all(), [
'file' => 'required|image',
])->validate();
驗證表單資料
Validator::make($request->all(), [
'file' => 'required|image',
])->validate();
差不多等價於:
$validate = $request->validate([
'file' => 'required|image',
]);
$request
有validate()
的方法。還可以直接建立一個驗證器,繼承Request
:
php artisan make:request ImageUpload
上面執行完後,現在,在app/Http/Requests/
目錄下多了ImageUpload.php
,內容改成下面這樣:
30天成爲Laravel萌新(第28天) - 上傳檔案
Laravel要上傳檔案非常的簡單,今天就來簡單帶個範例吧!
建立檔案目錄連結
首先,現用Artisan建立目錄連結。
artisan storage:link
上面命令會建立storage/app/public
目錄,並將目錄同樣綁定到public/storage
。這讓於此目錄下的內容,可以透過http://localhost/storage/<FILE NAME>
存取。在public
目錄下的檔案,基本都可以直接透過瀏覽器存取。
上傳頁面
同樣以一個簡單的上傳頁面作為範例。先建立resources/views/images/upload.blade.php
:
@extends("base",['title'=>'上傳圖片'])
@section('title', '上傳圖片')
@section('body')
<form action="{{route('image.upload')}}" method="post" enctype="multipart/form-data">
@csrf
<!--
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
-->
<input name="file" type="file" accept="image/*" value=""/>
<input name="" type="submit" value="上傳"/>
</form>
@endsection
30天成爲Laravel萌新(第27天) - 本地化、多語言支援(Localization)
@extends("base",['title'=>'哎呀,找不著頁面'])
@section('title', '哎呀,找不著頁面')
@section('body')
<h1>{{__('404 error message')}}</h1>
<b>哎呀,找不著頁面</b>
<p>
<ul>
<li><a href="/">點擊我,回到首頁</a></li>
<li><a href="{{route('blog/post.index')}}">我的網誌</a></li>
</ul>
</p>
@endsection
開始
在昨天簡單建立的錯誤頁面中,又再一次偷渡了一個東西。對,就是那個有點怪怪的{{__('404 error message')}}
,實際上這等價於@lang('404 error message')
。透過lang()
函式的方式,也可以在控制器做變換。lang()
會根據給定的字串,以及設定(app/config.php
)中的locale
和faker_locale
轉換為對應的語言。預設會轉換為locale
所設定的,而備用語言可由faker_locale
設定。當兩者都不存在時,會直接輸出字串。還記得我們把app/config.php
部份內容改成這樣嗎?
'locale' => 'zh-TW',
'fallback_locale' => 'en',
不過,不管是locale
還是faker_locale
,都還找不著{{__('404 error message')}}
要轉換成的文字,所以現在會直接生成404 error message
。
30天成爲Laravel萌新(第26天) - 客製化404錯誤頁面
HTTP協議上存在許多狀態碼,其中400系列、500系列錯誤可能是最常見到。200、300很難被注意到。恐怕又以404錯誤、403錯誤、500錯誤、503錯誤最常見。一個好的錯誤提示頁面,可以帶來很好的使用體驗。你可以看看別人怎麼設計(以及2018年不可錯過的創意404報錯設計),甚至更有404 PAGE TEMPLATE (https://www.404pagefree.com/) 可以直接下載404錯誤頁面模板(這網址真有趣),裡頭模板多以CC-BY 3.0姓名標示授權,可用於商業用途。
那麼在Laravel要怎麼客製化這些錯誤提示頁面?以404錯誤頁面為例,可以建立resources/views/errors/404.blade.php
檔案,寫入以下內容:
@extends("base",['title'=>'哎呀,找不著頁面'])
@section('title', '哎呀,找不著頁面')
@section('body')
<h1>{{__('404 error message')}}</h1>
<b>哎呀,找不著頁面</b>
<p>
<ul>
<li><a href="/">點擊我,回到首頁</a></li>
<li><a href="{{route('blog/post.index')}}">我的網誌</a></li>
</ul>
</p>
@endsection
同樣的…CSS不是本系列重點……
30天成爲Laravel萌新(第25天) - 紀錄檔
使用Logging
現在,要再回到laradock來。不過同樣的,Lavavel儲存紀錄檔的地方也是在storage/logs
。這裡會儲存使用Log::info()
所寫下的紀錄。
docker-compose exec workspace bash -c "tail -f storage/logs/最新的log"
如果使用Linux的話,也可以直接:
tail -f storage/logs/最新的log
當然你也可以直接把紀錄檔,用文字編輯器開啟。不過上述會這麼做,是因為如次可以更簡單的在開發時除錯。有時候,Laravel錯誤訊息頁面提供的訊息並非一目瞭然,會需要用到printf
方式除錯(我假設…你不動gdb
,但用過最基本的除錯手法…)。在這裡,使用一系列Log::
的方法,其中,Log::debug()
就對應除錯資訊。這和我曾經寫過的瀏覽器console.log()外的一些其他用法 有些像。
30天成爲Laravel萌新(第24天) - 頁面清單 下篇(使用Pagnation和刪除按鈕)
就先來看看使用Laravel提供Pagination能夠多麼簡單。
首先,控制器只需要使用BlogPost::paginate($limit)
查詢就好,並將查詢結果直接傳遞給視圖。
public function index()
{
// 如果想要將每頁顯示數量變成可以調整的,
// index需要傳入 (Request $request),並由下方方式取得$limit傳入paginate
// $limit = $request->query('limit', 30);
$posts = BlogPost::paginate(30);
return view("blog/index",[
"posts"=>$posts,
]);
}
BlogPost::paginate($limit)
會自動根據Request的page
參數取得存取的頁面,自動計算offset
,並根據給予的每頁顯示數量限制來查詢。甚至連index()
都不需要傳入Request $request
。傳給視圖的也少了不少。說到視圖,立馬來看看變得多簡潔:
@section('body')
<div class="container">
<ul>
@foreach ($posts as $post)
<li>
<a href="{{route('blog/post.show',['id'=>$post->id])}}">{{ $post->title }}</a>
</li>
@endforeach
</ul>
</div>
{{ $posts->links() }}
@endsection
列出文章清單的部份,大致什麼太大變化。但是頁面頁數選項不再那麼複雜,沒錯,只需要$post->links()
就好。他會自動生成符合Bootstrap的Pagination。在瀏覽器接受到的HTML樣子可能如下:
呵呵,根本不需要寫這麼麻煩…也太簡單了吧!
30天成爲Laravel萌新(第23天) - 頁面清單 上篇
因為重點不在於CSS,不在於Bootstrap。 其實大可以將頁面弄的很美觀。在Laravel專案資料夾中,其實有一個給
npm
使用的package.json
檔案。 不過還是來把重點放在Laravel上。
使用Pagnation前
Laravel提供一個超級簡單的方式,來處理分頁問題。不過在使用前,來看看原本要怎麼實現index()
。不但要取得頁數,並計算檢索數量,然後從資料庫透過offset()
、linit()
的方式取得資料。
public function index(Request $request)
{
$offset = $request->query("offset", 0);
$limit = $request->query("limit", 30);
$page = $request->query("page");
if($page){
$offset = ($page - 1) * $limit;
}else{
$page = ($offset / $limit) + 1;
}
$request->merge([
"offset" => $offset,
"page" => $page,
]);
$posts = BlogPost::orderBy('activity_id','ASC')
->offset($offset)
->limit($limit)
->get();
return view("blog/index",[
"posts"=>$posts,
"page"=>$page,
"total_pages" => $total_pages,
]);
}
30天成爲Laravel萌新(第22天) - 資源控制器(Resource Controller) 下篇
最終,我決定將index()
和destroy()
另外寫。一個是寫完create()
和edit()
,destroy()
也就不怎麼難。但是index()
意外也能有豐富內容能寫…(加上有另一種用法我還不太會)
總體來說,這篇程式還是有些趕工粗糙….
重點觀念
@method
的使用 相當於隱藏欄位_method
。@csrf
的使用
建立頁面模板
同樣的,先建立一個編輯頁面的模板resource/views/blog/edit.blade.php
:
@extends("base",['title'=>'編輯文章'])
@section('title', '編輯文章')
@section('body')
<form method="post" action="{{($type=="edit") ?
route("blog/post.update", ["id"=>$id]) :
route("blog/post.store")}}">
@csrf
@method(($type=="edit")? "patch" : "post")
<label for="title">標題:</label>
<input name="title" type="text" value="{{$title}}" id="title" />
<br/>
<label for="content">內容:</label>
<textarea cols="30" id="content" name="content" rows="10">{{$content}}</textarea>
<br/>
<input name="" type="submit" value="儲存"/>
</form>
@endsection
30天成爲Laravel萌新(第21天) - 資源控制器(Resource Controller) 中篇
現在,要正式把控制器與資料庫連結起來。順便偷埋之後兩個主題☻。
引入所需使用的套件
首先,得先把之前建立好的Model引入。另外,我們在偷偷引入一個Illuminate\Support\Facades\Log
。
use Illuminate\Support\Facades\Log;
use App\BlogPost;
use Parsedown;
完成CRUD操作
Create / Store
public function store(Request $request)
{
$title = $request->input("titile", "未命名文章");
$content = $request->input("content");
$post = new BlogPost;
$post->title = $title;
$post->content = $content;
$post->save();
Log::info("Store New Blog Post: id = $post->id");
return redirect()->action(
'Blog\PostController@show', ['id' => $post->id]
);
}
可以透過Request
的input
方法,取得POST來的資料,還可以補上預設參數。這邊取得標題與內容後,建立一個新的PostBlog
實體存入資料庫。
Read / Show
public function show($id){
$post = BlogPost::find($id);
if(! $post){
abort(404);
}
$content = $post->content;
{
$Parsedown = new Parsedown();
$content = $Parsedown->text($content);
}
return view("blog.post", [
"title" => $post->titile,
"content" => $content,
]);
}
讀取與之前控制器的內容差不多。不同的是,需要先從資料庫尋找資料,如果找不到就回傳404找不到錯誤頁面。之後會對該頁面進行修改。現在http://localhost/blog/post/12 將會顯示404錯誤頁面,只有http://localhost/blog/post/1 才會出現之前填入的內容。
30天成爲Laravel萌新(第20天) - 資源控制器(Resource Controller) 上篇
CRUD & RESTful
所謂CRUD是Create、Read、Update、Delete。昨天已經從資料庫模型(Model)的角度看過基本操作了,今天要將些操作加入到控制器(Controller)。
C | Create
R | Read
U | Update
D | Delete
另外,還需要另外知道的一件事情是RESTful。RESTful並不是硬性規定,只是一種在HTTP請求上的一種慣例設計。通常HTTP請求方法有GET
、POST
、PUT
、DELETE
、PATCH
,以及HEAD
、CONNECT
、OPTIONS
、TRACE
。以下節錄自MDN:
GET
GET 方法請求展示指定資源。使用 GET 的請求只應用於取得資料。
HEAD
HEAD 方法請求與 GET 方法相同的回應,但它沒有回應主體(response body)。
POST
POST 方法用於提交指定資源的實體,通常會改變伺服器的狀態或副作用(side effect)。
PUT
PUT 方法會取代指定資源所酬載請求(request payload)的所有表現。
DELETE
DELETE 方法會刪除指定資源.
CONNECT
CONNECT 方法會和指定資源標明的伺服器之間,建立隧道(tunnel)。
OPTIONS
OPTIONS 方法描述指定資源的溝通方法(communication option)。
TRACE
TRACE 方法會與指定資源標明的伺服器之間,執行迴路返回測試(loop-back test)。
PATCH
PATCH 方法套用指定資源的部份修改。
其實也就差不多對應了CRUD的操作:
CRUD | RESTful |
---|---|
Create | POST / PUT |
Read | GET |
Update | PATCH / PUT |
Delete | Delete |
30天成爲Laravel萌新(第19天) - Model的基本操作
ORM是物件關聯對映(Object Relational Mapping)。在Larvel裡提供的 Eloquent ORM能讓開發者以一個簡單、美觀的方式操作關聯式資料庫。今天就來寫一點點基本的操作,更多可以查看文件,以及Laravel資料庫相關文件。
Select All
在SQL,可能會用SELECT * FROM <TABLE>;
來取得所有資料,儘管在資料量龐大時,這樣操作並不好,但是Laravel同樣有對應的操作:
use \App\BlogPost
$posts = App\BlogPost::all();
foreach ($posts as $post) {
echo $post->title;
}
條件查詢Where
條件查詢也非常長使用到:
$post = App\BlogPost::where('id', 1)->get();
// ->orderBy('name', 'desc') // 還可以加以設定以下參數
// ->take(10)
// ->get();
用where
自然很容易從SQL轉換了解。不過,Laravel有更簡單的方式針對主鍵做查詢:
$post = App\BlogPost::find(1);
// 等同於
$post = App\BlogPost::where('active', 1)->first();
30天成爲Laravel萌新(第18天) - 建立Model
前幾天建立了Blog Post在資料庫的Schema,並且遷移(Migration)進資料庫,建立相關的資料表(blog_post)。為了讓Larave提供的ORM系統,以一個美觀、簡易的方式操作資料庫,還得建立BlogPost Model類別。
當然也可以使用artisan建立一個樣板:
artisan make:model BlogPost
然後會有app/BlogPost.php
的檔案
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogPost extends Model
{
//
}
這次,只需要簡單改成下面的樣子就好:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogPost extends Model
{
protected $table = "blog_post";
// protected $primaryKey = 'id';
// public $timestamps = false;
}
30天成爲Laravel萌新(第17天) - 資料庫管理工具(下)‧使用DBeaver
今天要在介紹一個,我用過後就喜歡上的資料庫管理工具–DBeaver。
DBeaver的兩個版本
DBeaver有社區版和企業版兩種。社區版可以免費使用,並且跨平台,透過JDBC支援多種資料庫。此外還會自動檢測資料庫版本,自動尋找相對應的Drive。
我手動配置過phpMyAdmin過。同樣直接下載安裝的情況下,DBeaver簡單好多。
DBeaver的界面很漂亮,我還蠻喜歡的。其中還有一個功能測試時非常好用–產生假資料。這個和前幾天亂數短文很像,不過DBeaver會針對欄位型態產生資料。資料庫測試的模擬數據生成(Mock Data Generation)也有一些限制,有些資料類型還是無法產生。
連線資料庫並填入資料
說了這麼多,相信已經下載安裝好了吧?該像昨天一樣,來連接資料庫,並塞入文章吧!
30天成爲Laravel萌新(第16天) - 資料庫管理工具(上)‧使用phpMyAdmin
今天,要用 phpMyAdmin 來看看昨天建立的資料表,我會順面補充一些之前做的設定。
查看Docker容器狀態
我會假設使用的是laradock,如果你使用的是XAMPP可能會簡單一些。
那麼,首先先看看運行的容器:
docker-compose ps
我們之前有啟動phpMyAdmin的服務,那麼輸出應該包含下面內容:
Name Command State Ports
---------------------------------------------------------------------------------------------------------------
laradock_phpmyadmin_1 /docker-entrypoint.sh apac ... Up 0.0.0.0:8080->80/tcp
其中注意到 Ports 的部份,這意思是綁定(bind)電腦8080端口到容器的80端口。所以我們可以透過瀏覽 http://localhost:8080 來使用phpMyAdmin。
登入phpMyAdmin
這裡伺服器要輸入mariadb
,帳號和密碼分別是:default
和secret
。如果你是啟用MySQL的話,伺服器就改成mysql
。
30天成爲Laravel萌新(第15天) - 建立資料庫Migration
要把文章存入資料庫?那麼對於傳統關聯式資料庫,需要先建立資料表Schema。你可以透過明天要介紹的資料庫管理工具,也可以透過今天來來說的使用 Artisan 建立資料表。
使用Artisan建立資料表
Artisan可以快速的建立控制器基本模板,同樣也可以建立資料庫相關模板:
artisan make:migration create_blog_post_table
透過上面命令,會在database/migrations
新增相關檔案,檔名由<日期><時間>_create_blog_post_table
組成。前面時間相關的在某些情況下會很重要,後面介紹的 artiasn 命令會依序建立資料表,所以如果資料表有相互依賴就有影像。現在我們開啟該檔案看看:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateBlogPostTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('blog_post', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('blog_post');
}
}
其中最重要兩行:
$table->bigIncrements('id');
$table->timestamps();
30天成爲Laravel萌新(第14天) - 控制器(Controller)
回頭看看,看,怎麼Route的內容變得這麼長阿!仔細一看,還做了路由以外的事情,像是修改變數內容等等。是不是該把Hangle Function獨立出來?有什麼辦法?Laravel的控制器(Controller)可以幫助。
新增Controller
我們可以透過Artisan來快速新增一個標準的控制器(Controller):
artisan make:controller Blog/ExamplePostController
在app/Http/Controllers/Blog
資料夾下會多一個ExamplePostController.php
的檔案,內容大致如下:
<?php
namespace App\Http\Controllers\Blog;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class ExamplePostController extends Controller
{
//
}
Blog
這個新建立的資料夾,實際對應於昨日group
路由當中的'namespace' => 'Blog'
參數。並且在上面生成的程式中use App\Http\Controllers\Controller;
,也是因為這個新建立的模組是在App\Http\Controllers\Blog
底下的命名空間,因此要引入使用App\Http\Controllers\Controller
。關於PHP的模組系統,就不多做解釋了,只是相關的Blog
字串如不想使用,要做相對應的調整。
之後會改用資源控制器。這邊單純要做個對照,之後可以刪除不使用。
30天成爲Laravel萌新(第13天)- 簡易Blog頁面(下)
昨天利用亂數文章(Lorem ipsum)產生了@section('body')
的預設內容。今天,要在Route把變數$content
設成類似的亂數文章。
中文亂數文章
Lorem最多的資源大多還是英文,不過中文也有不少資源可以使用。此外,有些情況使用設計好的預設文字可能會更適合。今天,就只是想來用用而已XD。透過亂數假文產生器 Chinese Lorem Ipsum可以產生中文亂數文章,也可以用於產生標題。現在來簡單的把$title
設成日的及度加機子魚年
。$content
設成:
身主一?是字對一日國地包最中感行物評民活於力!進上城大、很會英我:的樣基覺型家海當期是著:算像系大心樣十因天特企以加想點以後許,念落是客我什在白實文種運明市下只能當作力美間要速包企以統推多,收什火投,顧外。
指的司之身鄉然說小人發,我雜人轉英臺區實說麼後男會友大,阿產效第續明造識竟自沒大上有。到全藝地港為精件連需,過聯排可然更友我後們時?一風了組麼兒比、方來出科!車卻讀力風人父資國個年愛成故作自功:原用中有、港看車我高心打!親北受活女微特利動果的無何片經物……象巴約得?色時值壓五……過點子得提圖陽可計,來麼室拉的合天!人觀難一手樹以防黃精也需被那部我冷不不連痛以哥多人事營件、目熱而夫加……紀味曾蘭不好空農後電題熱頭國:戲識者是相大覺光,雙命影心可那?前區孩增,物時。
產明物力區有王真?院見很原,經總萬官方,生回。
海在康代界積實兒變:她哥出邊。善快寶,死面大,紀著資小成去資戲面和的發行的止的接來,南生不能野:政生企性班們教求給學就得受再球也生克決的金長冷成。
標題、文章內容當然還無意義wwww
調整排版
恩~沒分段了?這是因為HTML會忽略空白,如過要換行還需要使用<br/>
。當然你可以在變數內容這麼加,然後在模板將顯示的部份改為{!! $content !!}
,或是也可以用<pre>
來做,像是把@section('body')
改成:
@section('body')
<h1>{{$title}}</h1>
<pre>{{$content}}</pre>
@endsection
30天成爲Laravel萌新(第12天)- 簡易Blog頁面(上)
今天要來建立一個部落格文章的極簡陋顯示頁面。
建立路由
在routes/web.php
檔案中添加以下內容:
Route::group(['prefix' => 'blog',
'as' => 'blog/',
'namespace' => 'Blog', ],
function(){
Route::get('/post/{post_id}', function($post_id){
$title = "Example Title";
$content = "Example Content";
return view('blog.post', [
"title" => $title,
'content' => $content,
]);
});
});
儘管其實完全不需要用到group
,不過未來可以像其他blog系統一樣,除了有文章頁面(post),還可以有其他獨立頁面(page)。另外,添加blog的前綴,可以將其他前綴用於其他作用,或是最後在做個轉址。總之,先選了個靈活的寫法。
其中,好好注意$title
和$content
這兩個參數,這兩個參數隨後會在多次變動修改,今天主要先把頁面顯示出來。
部落格文章頁面模板
接者建立resources/views/blog/post.blade.php
檔案(當然需要先建立resources/views/blog
資料夾)。並且上頭原本路由區用的view也可以寫為blog/post
。將這份檔案簡單填入以下內容:
@extends("base",['title'=>$title]) {{-- 第二個參數可以傳遞變數給父模板,但是父模板改用插槽方式,須改用以下方式 --}}
@section('title', $title)
@section('body')
<h1>{{$title}}</h1>
{{$content}}
@endsection
這個頁面模板繼承了基礎頁面(base
),並填入頁面內容。不過,還沒有基礎也面阿?現在來建立resources/views/base.blade.php
,並寫下以下內容:
30天成爲Laravel萌新(第11天) - 路由&頁面模板(4)
使用Artisan顯示目前路由狀態
首先,先來說說怎麼看目前路由狀態。透過artisan route:list
列出目前路由狀態:
+--------+----------------------------------------+--------------+------+---------+--------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+----------------------------------------+--------------+------+---------+--------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api,auth:api |
| | GET|HEAD | hello | | Closure | web |
| | GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS | hello-world | | Closure | web |
| | GET|HEAD | hello/{name} | | Closure | web |
+--------+----------------------------------------+--------------+------+---------+--------------+
首先要注意到的是Method
和URI
,這也是在定義路由最基礎的部份,URL
在路由定義時就稱作PATH
。我們還可以將路由命名(Name
);至於Action
與以後會提到的控制器(Controller
)有關,這裡顯示Closure
表示路由由handler function
處理請求;最後Middleware
預計本系列文章不會提到,又興趣可以去看官方文件。
api/user
定義在routes/api.php
中。
基礎路由方法
基本路由方法有:
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
基本上就對應了REST的GET
、POST
、PUT
、PATCH
、DELETE
、OPTIONS
的請求方法。此外還可以透過Route::any($uri, $callback);
來直接處理請求,而不管請求方法;或是使用Route::match([$method,...], $uri, $callback);
來處理特定請求方法。
※ 請求方法 是HTTP請求裡頭的一個欄位:METHOD。
30天成爲Laravel萌新(第10天) - 路由&頁面模板(3)
回去看了一下文件,才發現之前有好多沒用到過 畢竟只學了2周的時間。不過有些設計挺有趣的。
今天來看一下Blade模板語言中的一些(我覺得)重要的功能。
印出變數
昨天已經看過怎麼顯示變數了,使用{{$variable}}
。今天要特提的是:怎麼給變數一個預設值。
{{isset($variable) ? $variable : "Default Value"}}
{{ $variable or 'Default' }}
不過在5.8、6.x以後似乎改成這樣:
{{$variable ?? 'Default'}}
我不清楚爲什麼把這麼方便的功能給去除掉了。 6.x之後的用法並沒有在官網的文件特別提,會發現單純是範例中有類似程式碼。
上面其實還相當於
@isset ($variable)
{{$variable}}
@else
Default
@endisset
另外還有@empty
@empty($variable)
Default
@else
{{$variable}}
@endempty
30天成爲Laravel萌新(第9天) - 路由&頁面模板(2)
繼續昨天
Route::get('/hello/{name}', function ($name) {
return '<h1>Hello, '.$name.'</h1>';
});
透過路徑接受了參數傳了進來,再加以加工輸出回去。不過這麼寫不怎麼美觀, 因為這違反了 「 只做一件事情 」的原則,不但處理了路由請求,還傳遞參數並渲染頁面。
今天透過模板系統,要來把路由請求處理與頁面設計分離。
首先,把上面程式修改為以下內容
Route::get('/hello/{name}', function ($name) {
return view("hello-name", [
"name" => $name,
]);
});
接著在新增一個模板檔案resources/views/hello-name.blade.php
<h1>Hello, {{$name}}</h1>
然後瀏覽http://localhost/hello/Daniel 看看,應該與之前畫面並無差異。
30天成爲Laravel萌新(第8天) - 路由&頁面模板(1)
接下來幾天,會交叉介紹路由(Route)和頁面模板(View/Blade)。幾經思考,這兩者關係十分密切,不太好單獨撰寫。
關於路由(Route),我們曾經在第六天短暫看到過。
<?php
Route::get('/', function () {
return '<h1>Hello</h1>';
});
現在我們看看routes
資料裡面的內容:
- api.php
- channels.php
- console.php
- web.php
我們修改的是web.php
的內容,這也是最主要訂立路由的檔案。api.php
的檔案實際上也不太有差別,不過api.php
底下定義的路由,預設會前置增加api/
的前綴,是設計用於提供HTTP API路由定義的檔案。
瀏覽 http://localhost/ 以後,就會顯示Hello World,如果你嘗試右鍵瀏覽網頁原始碼(view-source:http://localhost/)就會看到<h1>Hello</h1>
。
在回頭看看網址,除了localhost
外,後面的就是路徑/
,認知道這個斜線是很重要的,因為Apache2和Nginx對於結尾斜線的認知有些不同。接著再在wep.php
增加以下入由看看:
30天成爲Laravel萌新(第7天) - 認識artisan
Laravel有「 為網頁藝術家創造的框架 」的美譽,他的工具名字也很有意思 artisan ,意為 工匠 ,與藝術家(artist)一樣,是與藝術(art)有關的字。
artisan 可以用來顯示路由狀態、遷移資料庫、產生基本樣板程式碼、調整文件結構狀態等等。而且之前已經看過,就是我們用來產生專案文件密鑰(key)的artisan key:generate
。
不過,如果使用laradock
進入workspace
的docker容器的話,可能會找不著指令。artisan
詳細使用方式,會在未來有需要時在做說明,今天,就先來簡單看一下。
如果使用laradock
進入workspace
的docker容器的話,找不著指令嗎? 透過下面命令切換到/var/www
目錄下在試試。
cd /var/www
列出所有artisan子命令
不同版本的artisan
有可能存在使用差異。今天主要說明怎麼快速了解指令如何使用。
首先先學著者麼列出所有能使用的工具:
artisan
或是
artisan list
30天成爲Laravel萌新(第6天) - 配置專案
在前三天已經安裝好Laravel的基本環境。今天算是一個分水嶺,不管你採用哪種方式建立開發環境,都應該已經得到一個預設好的Laravel工作目錄。在此我不會解釋目錄結構,有興趣可以自行參閱文檔。不過是先留意一下幾個文件與目錄:
- artisan
- config/
- database/
- public/
- resources/
- routes/
- storage/
之後有用到會在加以說明。而今天,首先要進行專案的配置,也就是設定(config/)。沒錯,目錄 config 就是儲存相關配置的目錄。不過在此,我們還得先編輯 .env 檔案。你可能會找不著這份檔案,別擔心,目錄下有個 .env.example ,將其複製並重新命名即可。
接著我們找到以下內容:
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
目前最爲重要的是關於APP和DB的設定。在發佈階段,APP_URL
會需要調整,不過我們之後在說說。而目前預設情況僅有自己的電腦能夠瀏覽。其次是APP_DEBG
,在發佈階段需要改為false
。 此外,還會注意到一個APP_KEY
的設定,如果其為空,請執行以下命令:
php artisan key:generate
接着是資料庫的設定,這裏需要改成資料庫的連結設定。如果使用MariaDB和MySQL,維持mysql
就好。(新版本的MariaDB可能有問題)
30天成爲Laravel萌新(第5天) - Laradock的工作空間容器
在開始配置Laravel的環境設定檔案之前(.env
),先來帶大伙看看laradocke最
重要的一個容器workspace
。
關於workspace容器
workspace是laradock連結各個容器的重要容器,還記得我們昨天這麼做嗎:
docker-compose exec workspace composer create-project --prefer-dist laravel/laravel tutorial_blog 5.8.*
docker-compose exec
的格式是docker-compose exec <CONTAINER> <COMMAND>
。可是,可沒有吧workspace
啟動(up
)起來呀!我們像下面啟動了nginx
、mariaDB
、phpmyadmin
而已阿。
docker-compose up -d nginx mariadb phpmyadmin
是的,laradock會自動啟動workspace
這個容器。並且注意到後面的COMMAND
實際就是在第2天用來初始話laravel專案的命令。在下完這的命令以後,會在原本建立的 laravel-tutorial 目錄下多一個 tutorial_blog 目錄。並且裡面有基本Laravel的環境。我們會在幾天後來設定這個環境,現在,來我們專注於 workspace 這個容器。
我們同用用docker-compose exec
來進到 workspace 容器裡面。
docker-compose exec -u laradock workspace /bin/bash
或是用docker exec
來進到裡頭。不過使用docker exec
你還會需要知道真正的容器名字。所以命令可能像是下面這樣:
docker exec -it -u laradock laradock_workspace_1 /bin/bash
我們使用 laradock 登入workspace容器(-u/--user
)。laradock 預設用於開發的使用者帳號,如果不加上這個選項,會使用最高管理權限登入(root)。透過使用這個帳號登入,未來可能可以省去一些不必要的麻煩(ex:宿主機和虛擬機掛載目錄的權限問題)。
30天成爲Laravel萌新(第4天) - 使用laradock建立開發環境(下)
建立專案目錄
本次專案目錄預計會有以下內容:
- laravel-tutorial
- laradock
- .laradock
- tutorial_blog
laradock 和 .laradock 先不管他們。先建立 laravel-tutorial 目錄,並在該目錄鍵入:
git clone https://github.com/Laradock/laradock.git
cd laradock
git checkout v7.15
以上會安裝laradock,並切換到7.15版本。
設定laradock
在建立服務容器前,要先做設定。
首先, 複製 env-example 為 .env,然後找到以下設定並變更:
APP_CODE_PATH_HOST=../tutorial_blog
...
...
...
DATA_PATH_HOST=../.laradock/data
APP_CODE_PATH_HOST
指定專案目錄(下一步驟建立),DATA_PATH_HOST
則是未來資料儲存的位置,包含資料庫儲存位置。
接着建立並啓動環境:
30天成爲Laravel萌新(第3天) - 使用laradock建立開發環境(上)
除了使用composer
以外,還可以使用 laradock 、Homestead、Valet、Laragon。Homestead是基於 Vagrant ,如果您已安裝VirtualBox,可以使用看看。而接下來說明laradock的使用方式。
Laradock 環境需求
laradock是基於docker的一個快速建立laravel的開發環境工具,理所當然的你會需要docker,此外你還會需要 docker-compose,以下列出範例使用的版本:
軟體 | 版本 |
---|---|
docker | 18.09 |
docker-compose | 1.25 |
git | 2.7.4 |
透過使用laradock,可以很快速的在Apache2、Nginx;MySQL、MariaDB;甚至是在PHP不同版本之間做切換。
30天成爲Laravel萌新(第2天) - 安装 Laravel
環境需求
這不是全部強制的,只是接下來一個月的時間,會以以下環境為範例:
軟體 | 版本 |
---|---|
Laravel | 5.8.18 |
Nginx | 1.14.0 |
MariaDB | 10.3.15 |
PHP | 7.2.19 |
如同前言所說,雖然Laravel已經釋出6.0版,但接下來將會以5.8為主。此外,也可以使用Apache網頁伺服器,儘管有些設定不同;至於資料庫也可以使用MySQL。並且,以上也都不是強制的,Laravel對於多個網頁伺服器、資料庫接受度良好,所以當然也可以使用PostgreSQL和其他支援PHP的網頁伺服器。
(雖然上面這樣列出,不過最後有可能會用Apache+MySQL再測試一次)
使用XAMPP
明天,我會介紹另外一個快速建置環境的方式,我會更推薦使用該方式。
如果你是Windows,可以直接安裝XAMPP,只是在之後如果遇到問題,請注意一下各個組件的版本。更多可以參考XAMPP的網站。
安裝Laravel
Composer是PHP的一個包管理器,儘管不是必要的,不過可以大量簡化Laravel的安裝程序,並且也可以加以安裝其他組件。因此,需要先確定Composer已經安裝好,並設定好環境。你可以透過 命令提示字元 或其他 Shell 的環境輸入composer -V
,正確安裝完會顯示版本資訊。
安裝Laravel:
composer global require laravel/installer
30天成爲Laravel萌新(第1天) - 認識Laravel
原本,我是想寫下Laravel的介紹,但是…可能有些單調。因為有些經驗實在是 不太多,就算是看別人寫的關於Laravel的特色,和過往PHP開發到底又怎樣差異, 也還是對我而言有些無感。因此決定從我自身角度來介紹Laravel。
Laravel又被人稱為 為網頁藝術家創造的框架 。那是因為,相較於以前PHP 將頁面資料與邏輯代碼混合寫在一起的 義大利麵寫法 , Laravel是類似 Django這樣廣義的MVC框架。也就是將頁面資料與邏輯代碼分開。
那摸到底為神ㄇ要用Laravel? 我簡單列出以下以點:
- 首先,PHP還沒有死亡。實際上PHP在許多地方還是可以看的到。著名的 WordPress、Drupal都是用PHP寫的。在許多與網頁相關應用方面,PHP成熟且 易用,這也讓使用PHP多了一個理由。過去寫過很短的PHP還活 著 可以去參考一下wwww。
- 相比WordPress的易用、易上手,Laravel提供更高度的彈性。
- 遵守Laravel一些基本的開發原則,原始碼更容易維護。(就是有些人一樣能 寫成義大利麵….)
- Laravel提供多個極為好用的基本可選用功能。包含身份驗證、資料驗證、資 料庫分離、ORM、資源控制器、上傳等等等。
30天成爲Laravel萌新(第0天) - 前言
今年有一段時間,短暫的1~2周,因為一些原因學習了Laravel,這次我事後的學習筆記。
Laravel是一個流行的PHP開發框架。不同於熱門的Drupal、WordPress.org, Laravel更像是Node.js的Express.js、Python的Django等等廣義的MVC框架。當然,他也可以快速的發展成CMS。
Laravel目前也發展到6.0,不過接下來幾天的內容會以5.8為主。Laravel也有完整的開發文檔。儘管有些翻譯仍然不完全,但也有多種語言的翻譯。首先,介紹一些學習資源:
- Laravel 官方網站
- Laravel.tw 中文站 文檔最新只到5.3版本
- Laravel中文學院
預計未來內容撰寫的方向:
- 關於Laravel
- 建立Laravel開發環境
- 使用Composer安装
- 使用laradock建立環境
- 認識artisan
- Route - MVC
- 會員系統
- Template
- Controller
- 文章資料庫 - Database Schema
- 商品資料庫 - 驗證資料
- 上傳檔案
- API
Tag: python
淺嘗Python3.9新特性
作為一個開發人員,最好固定一段時間就去了解一下有什麼新的東西可以使用。它可能不會馬上使用到,但是有印象會加速當需要使用時,尋找的時間。
這次,稍微看了一下Python 3.9和Python 3.10的相關改動,並玩玩紀錄一下一些我認為有點意思的部份。
現在Python的更新文件幾乎都有中文版,閱讀起來並不是那麼困難,但是有部份特性並沒有說的非常清楚,還是看一下PEP和親身使用下比較明白。(隨然還是有些注意到的特性,仍不知道是要做什麼的)。
Python 3.10編譯
為此,我直接下載Python 3.10來編譯使用,順便紀錄一下編譯過程。
./confiure --prefix=`pwd`/dist/
make
make test
make install
編譯過程還算順利,除了在test階段test_os是失敗的,並跳過了20多個測試,但不影響接者的使用。喔對,不知道哪裡下載原始碼的,可以在這個頁面找到。
Python 3.9 釋出
Python 3.9於2020年10月05日正式釋出。其中主要幾個新特性包含:
表示無限大。運算與比較(Python)
前言
在做LeetCode題目。然後因為我思考邏輯的關係,會需要使用到無限大(Inf
)的概念,所以首先自幹的一個:
class Inf(numbers.Number):
def __gt__(self, n: numbers.Number):
return True
def __eq__(self, n: numbers.Number):
if isinstance(n, Inf): return True
else: return False
遺憾的是,這雖然可以處理Inf() > int(1)
,卻無法處理int(1) > Inf()
的問題。無法輕易直接修改內置類型int
,要是在多包一層感覺有些彆扭。所以找找看Python有沒有像EMCAScript內有Infinity
的東西。可是…我第一個看的卻是日文內容。
Google中文第一個查到的也是簡體中文。要不就是Numpy
的東西。
於是乎,這篇簡單的分享就出來了。
※ 最開始,我還有在numbers裡面找,無果。
什麼是IIFE(Immediately Invoked Function Expression)
前言
這原本我是在Facebok一篇貼文的回應,因為覺得還蠻清楚的,所以修改過來這裡紀錄一下。(當然也可能我有理解錯誤就是)
什麼是IIFE
根據MDN是這樣寫的:
It is a “design pattern” which is also known as a Self-Executing Anonymous Function
(他又稱為 Self-Executing Anonymous Function,也是一種常見的"設計模式”)
注意 設計模式(design pattern)
這不是特指某一種技術、特性。而是思想、設計。竟然是想法,實現有差異、認知有差異可以理解的。
所以按照中文定義( 定義完馬上就執行 ),淺層廣義的來看:
function f(){};
f();
確實也是定義後執行
撰寫風格上OK,只是益處不大。
(f = function(){})() // execute first
f() // execute second
也是一樣。(這再js是合法的,之後還可以在執行f()
)
但就語法上就更精簡許多
從狹義、技術來看,是看是不是有附值(函式命名也是),然後才執行。 如此剛題的兩個,都是命名後才執行,狹義不算是IIFE 不過這終究只是一種設計模式,實現方式沒有一定。
function f(){};
f();
如果上面的形式,編譯器可以做優化,而直接忽略f
,那他也可能是狹義的定義。
(另外這裡廣義、狹義只是我片面的解讀)
此外,在維基百科這麼寫到:
“立即呼叫函式表達式” 最早稱為「自執行(匿名)函式」 但是立即執行的函式不一定是匿名的。 ECMAScript 5的 strict mode 禁止arguments.callee 因此,這個術語不夠準確。
這或許也側面證明我的想法。
關於Python Lambda那些可能不知道的三兩事
lambda本質和function無異
def f():
pass
type(lambda : None) # => <type 'function'>
type(f) # => <type 'function'>
lambda :None # => <function <lambda> at 0x7ffa6d343650>
f # => <function f at 0x7ffa6d349dd0>
lambda和function的型態都是function
,並沒有區分開來。
一個是語句、一個是表達式
差別在於def
是關鍵字形成的語句(statement),lambda
是表達式(expression)。他們差別在於,能出現在程式碼的不同位置。
※ Note: 表達是(expression)也是一種語句(statement)。為了簡單來看,接下來都會區分開來。
內建有多個函式可以接受函式參數
因為lambda和function本質差無異,所以不會有函式只可以接受lambda,不可以接受function。
filter(lambda parameter: expression, iterable)
filter(function, iterable) # more correct
比起使用lambda描述filter,用function更正確。reduce等也是這樣。
lambda多行的寫法
通常看到lambda
都只有一行,正常情況下也都是這樣。不過先來看看文件怎麼描述lambda的語法:
lambda_expr ::= "lambda" [parameter_list] ":" expression
lambda_expr_nocond ::= "lambda" [parameter_list] ":" expression_nocond
相當於:
def <lambda>(parameters):
return expression
【微更】你可能沒看過得Python - Callable(續)
【微更】Callable的實現
之前我實現了Callable Class,當時對於CallableWrapper
的實現如下:
class CallableWrapper:
def __init__(self, wrap):
self.wrap = wrap
def __call__(self, f=None, *args):
if f == None:
return self.wrap
return CallableWrapper(f(self.wrap, *args))
這個實現有一些缺憾,CallableWrapper
的__call__
回傳值,也是CallableWrapper
。而CallableWrapper
本身預期被呼叫,這導致結果不能直接使用,需要多給一次空呼叫(result()
)。舉例來說:
arr = CallableWrapper([1,2,3,4,5,6,])
arr # => <__main__.CallableWrapper object at 0x7f2102f00668>
# arr(sum) + 10 # You can't do this, because CallableWrapper can't add integer
arr(sum)() + 10
上例中,並不能直接寫arr(sum) + 10
,要寫arr(sum)() + 10
。這感覺有點脫褲子放屁阿…
基於此想法,我改寫成:
MyString委派(delegate)加法
之前擴展String的+方法。不過這樣的實現不是很彈性:
count = 10
"-"*10
# "Count: " + count # will rise exception
"Count: " + str(count) # allow string + string
class myString(str):
def __add__(self, other):
if other == None:
return myString(str(self) + "None")
elif type(other) == str:
return myString(str(self) + other)
elif type(other) == int or type(other) == float:
return myString(str(self) + str(other))
else:
return myString(str(self) + str(other))
myString("Count: ") + count # success. => 'Count: 10'
myString("Count: ") + count + " and " + count # => 'Count: 10 and 10'
myString("Count: ") + None # => 'Count: None'
這次來稍微修改,改用委派的方式。
Python的資料模型(Data Model)的特殊成員(Special Member)
上一篇寫到,覆寫Python操作子(Override Operator)。其中提到operator這個Package,並對類別實現特殊方法,來覆寫運算子的行為。不過這麼寫不完全正確,只是Operator Package的頁面清楚的多,而這覆寫的動作,實際更直接與Python資料模型中的特殊成員(特殊屬性&特殊方法)(Data Model - Special Member(Special Attribute & Special Method))相關。
Python的函數(Function)vs方法(method)
前言
※ 本段有些難度,略過並不影響後續理解。
在幾天前,我實現了一個可以傳入函數呼叫的類別(Callable Class),相關可以參考「用Python實現Callable Class」。
起初,我實現Meta-Class的方式,是在defineclz.__new__(clz, *args, **kwargs)
實例(instance)後,將實例增加函數成員(Function Member)。不過這樣做並不太成功。會需要使用instance.__call__(f, *args, **kargs)
去執行。而直接複寫defineclz.__call__
的話,以會造成不斷呼叫的問題,而且無法讓後續繼承子孫,再進一步調整方法。這也是為什麼後來以內部類別實現,並且另擁有__call__
能力的類別,後於defineclz
,好讓自定義的__call__
可以複寫掉預設行為。
※ Note: 本文寫到的函數成員(Function Member),主要是指實例屬性,但這個屬性是一個函數。更多時候,成員(Member)可能就包含方法(Method)和屬性(Attribute),而函數成員(Function Member)可能就直指方法(Method)。
定義方法(method)vs定義函數(function)
※ 尚為去翻找文件,可能有錯誤。
在Python,可以用def
關鍵字去定義一個函數:
def f1():
print("call f1")
f1()
而方法的定義也是使用def
:
class C1:
class_attr1 = {}
def __init__(self):
self.instance_attr1 = {}
def _f1(self):
print("C1: f1")
def f1(self):
self._f1() # if wrote `_f1()`, it will not found
def f2(self):
def _f2():
print("C1.f2: _f2")
_f2()
c1 = C1()
c1.f1()
c1.f2()
但其實這裡就可以看出一些行為的不同。上面_f1()
和f1()
看是在同一個scope,但是如果f1()
需要呼叫_f1()
,必須使用self._f1()
或是C1._f1(self)
,如果直接寫_f1()
會報錯說找不到。相對來說f2()
是C1
類別的方法,但是內部函式_f2
不是,因此就不需要這樣寫。現在在把f1()
移出類別外,變成一般函式看看:
用Python實現Callable Class,FP更好寫
This Article has English Version, please goto here to read.
前言
最近,和朋友們在解LeetCode的題目。看著不同人寫出來的程式,也讓我對於一個題目的解法,有更寬廣的視野。
938. Range Sum of BST
URL: https://leetcode.com/problems/range-sum-of-bst/
這篇文章與這個想法,也是受到朋友寫的一個Ruby寫法的啟發。我用同樣的邏輯去寫Python,如下圖:
當然,這題目有更好更快的寫法,但這樣寫更為有趣有意思。
但是,內部隱含的block
函數,從函數型語言的設計原則來看,他是危險、骯髒的。這是因為每次他的執行,都會修改到外部變數s
的的值。(這辦法不夠 純函數 )
因此,我修改成以下方式:
如此,你還可以改寫成一行:
關於Python 3.8的象牙運算符(:=)
我沒想到一天會來寫個三篇😅。不過看完PEP 572 and The Walrus Operator後,對於象牙運算符(:=
),稍微產生了點不同樣的看法。至於稍早前覺得蠻新鮮的想法…可以去看看我上一篇。
此外,感謝Chang-Ning Tsai在社團分享該篇文章。
有爭議的象牙運算符(:=)
it is ambiguous for developers to distinguish the difference between the walrus operator (:=) and the equal operator (=). Even though sophisticated developers can use “:=” smoothly, they may concern the readability of their code.
最近在解LeetCode,在沒有這個賦予值的方法前,真覺得有些時候有點不方便。可是這個運算符在是否納入時,似乎有引起一些爭議。像是:=
可能難與閱讀,不過我覺得比較有問題的還是賦予值運算子(=
)和象牙運算子(:=
)的混搖,更正確的說,我是覺得兩者有點太像,這似乎有點違反Python的則學之一:「There should be one– and preferably only one –obvious way to do it.」,用同樣的方式做事。
然後,不好意思,有一條是怎麼回事?
Although that way may not be obvious at first unless you're Dutch.
Dutch怎麼了嗎?
繼續深入
Developers may confuse the difference between “:=” and “=.” In fact, they serve the same purpose, assigning somethings to variables. Why Python introduced “:=” instead of using “=”? What is the benefit of using “:=”? One reason is to reinforce the visual recognition due to a common mistake made by C/C++ developers. For instance,
int rc = access("hello_walrus", R_OK);
// rc is unintentionally assigned to -1
if (rc = -1) {
fprintf(stderr, "%s", strerror(errno));
goto end;
}
Python3.8的幾個我覺得有意思的新特性
Python 3.8同樣有不少變動,有些蠻細微的,需要詳細了解可能還是直接看Python 3.8 有什么新变化der好。現在文檔都有(簡體)中文版,覺得比英文好讀多了。所以這篇只會分享幾個我覺得有意思的新特性。
遺憾的是,我還沒有更新到3.8,只能看看而已😭。
f-Strings
這個特性我單獨寫了一篇,歡迎看看:Python-3.8後的f-String。
象牙運算符(:=)
18:00更新
關於這看起來有點好用的東西,在做了更深一步的了解,可參考:關於Python 3.8的象牙運算符(:=)
寫過Golang的同鞋,對於這個運算符應該蠻熟悉的,這另這兩個語言有自動推導類型的能力。不過在Python裡,是相較於asign運算符(=
),象牙運算符(:=
)更像表達式(expression),而非語句(statements)。要我寫的話,偽代碼可能有點像:
※ Note: 表達式(expressions)也是一種語句(statements)。表達式會嘗試求值,但是語句不一定。此處主要是想表達 象牙運算符(:=) 會嘗試求值。
# @symbol var
def :=(val):
nonlocal var
var = val
return var
賦予完變數值後,返回變數的值。這麼一來可以寫在任何需要表達式的地方(如果我沒理解錯的話0.0)。來看幾個文件上的範例:
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
原本f-Strings裡可能要寫"{len(a)}"
,但是這樣做的話就無須在對len(a)
求值一次;或是在外圍設定變數值n = len(a)
。(儘管最近觀察,內置內行的len(var)
求值,可能還比賦予值(=
)消耗成本還低)
接下來這例子就讓我真的很想用了:
# Loop over fixed length blocks
while (block := f.read(256)) != '':
process(block)
我一度在想會不會有while expr as var:
的語句。雖然沒有,不過用:=
可以更為靈活。
PEP 578: Python 運行時審核鉤子(Runtime Audit Hooks)
Python-3.8後的f-String
近況
最近在翻itertools
、collections
,和已經很常在用,但想看看還有啥的functools
。這裡頭的東西並非必須,大部分我都有能力自幹。但沒必要再造輪子,甚至可以從此獲得一些啟發。譬如,itertools.groupby
,我看到第一個想法就是:「這不就可以更簡單實現Hadoop裡,Map-Reduce分組的功能?」。於是乎就寫了下面一段程式:
from itertools import groupby
from functools import reduce
a = [1,2,3,4,5,6,7,8,9,]
def pipe(data, *process):
result = data
for proce in process:
result = proce(result)
return result
p = pipe(a, lambda x: map(lambda y:y**2, x),
lambda x: sorted(x, key=lambda y: y%2),
lambda x: groupby(x, key=lambda y: y%2),)
for g, d in p:
l = list(d)
s = sum(l)
print("{g}: {d}, sum:{s}".format(g=g, d=l, s=s))
不知道python有沒有
pipe
的內部實現?
這段程式碼是將list a
裡的奇數和偶數分組取平方加總。比較麻煩的是groupby依照官方文件寫法,相當於以下程式碼:
class groupby:
# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
def __init__(self, iterable, key=None):
if key is None:
key = lambda x: x
self.keyfunc = key
self.it = iter(iterable)
self.tgtkey = self.currkey = self.currvalue = object()
def __iter__(self):
return self
def __next__(self):
self.id = object()
while self.currkey == self.tgtkey:
self.currvalue = next(self.it) # Exit on StopIteration
self.currkey = self.keyfunc(self.currvalue)
self.tgtkey = self.currkey
return (self.currkey, self._grouper(self.tgtkey, self.id))
def _grouper(self, tgtkey, id):
while self.id is id and self.currkey == tgtkey:
yield self.currvalue
try:
self.currvalue = next(self.it)
except StopIteration:
return
self.currkey = self.keyfunc(self.currvalue)
深入了解scikit Learn裡TFIDF計算方式
TFIDF計算說明
參加今年iT鐵人賽時,曾經寫過簡單使用scikit-learn裡的TFIDF看看,並寫到scikit-learn裡tfidf計算方式與經典算法不同。後來在官方文件中找到說明,也簡單嘗試了一下。這次來做點分享。
在經典算法,TF是這樣計算:。不過scikit-learn是直接用
n_(i,j)
,也就是使用CountVectorizer
的結果。
IDF的部份,原本經典算法是:,為了避免分母為零,也經常會使用
log(n/(df(k)+1))
計算,也就是將分子+1
。scikit-learn裡面則分成兩種,預設使用smooth
的版本:log((n+1)/(df(k)+1))
,也就是分子分母都加一;另一種是經典原始版本,而外加上1:log(n/df(k))+1
。
最後sckit-lean會做標準化(normalize),所以最後結果會是normaliz(tf*idf)
。
嘗試實驗
引入套件
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
import math
from sklearn.preprocessing import normalize
實驗資料
d1 = 'a b d e d f a f e fa d s a b n'
d2 = 'a z a f e fa h'
d3 = 'a z a f e fa h'
對,基本上就是簡單使用scikit-learn裡的TFIDF看看所測試的方式,所以當然也要將stop_words=None
, token_pattern="(?u)\\b\\w+\\b"
而外設定,原因這裡就不多做說明了。
Python在3.6後幾個便利的新變化
前言
這幾天又看了/試了不少東西喔。雖然也開始往下一個階段進行, 但興趣的事情,還是添加不少東西能來寫下隨筆。
這次的話題與前幾篇–JShell?-程式語言越來越像Lisp有關。 在那篇我留下了不少以後可能回來談談的話題,包含:
- JAVA版本之謎。
- 為什麼不使用LISP?
關於第一點,我打算去編譯看看幾個我挺喜歡的軟體再來說說感想, 但幾日前在Medium看到一篇文章。裡面有張圖片這麼形容JAVA:
JAVA騎士要去救城堡裏的公主,他很快的完成裝備前往救援,但是 他發現竟然有兩個 不同版本的城堡,重點是裡面沒有公主 。
Python 3.6之後LAG覺得有用的幾個新特性
已經忘記原因了齁,但數天前又LAG跑去看Python Whtat's news。 今天來分享幾個又LAG覺得非常有用的新特性。
不過很可惜的,又LAG主要的電腦還停留在 Python 3.5.2
。要更新到新版本需要自行編譯,還好目前沒有這樣特別的需求,
並且筆電與虛擬機都是使用 3.6
或之後的版本,可以說如果你是安裝
Ubuntu 16.04
之後或其衍生版,應該內建的都是 3.6
之後的版本了。
至於Windows的用戶應該可以直接在官網下載安裝包。
實現Python裏面的classmethod和staticmethod
在Python裏面存在著兩個用於定義物件方法的內建函式(built-in function) – classmethod
和staticmethod
。其實我一開始在學的時候根本沒用過他們(´・ω・`),先不管他們常不常用,雖然應該就是常用所以才會被放進內建函式,不過因爲常見的特殊語法–修飾器,使得隨後定義的方法與實際作用變得不同,常讓初學的人摸不著頭緒。今天就來以實現說說這兩個函式。
預備知識
首先你要先有基本的Python概念。接著最好已經很瞭解 修飾器 的作用。如果不瞭解修飾器,強烈建議先閱讀函式修飾器。 雖然本文在最後也爲實現一個相似於修飾器的函式版本 – [wrap](#修飾器的作用與實現 – wrap) ,不過其又涉及到其他知識,並不建議直接閱讀。
Check List
- 我瞭解最基本Python
- 我知道Python裏面修飾器的作用
實現classmethod和staticmethod – MyClassmethod, MyStaticmethod
在實現之前,必須先瞭解classmethod和staticmethod有甚麼特性?在使用後會發生什麼事情?
一般而言,在定義物件方法時會給與def methodName(self, *args, **kargs)
,其中self
表示物件實例本身。如果你瞭解 Lua 會發現是同樣的顯式引用發法,並且Lua中的語法糖instance:method(...)
(相當於class.method(instance, ...)
),幾乎就對應於Python裏面的Instance.method(*args, **kargs)
(相當於Class.method(Instance, *args, **kargs)
)。
相較之下,C++、Java裏面的this
,或是VB裏面的Me
,就是隱式用法。我自己是比較喜歡顯式引用,比較明確。
Tag: python3
Python在3.6後幾個便利的新變化
前言
這幾天又看了/試了不少東西喔。雖然也開始往下一個階段進行, 但興趣的事情,還是添加不少東西能來寫下隨筆。
這次的話題與前幾篇–JShell?-程式語言越來越像Lisp有關。 在那篇我留下了不少以後可能回來談談的話題,包含:
- JAVA版本之謎。
- 為什麼不使用LISP?
關於第一點,我打算去編譯看看幾個我挺喜歡的軟體再來說說感想, 但幾日前在Medium看到一篇文章。裡面有張圖片這麼形容JAVA:
JAVA騎士要去救城堡裏的公主,他很快的完成裝備前往救援,但是 他發現竟然有兩個 不同版本的城堡,重點是裡面沒有公主 。
Python 3.6之後LAG覺得有用的幾個新特性
已經忘記原因了齁,但數天前又LAG跑去看Python Whtat's news。 今天來分享幾個又LAG覺得非常有用的新特性。
不過很可惜的,又LAG主要的電腦還停留在 Python 3.5.2
。要更新到新版本需要自行編譯,還好目前沒有這樣特別的需求,
並且筆電與虛擬機都是使用 3.6
或之後的版本,可以說如果你是安裝
Ubuntu 16.04
之後或其衍生版,應該內建的都是 3.6
之後的版本了。
至於Windows的用戶應該可以直接在官網下載安裝包。
實現Python裏面的classmethod和staticmethod
在Python裏面存在著兩個用於定義物件方法的內建函式(built-in function) – classmethod
和staticmethod
。其實我一開始在學的時候根本沒用過他們(´・ω・`),先不管他們常不常用,雖然應該就是常用所以才會被放進內建函式,不過因爲常見的特殊語法–修飾器,使得隨後定義的方法與實際作用變得不同,常讓初學的人摸不著頭緒。今天就來以實現說說這兩個函式。
預備知識
首先你要先有基本的Python概念。接著最好已經很瞭解 修飾器 的作用。如果不瞭解修飾器,強烈建議先閱讀函式修飾器。 雖然本文在最後也爲實現一個相似於修飾器的函式版本 – [wrap](#修飾器的作用與實現 – wrap) ,不過其又涉及到其他知識,並不建議直接閱讀。
Check List
- 我瞭解最基本Python
- 我知道Python裏面修飾器的作用
實現classmethod和staticmethod – MyClassmethod, MyStaticmethod
在實現之前,必須先瞭解classmethod和staticmethod有甚麼特性?在使用後會發生什麼事情?
一般而言,在定義物件方法時會給與def methodName(self, *args, **kargs)
,其中self
表示物件實例本身。如果你瞭解 Lua 會發現是同樣的顯式引用發法,並且Lua中的語法糖instance:method(...)
(相當於class.method(instance, ...)
),幾乎就對應於Python裏面的Instance.method(*args, **kargs)
(相當於Class.method(Instance, *args, **kargs)
)。
相較之下,C++、Java裏面的this
,或是VB裏面的Me
,就是隱式用法。我自己是比較喜歡顯式引用,比較明確。
Tag: rust
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
Tag: sso
【用Keycloak學習身份驗證與授權02】Quick Start(2)
昨天,已經完成了一部分配置,且也已經可以建立帳號並登入了。
不過,這只能算是半套,而今天要在來完成另外半套。
你可以按照昨天的做法,重新建立一個新的Client。
只是注意在建立的時候,「Root URL」改爲: http://localhost:4200
。
今天,我們要自己實現一個前端網頁去的Web App,然後綁定Keycloak去做登入。
前置要求:
- 用Keycloak建立一個Client
- 網頁開發基礎知識(HTML/CSS/JavaScript)
- TypeScript的部分知識
- Angular知識(非必要)
調整Keycloak的Client配置
前面說過,Keycloak的Client實際上並不是真正的Client Application,只是做了一些關聯。 今天就要來 快速開始 個自己的Web App。而首先,需要先調整Client的關聯:
- 選擇「Clients」
- 找到昨天建立的「my-quick-start-app」,然後點選「Edit」
這此調整主要做兩個修改:
Tag: ted
【TED筆記】為什麼你應該要定義你的恐懼而非你的目標?
TED連結:Transcript of “Why you should define your fears instead of your goals”
看完後,最近我也在嘗試使用這樣的方式分析、協助自己做決策。有時候定義目標很難,那何不嘗試試定義恐懼?
定義恐懼
斯多葛主義(Stoicism)的一種衍伸,其幫助於自己專注於自己可以控制的要件,而不去擔心不可控制的部份所產生出的恐懼。
你能控制與你不能控制(專注於前者)
未知是恐懼的。未來充滿未知、充滿恐懼,但或許是想多了?
「我們在想像中所受的苦比在現實中還多」
“We suffer more often in imagination than in reality,”
—塞內卡(Seneca)
間單說就是:
Tag: web
什麼是jQuery?前端框架盛行還需要JavaScript函式庫嗎?
- 本篇首發於ALPHACamp Blog(2020-03-25)
- 2020-04-05 更新
前端框架盛行還需要JavaScript函式庫嗎?
如果你想透過本篇文章,直截了當的知道該不該使用jQuery,可能要讓你失望了。
很難說到底應不應該使用jQuery,可能在某些情況下使用jQuery仍是最好的選擇。即使是現在的流行的三大前端框架,也是依照情況與需求使用。
了解jQuery到底是什麼、前端框架是什麼,會比要不要使用jQuery更有意義。本篇帶你看看我了解的jQuery前世今生
瀏覽器發展歷史上出現過幾次大混戰。儘管後來有了W3C(全球資訊網協會/World Wide Web Consortium)進行標準的制定,各個瀏覽器廠商仍開發各自瀏覽器的「特異功能」吸引使用者與開發者。這些特異功能促使了瀏覽器標準的進步,也照成了開發上的困難。
為解決網路應用中不同平台、技術和開發者帶來的不相容問題,保障網路資訊的順利和完整流通,全球資訊網協會制定了一系列標準並督促網路應用開發者和內容提供者遵循這些標準。1
2006年1月,jQuery
推出了第一個版本,隨後變成為了最受歡迎的JavaScript函式庫之一2。附帶一題,我們現在熟悉的HTML5標準是在2014年正式定稿推出3,即使是現在,每年瀏覽器的標準也是快速地在變動。在後文我會在說明為何開頭我想提到這兩件事情。
什麼是jQuery? 從常見功能來看
jQuery之所以受歡迎,是因為他提供了一系列簡單方便,而且兼容的操作,省去開發人員的部份煩惱。這裡舉幾個簡單的例子:
選取器
現代瀏覽器標準分成三個部份:
- HTML
- CSS
- ECMAScript / JavaScript
其中第三個就是常聽到的JS/Javascript。目前更正式的名稱是ECMAScript,也是今天主要jQuery主要的基礎。不同於瀏覽器腳本(Script)的標準,CSS2在以前就提供豐富易用的選擇器,讓設計人員撰寫樣式表(stylesheet)
Tag: work
關於SSO服務的DB群集節點調整設定經驗這件事
背景
目前任職公司的SSO服務架構是由我建立以及維護的。我們在四個廠區建立服務群集,每個廠區分別有一個負載平衡/反向代理節點、一個備援的負載平衡/反向代理節點,以及實際作為服務的兩個應用節點。
資料庫同樣在四個廠區皆有節點,並組成跨廠區群集,使用MariaDB的Galera Cluster機制。由於資料庫是多主叢集架構,各別都可以作爲主節點寫入,因此SSO服務節點使用的AD服務和DB服務都是設定相同廠區,減少物理上的路徑。但不同的是資料庫是由DBA負責管理的。
而這次的狀況是: 資料庫需要進行設定調整,啟用某些功能。這些功能有助於了解服務使用狀況等等。
不過DBA並沒有信心能夠調整而不影響服務。因此有先向我詢問過意見。
Tag: xpm
圖片格式-[XPM]
前言
之所以會去瞭解XPM,其一是這是我最早在一本與 C語言 相關書籍中看到解釋的圖片格式,基本上完全相融於C的語法,這意味著完全可以使用文字編輯器進行編輯。
同樣的PBM作爲一個可攜式的圖片格式,一樣有純文字版本。因此,特別去瞭解這兩個圖片格式。
Linux可能會對XPM檔案這麼描述:
X pixmap image, ASCII text
當然,他應該也有二進位格式吧….
XPM格式簡單說明
XPM正式全名為:X PixMap,自1989年發展起,歷經3個版本,第三個版本–XPM3完全相融於C語言的格式,這也是為什麼C語言書籍會拿它來說明,並當作事窗的icon範例程式。 不過又LAG於自己的電腦環境測試(LinuxMint 18.3),只有版本3能被作業系統正常識別(還有一點規則需求),版本1和版本2只會被認爲是純文本。不過3個版本都會略微提到。
Tag: 你所不知道的-c-語言
淺嘗「你所不知道的C語言」系列講座 技巧篇 筆記
今天把 你所不知道的 C 語言:技巧篇 (2019-07-11)看完了,簡單寫下一些筆記。
系列講座單元目錄:
以下內容可以在 https://ethercalc.org/dykc 找到。或是在系列講座直接查看個單元的簡介。有很多很少學校會教的技巧和概念,這次看的是技巧篇,其他內容我應該也會慢慢看完。(可能也會去找影片來看)
你所不知道的 C 語言 | |
---|---|
https://hackmd.io/s/HJpiYaZfl | 系列講座 |
https://hackmd.io/s/HJFyt37Mx | 為何要深入學習 C 語言 |
https://hackmd.io/s/HyBPr9WGl | 指標篇 |
https://hackmd.io/s/SJ6hRj-zg | 函式呼叫篇 |
https://hackmd.io/s/BkuMDQ9K7 | 記憶體管理、對齊及硬體特性 |
https://hackmd.io/s/rJ8BOjGGl | 遞迴呼叫篇 |
https://hackmd.io/s/B1e2AUZeM | goto 和流程控制篇 |
https://hackmd.io/s/BkRKhQGae | 數值系統篇 |
https://hackmd.io/s/Hy72937Me | 編譯器和最佳化原理篇 |
https://hackmd.io/s/H1ZzeiCIQ | C編譯器原理和案例分析 |
https://hackmd.io/s/HJLyQaQMl | 物件導向程式設計篇 |
https://hackmd.io/s/S1maxCXMl | 前置處理器應用篇 |
https://hackmd.io/s/HkK7Uf4Ml | 動態連結器和執行時期篇 |
https://hackmd.io/s/SysiUkgUV | 連結器和執行檔資訊 |
https://hackmd.io/s/Hkcr5cn97 | 執行階段程式庫 (CRT) |
https://hackmd.io/s/Skr9vGiQm | 未定義行為篇 |
https://hackmd.io/s/HyIdoLnjl | 技巧篇 |
https://hackmd.io/s/SkE33UTHf | linked list 和非連續記憶體操作 |
https://hackmd.io/s/B1s8hX1yg | 從打造類似 Facebook 網路服務探討整合開發 |
https://hackmd.io/@sysprog/Sy8pJ0x9G | Stream I/O, EOF 和例外處理 |
允許陣列結尾逗點
C語言流行的標準版本包含:C89、C99,和增加與C++相容性的C11/C18。我不清楚哪些是在哪個版本增加的,基本上下面的特性,在支援C11/C18的gcc編譯器都可以使用。C89或是C99可能會報錯。
看個簡單了程式,其中注意到變數a
是整數陣列,但是在初始化階段,最後是寫下,};
。,
後一個空元素,在早期C語言編輯器應該是不被允許的,不過在GCC可以正常編譯。
Tag: 修飾器
實現Python裏面的classmethod和staticmethod
在Python裏面存在著兩個用於定義物件方法的內建函式(built-in function) – classmethod
和staticmethod
。其實我一開始在學的時候根本沒用過他們(´・ω・`),先不管他們常不常用,雖然應該就是常用所以才會被放進內建函式,不過因爲常見的特殊語法–修飾器,使得隨後定義的方法與實際作用變得不同,常讓初學的人摸不著頭緒。今天就來以實現說說這兩個函式。
預備知識
首先你要先有基本的Python概念。接著最好已經很瞭解 修飾器 的作用。如果不瞭解修飾器,強烈建議先閱讀函式修飾器。 雖然本文在最後也爲實現一個相似於修飾器的函式版本 – [wrap](#修飾器的作用與實現 – wrap) ,不過其又涉及到其他知識,並不建議直接閱讀。
Check List
- 我瞭解最基本Python
- 我知道Python裏面修飾器的作用
實現classmethod和staticmethod – MyClassmethod, MyStaticmethod
在實現之前,必須先瞭解classmethod和staticmethod有甚麼特性?在使用後會發生什麼事情?
一般而言,在定義物件方法時會給與def methodName(self, *args, **kargs)
,其中self
表示物件實例本身。如果你瞭解 Lua 會發現是同樣的顯式引用發法,並且Lua中的語法糖instance:method(...)
(相當於class.method(instance, ...)
),幾乎就對應於Python裏面的Instance.method(*args, **kargs)
(相當於Class.method(Instance, *args, **kargs)
)。
相較之下,C++、Java裏面的this
,或是VB裏面的Me
,就是隱式用法。我自己是比較喜歡顯式引用,比較明確。
Tag: 圖片格式
圖片格式-[PBM]
前言
稍早前寫過一篇 圖片格式-[XPM] ,朋友們可以先去找來看看。這篇內容會比較短一點,部分東西會帶過去。
PBM格式簡單說明
PBM全名為: portable bitmap format,光是從字面意義上就可以知道,這是一個可攜的(portable)圖片格式。 與XPM相同,PBM支援存文本模式。不過比起只分版本,不如說PBM是一系列家族–PPM/PGM/PBM/PNM。不囉說,直接附上維基的表格:
檔案描述子 | 類型 | 編碼 | 副檔名 |
---|---|---|---|
P1 | 點陣圖 | ASCII | .pbm |
P2 | 灰度圖 | ASCII | .pgm |
P3 | 像素圖 | ASCII | .ppm |
P4 | 點陣圖 | 二進位 | .pbm |
P5 | 灰度圖 | 二進位 | .pgm |
P6 | 像素圖 | 二進位 | .ppm |
Linux可能會對pbm檔案這麼描述:
Netpbm image data, size = 33 x 32, bitmap, ASCII text
迷之音:“嘿,為什麼XPM就不給size了?”
二進位格式
Netpbm image data, size = 40 x 40, rawbits, bitmap
圖片格式-[XPM]
前言
之所以會去瞭解XPM,其一是這是我最早在一本與 C語言 相關書籍中看到解釋的圖片格式,基本上完全相融於C的語法,這意味著完全可以使用文字編輯器進行編輯。
同樣的PBM作爲一個可攜式的圖片格式,一樣有純文字版本。因此,特別去瞭解這兩個圖片格式。
Linux可能會對XPM檔案這麼描述:
X pixmap image, ASCII text
當然,他應該也有二進位格式吧….
XPM格式簡單說明
XPM正式全名為:X PixMap,自1989年發展起,歷經3個版本,第三個版本–XPM3完全相融於C語言的格式,這也是為什麼C語言書籍會拿它來說明,並當作事窗的icon範例程式。 不過又LAG於自己的電腦環境測試(LinuxMint 18.3),只有版本3能被作業系統正常識別(還有一點規則需求),版本1和版本2只會被認爲是純文本。不過3個版本都會略微提到。
Tag: 嵌入式程式語言
適合嵌入使用的程式語言 在Raspberry Pi上的編譯紀錄
幾乎所有應用都不可能只使用一個程式語言完成。
忘了是那看過的,現在想想,當時他所說的,或許不只是ABI、與C交互、或是組合語言,像是應用層面的HTML,或是通訊成面的HTTP等,應也可以視為程式語言裡的異語言。既然無可避免,那多少了解一些其他語言也是必須的(不過近期打算更進一步XD),不過我自己初衷很單純是「興趣」(原來是興趣使然的程式語言研究員阿)。
之前曾經找過適合作為嵌入式,嵌入到其他程式語言的腳本語言有哪些。
當然是受到最適合、也是以此為目標設計的Lua影響,但Lua及其簡單,就表達層面上,不適合作為複雜應用。
拿個簡單的例子來說,同為原形設計的物件導向程式語言–EMCAScript後來在ES6也有了class
的語法糖。
總之,Lua很簡單,執行效率也極高,這想點我想是相輔相成的,也沒比要為了語法糖提高其語言解析(parse)的難度。但同樣作為一個圖靈完全(Turing completeness)的程式語言,Lua必然也可以實現複雜應用。
但既然Lua已經幾乎達到其設計目標,那如果需要其他設計方法怎辦,作法之一是使用Python這類膠水語言(glue language,Lua也是),但不管是CPython、CRuby還是Node.js恐怕實現都有些過於「肥大」。繞個路的作法就是使用其他實現,接著帶大伙看看幾個我認為有潛力的語言實現。
嵌入式程式語言
Tag: 生活
【隨筆】參加11屆iT鐵人賽頒獎典禮
1月4號去參加iT體人賽頒獎典禮,一如往常的,拖到今天才來寫😹。
這是我第二次參加iT鐵人賽,上次報兩個主題只完賽一個。但這次…我報三個,好險都有完成……
但上次因為時空因素,我沒有參加頒獎典禮,所以儘管只有拿到參賽獎,但這是我第一次參加頒獎典禮。
(參加典禮的這幾天,一隻眼睛還有點腫腫的,不知道是參加CTF太燒腦,還是因為幾天前噎到吐出來,傷到鼻腔導致的。那幾天鼻子有血塊)
儘管只有參賽獎,但覺得這次頒獎典禮沒白來。有很多很多,原本沒想過,但有說到心裡的話。其中,「我們正在創造繁體中文技術文件的一個里程碑,一個歷史」這句尤感。
上台北
跟上次上台北比起來,心情輕快很多,截然不同。捷運站還看到一隻不知道是在流汗還是流口水的不明生物:
Tag: 看完不會變大師
【讀書筆記】你也可以撰寫Linux核心 從嵌入式系統切入
推薦指數:★☆☆☆☆
並不是說不推薦,只是不知道通用性有多高。本書主要是針對一個硬體平臺為標的,雖然利用了大量Linux核心的抽象,應該移植不會困難。但雖然我也沒有實際操作,但本書仍然可以看到Linux核心的設計和原則,其中我也進一步了解過去不清楚的部份。僅推薦給已經有一部分概念的人閱讀。那麼一樣,紀錄一下我認為值得紀錄的部份。(雖然這次不多,因為大部分段落沒完全看懂,感受不到很深刻)
還有,這篇筆記寫得蠻亂的XXDDD
【TED筆記】為什麼你應該要定義你的恐懼而非你的目標?
TED連結:Transcript of “Why you should define your fears instead of your goals”
看完後,最近我也在嘗試使用這樣的方式分析、協助自己做決策。有時候定義目標很難,那何不嘗試試定義恐懼?
定義恐懼
斯多葛主義(Stoicism)的一種衍伸,其幫助於自己專注於自己可以控制的要件,而不去擔心不可控制的部份所產生出的恐懼。
你能控制與你不能控制(專注於前者)
未知是恐懼的。未來充滿未知、充滿恐懼,但或許是想多了?
「我們在想像中所受的苦比在現實中還多」
“We suffer more often in imagination than in reality,”
—塞內卡(Seneca)
間單說就是:
【讀書筆記】學徒模式-優秀軟體開發者的養成之路
書名:
- 學徒模式-優秀軟體開發者的養成之路
- Apprenticeship Patterns(原文)
- 軟體開發者路線圖:從學徒到高手(陸譯)
讀完日期:2020/10/31
推薦指數:★★☆☆☆
這本書我是因為看到別人的書評筆記才去找來看的,其中最重要的幾點是因為有些與我想法相符,我想更了解作者還有什麼想法。這當中有許多想法或許與現在的主流思想不同(我認識一些求快而不懂原理的人),所以或許並不那麼值得推薦。但如果你是正在專研一項技術知識路上的旅人/學徒,這本絕對值得看看同為路上的人怎麼想。
【心得筆記】與熊共舞
與熊共舞,這本書的筆記你會發現跟我之前寫的筆記方式有些不同。我對本書有很多感觸,也因此我更想好好的整理。但總拖著,本書我六月份就看完了,也做過一些初步整理,直至今日才來寫寫心得筆記,撰寫之初亦沒有把握這筆記能夠寫得我滿意。不管怎說,這件事總覺得應該做、必須做,有很多東西,也只有做了才知道。
書名:與熊共舞
讀完日期:2020/06/13
推薦指數:★★★★★
(如果你已經帶過幾次軟體專案,我推薦爆表)
關於與熊共舞
在筆記之前,還是得來吹吹這本書的地位。「與熊共舞」和「人月神話」、「最後期限」並列為軟體專案管理,歷久不衰的必讀經典著作,如果你是軟體開發人員,我會額外推薦你閱讀Bob大叔的「Clean Code」。
儘管我聽過「與熊共舞」和「人月神話」好一段日子,但是這是我第一次讀三本中的其中一本。「最後期限」更是本書推薦後我才知道,亦非常期待之後閱讀。
そか~在看其他東西,都忘了我自己還訂了這兩本書單。
本書的副標題揭露的探討的內容:「軟體專案的風險管理」。但我認為其中提到的風險管理概念,不只可以用於軟體專案,對於任何形式的專案都具有幫助。
風險的定義
【閱讀筆記】深度學習演算法實踐
這本書以一個個假想的案例,帶讀者看看文字分析、對話機器人、臉部辨識、表情分析、強化學習、預測與推薦。內容不深,也有一些建議,可以是作一個個簡單的案例來看。
推薦指數:★★☆☆☆
閱讀日期:2020/05/28
【閱讀筆記】編寫可讀代碼的藝術
編寫可讀代碼的藝術(The Art of Readable Code) - 閱讀筆記
這本書有些地方是不合時宜的、我不太認同的,尤其是關於JS的部份,畢竟ES6後JS這個語言本身變化得非常多。我還是想引用Bob大叔在《Clean Code》的裡寫的:
當然,本書的許多建議是具爭議性的,你或許不會完同意這些意見,…… ,我們也無法主張自己的看法就是最終的權威。……。所以先不論你是否同意我們的想法,如果你根本沒有看到,或尊重我們的看法,你應該要感到羞愧。
– by Robert C. Martin(人稱Bob大叔(Uncle Bob))。Clean Code無暇的程式碼。
總之,儘管有些部份與自己作法略有不同,但不管是內容,還是內文漫畫(cartoon1真的話的很有趣)都有很大閱讀的價值,非常推薦!
推薦指數:★★★★☆
閱讀日期:2020/05/07
【閱讀筆記】軟體測試的藝術
《軟體測試的藝術》,寫過筆記的人也不少,但筆記畢竟主要是給自己看的,就還是紀錄一下自己認為值得反覆咀嚼的內容。值得意外的有點多,尤其是第六章節。很多我無法用一兩句話去記憶、帶過,自己尚未內化或許是原因之一,所以這篇筆記會很亂。總之,雖然剛開頭覺得有些無趣,但是強烈推薦去完整讀讀第六章節。
測試是為發現錯誤而執行程序的過程。
軟體測試更適宜被視為試圖發現程序中錯誤(假設其存在)的破壞性的過程。軟體做了其應該做的,未做其不應該做的。
推薦指數:★★★☆☆ 閱讀日期:2020/04/24
以下為筆記內容
第六章、更高級別的測試
軟體開發週期:
- 最終用戶轉換為書面需求(最終實現目標)(需求分析)
- 評估可行性與成本,消除相牴觸的用戶需求,建立優先級別和平衡關係,轉換為具體目標。(WPS、干特圖)(設計)
- 轉換為規格說明(外部格說明)
- 如果該產品是一個系統,接著就是系統設計。拆分成多的獨立的子程序、部件或子系統,並定義接口。
- 通過定義的每個模塊功能、模塊層次結構與接口,來設計程式或程式集合的結構。
- 設計一份 準確的規格說明 ,定義每個模塊的功能與接口。
- 經過一個或多個子步驟,將模塊接口規格說明轉換為每個模塊的原始碼。(開發)
- 需求規格 說明定義了為什麼要開發程序。
- 目標定義 了程序要做什麼,以及應做的怎樣。
- 外部規格說明 定義了程序對用戶的準確表現。
- 與後續階段相關的文檔越來越詳細地規定了程序是如何建立起來的。
測試方法、階段:
程式設計師應當避免測試自己編寫的程序。(除了單元測試外)
【閱讀筆記】代碼重構的藝術
目前在服義務役當兵,這本有點忘記啥時看完的了,印象中在入營前。總之,利用放假一點時間寫一下筆記。
喔!對了,我也看完「軟體測試的藝術」了。有時間再來寫。
六六三十六,數中有術,術中有數。
陰陽變理,機在其中。
機不可設,設則不中。
– 《三十六計》
推薦指數:★★★☆☆
本書分成三大部份:
- 第一部份:修改機理
- 第二部份:修改代碼的技術
- 第三部份:解依賴的技術
對我而言有幫助的在後兩個部份,但我也還在理解中。一個是個人經驗不足,有些內容沒有那麼深刻的體會。故如果你跟我一樣沒有多少重構經驗,看這本書可能也會像在霧裡看花。但我仍然覺得有些收穫,所以我會給2-3顆星。而如果你已經有一些重構經驗,但重構的有些痛苦,這本書或許會很適合你。
好的技術書籍一般有兩種情況,一種是介紹一些新奇而有趣的技術,另一種是能將現有的技術闡述或概括得通透淋漓。然而, 實際上還有第三種–既非介紹新奇的技術,也非闡述紀有的技術,而是將長期實踐所證明了的大量技術手法囊括在一起。看起來琳瑯滿目五花八門,但又各有各的用武之地。 這樣的書一般較少見,因為需要長期的累積和時間的洗禮。
本書正式這樣一本書。
– p.6 譯者序 節錄
【閱讀筆記】Common Lisp相關好文閱讀筆記
我對Common Lisp的喜愛應該不用多說。我不知道他還可以帶給我多少驚喜。
節錄幾個Common Lisp文章的相關敘述。
超凡脫俗的極限 - Common Lisp 文/田春
在文中最後寫:
原文: 超凡脫俗的極限 文/田春 鏈接已失效。
我閱讀位置:https://open343.github.io/Writing/zh-cmn-Hant/Overworldly-Common-Lisp.html
這應該是我第二次看,第一次看應該是在原本連結處。
語法
中序表達式可以徹底避免運算符優先級,例如 C 語言的表達式 1+23 在 Lisp 中將寫成 (+ 1 ( 2 3)) ,其中的 + 和 * 都是普通函數的名稱,和其他用戶定義的函數沒有區別。 值得注意的是,小括號的使用並不是必須,只是 Lisp 讀取器的一種標識,完全可以定製。如果用戶喜歡用中括號甚至後序表達式來描述 Lisp 程序,也是有可能的,相關的方法請查詢 Common Lisp 的 get-macro-character 和 set-macro-character 函數。
高度賦予程式成員自由性
Common Lisp 是唯一的允許程序員控制從源代碼到目標程序的所有方面的編程語言 。典型的 Lisp 代碼的處理分為三個階段:讀取、編譯、加載以及執行,其中每個階段都允許程序員介入。
- 在讀取階段,用戶可以設置特殊的讀取宏,用簡潔的形式讀取用戶自定義的對象;
- 在編譯階段,通過定義宏可以執行任意代碼來生成被編譯器所讀取的代碼;
- 在程序加載階段,附加的代碼有機會被執行,例如全局變量的初始化;
- 而在最終的程序執行階段,Lisp 系統還仍然有機會繼續編譯和加載程序的其餘部分,例如補丁,因為包括 compile 和 load 在內的函數是語言規範的一部分。
在讀取階段有set-macro-character
等讀取宏(read macro,我更喜歡使用原文。宏或巨集都不太能表達其強大)。
在編譯階段有defmacro
、define-compiler-macro
等可以使用。
[填坑筆記]kk音表解碼&國際拼音
kk音表解碼
課程影片:音標解碼:如何用26個英文字母來帶出所有的美式音標符號 (30分鐘必學課程)
筆記:https://postimg.cc/gallery/2rtdl5o5g/
想跟著寫一次,算是填上坑了
不過掃描後,黃色highlight變得好不明顯XD
國際拼音
【心得筆記】軟體品質相關文章閱讀心得
今天寫的筆記,都是公開文檔。我會推薦都去看看。有些原則不一定需要遵守,保留彈性。內容與我之前寫的筆記也有關,最好先完整閱讀一個,且最好是買本Bob大叔的clean code。
無瑕的程式碼 JavaScript
連結:https://hackmd.io/@trylovetom/SJnKIrajH
受到Bob大叔的clean code啟發,是clean code的js版本。除了提到一些原則外,另有提及一些JS生態使用的工具。筆記僅簡單紀錄紀個我感興趣的部份,推薦要寫JS的還是去完整閱讀。
注意!你不必嚴格遵守每一項原則,有些甚至不被大眾所認同。雖然這只是份指南,卻是來自《無暇的程式碼》作者的多年結晶。
軟體工程只發展了五十年,仍然有很多地方值得去探討。當軟體與建築一樣古老時,也許會有一些墨守成規的原則。但現在,先讓這份指南當試金石,作為你和團隊的 JS 程式碼標準。
還有一件事情:知道這些原則,並不會立刻讓你成為出色的開發者,長期奉行它們,不代表你能高枕無憂不再犯錯。但是,千里之行,始於足下,時常與志同道合們進行討論(Code Review),改善不完備之處。不要因為自己寫出來的程式碼很糟糕而害怕分享,而是要畏懼自己居然寫出了這樣的程式碼!
評估程式碼好壞的方法
透過計算閱讀程式碼時的咒罵次數,來評估軟體品質
這方法與「可不可以不要寫糙 code」有異曲同工之妙。
協助維持軟體品質的工具
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
【心得筆記】精通 Go 程式設計
簡評
怎麼說呢…看完當下的感覺是覺得,這本書很特別,我想這會許是因為這本書與Go語言設計者本身有關係。
Alan Donovan特别感謝:Sameer Ajmani、Chris Demetriou、Walt Drummond和Google公司的Reid Tatge允許他有充裕的時間去寫本書;
Alan A. A. Donovan 是作者之一,從致謝頁來看,同時也是Google員工。而Go語言,又是Google所開發的一個簡潔、高效的程式語言。Alan很可能參與了Go語言的設計與開發,也很可能是最早使用Go語言的一批人。這或許說明為何此種,跟閱讀「松本行弘的程序設計」之後的感覺,是相似的。推薦大家閱讀。
推薦指數:★★★★☆
【心得筆記】這幾天看完的文章 紀錄
紀錄並分享幾個,這幾天看完的系列文章。
iT體人賽,有蠻多很優質的文章,雖然有些東西還是要看時間讀,才比較有感覺。但總之,這次算是把之前先訂閱的系列看個一遍,今天做一下紀錄。
淺入淺出-計算機組織
系列連結:https://ithelp.ithome.com.tw/users/20091778/ironman/921
這篇文章應該是上Coursera的課程,那個北大的課程我也上過(其給予的連結我上去沒看到,這是另外找的),不過沒把問題做完,所以不算完課。雖然當時是準備考研究所去上的,最後也不算是考上我最主要目標的學校科系,但這門課老師說的真的很有趣,有興趣的可以去看看。
另外離散數學這門課也很好,雖然我有完課,但沒買證書。內容也有些忘記,之後可能會在挖出來複習一下。
整體而言,我會更推薦直接去聽課,課程中有動畫,解釋也比較清楚,但是作為基礎導覽或複習,這系列文章可以看看。
可不可以不要寫糙 code
https://ithelp.ithome.com.tw/users/20107637/ironman/1927
糙,注音:ㄘㄠˋ;拼音:cao。
意思是:不細緻的、草率、鹵莽(魯莽)。
特別注意:
- 這個詞與看 code 時發出的狀聲詞若發音相同,一定是巧合。
- 若這一系列介紹的範例 code ,讓你有一種親切感,也一定是巧合,因為文章並不會只是針對某些人,而指的是在座的各位…,沒啦!一定是巧合。
【心得筆記】設計模式:可覆用物件導向軟體基礎
1月9號把一本經典的書籍給看完了,這麼說其實也不太對,因為我幾乎略過所有程式相關的部份,至於原因待會會提到。個人並不是非常推薦這本書– 設計模式:可覆用物件導向軟體基礎 ,就連已經看過「深入淺出設計模式」的我,都認為其中有些描述有點難懂。不過,設計模式本就有其應用的環境(context),如過無法沈浸在其環境,就難裡理解為什麼使用這個模式。比較不好的是,有人反而為了使用模式,而用模式,沒有考慮到其環境是否適合。這本書我認為的價值,是可以釐清模式的價值,與一些被人捧上天的錯誤想法。
推薦指數:★★☆☆☆
(應該可以多給半顆拉!反正都是我的主觀判斷www)
【心得筆記】「松本行弘的程序世界」讀書筆記
這是一本「不只是Ruby的Ruby技術書籍」,要我在下另一個註腳的話,應該會是「從Ruby看計算機概論」。內容涵蓋之廣,物件導向程式設計、動態程式語言、原形程式設計(Propotype-base programming)、鴨子型別(算是一種設計模式)、強大的元編程(Meta Programming)、設計模式、有趣的猴子補丁、文字編碼、Ajax網頁技術、正則表達式(Rege)、平行處理(多工)、資訊安全、資料持久化(Serialization)、函數型程式設計(Functional Programming)。儘管只有快400頁,但可以從一本書裡窺看不同角度的技術世界。自己升大學以前,曾經在暑假看了至少三本計算機概論,好了解自己未來所選的科系,大學更是又讀了一次,在我看來,這本「松本行弘的程序世界」簡直就是技術界的計算機概論。對於只學過一種編程想法(如結構化程式設計或是物件導向程式設計)的人來說,或許會有許多地方看不懂,就當做個入門。不過,我自己到是看的頗為愉快。之前就有人說:Ruby的開發者是語言學家、Python的開發者是數學家。我看完本書後,松本行弘的視野真的很多角度、很廣。研究學習程式語言也是我的興趣之一,看在本書的當下,真覺得我和松大哥很投緣。
上面順序主要照書內出現的順序,下面稍微歸類整理一下:
- 程式語言設計思想
- 動態程式語言
- 物件導向程式設計(Object-oriented programming)
- 原形程式設計(Propotype-base programming)
- 函數型程式設計(Functional Programming)
- 開發設計思想
- 設計模式(Design Pattern)
- 鴨子型別(算是一種設計模式)
- 有趣的猴子補丁(Monkey Pitching)
- 強大的元編程(Meta Programming)
- 計算機概論常見內容
- 文字編碼
- 正則表達式(Rege)
- Ajax網頁技術
- 平行處理(多工)
- 資訊安全
- 資料持久化(Serialization)
這本書我在 2019/12/28 其實就看完了,不過跑去試了一些其他東西,到今天才來寫心得筆記。(看得當下其實就很想寫了🐰)
推薦指數:★★★★★
[讀書心得] 設計的心理學 小筆記
在讀這本書的時候,我腦內不斷的出現過去上UI/UX課程內容與以前看過的影片。雖然我確定是第一次看這本書,因為剛買時看書評說並不是非常好懂,就一直放著,真不知道放了多久。這回看下來,收穫頗多,加深了很多知識點。
內容短介
當代知名認知心理學先驅 唐納諾曼 的著作–設計的心理學。帶讀者瞭解生活上各種事物背後的設計與心理。人的行爲模式、心理與設計方法、流程。為何人會犯錯?是錯誤還是失誤?
「人為過失?錯了,是設計不良」透過欲設功能、意指、使用局限降低人犯錯的機會。 「五個為什麼」探討深層目的與原因1。 「一個產品開發的那一天,就已經進度落後」如何在商業環境與設計達到平衡?
推薦指數:★★★★☆
Tag: 程式筆記
[Common Lisp] 狀況(condition)處理快速筆記
Common Lisp的狀況系統(Condition System)遠比其他語言的錯誤處理來的強大,卻也不那麼容易理解。LAG也學習了不下三次,在重新翻閱「實用Common Lisp」之後,有更深程度的理解。今天先來快速的寫下重點筆記。
Tag: 筆記
【讀書筆記】你也可以撰寫Linux核心 從嵌入式系統切入
推薦指數:★☆☆☆☆
並不是說不推薦,只是不知道通用性有多高。本書主要是針對一個硬體平臺為標的,雖然利用了大量Linux核心的抽象,應該移植不會困難。但雖然我也沒有實際操作,但本書仍然可以看到Linux核心的設計和原則,其中我也進一步了解過去不清楚的部份。僅推薦給已經有一部分概念的人閱讀。那麼一樣,紀錄一下我認為值得紀錄的部份。(雖然這次不多,因為大部分段落沒完全看懂,感受不到很深刻)
還有,這篇筆記寫得蠻亂的XXDDD
【TED筆記】為什麼你應該要定義你的恐懼而非你的目標?
TED連結:Transcript of “Why you should define your fears instead of your goals”
看完後,最近我也在嘗試使用這樣的方式分析、協助自己做決策。有時候定義目標很難,那何不嘗試試定義恐懼?
定義恐懼
斯多葛主義(Stoicism)的一種衍伸,其幫助於自己專注於自己可以控制的要件,而不去擔心不可控制的部份所產生出的恐懼。
你能控制與你不能控制(專注於前者)
未知是恐懼的。未來充滿未知、充滿恐懼,但或許是想多了?
「我們在想像中所受的苦比在現實中還多」
“We suffer more often in imagination than in reality,”
—塞內卡(Seneca)
間單說就是:
【讀書筆記】學徒模式-優秀軟體開發者的養成之路
書名:
- 學徒模式-優秀軟體開發者的養成之路
- Apprenticeship Patterns(原文)
- 軟體開發者路線圖:從學徒到高手(陸譯)
讀完日期:2020/10/31
推薦指數:★★☆☆☆
這本書我是因為看到別人的書評筆記才去找來看的,其中最重要的幾點是因為有些與我想法相符,我想更了解作者還有什麼想法。這當中有許多想法或許與現在的主流思想不同(我認識一些求快而不懂原理的人),所以或許並不那麼值得推薦。但如果你是正在專研一項技術知識路上的旅人/學徒,這本絕對值得看看同為路上的人怎麼想。
【心得筆記】與熊共舞
與熊共舞,這本書的筆記你會發現跟我之前寫的筆記方式有些不同。我對本書有很多感觸,也因此我更想好好的整理。但總拖著,本書我六月份就看完了,也做過一些初步整理,直至今日才來寫寫心得筆記,撰寫之初亦沒有把握這筆記能夠寫得我滿意。不管怎說,這件事總覺得應該做、必須做,有很多東西,也只有做了才知道。
書名:與熊共舞
讀完日期:2020/06/13
推薦指數:★★★★★
(如果你已經帶過幾次軟體專案,我推薦爆表)
關於與熊共舞
在筆記之前,還是得來吹吹這本書的地位。「與熊共舞」和「人月神話」、「最後期限」並列為軟體專案管理,歷久不衰的必讀經典著作,如果你是軟體開發人員,我會額外推薦你閱讀Bob大叔的「Clean Code」。
儘管我聽過「與熊共舞」和「人月神話」好一段日子,但是這是我第一次讀三本中的其中一本。「最後期限」更是本書推薦後我才知道,亦非常期待之後閱讀。
そか~在看其他東西,都忘了我自己還訂了這兩本書單。
本書的副標題揭露的探討的內容:「軟體專案的風險管理」。但我認為其中提到的風險管理概念,不只可以用於軟體專案,對於任何形式的專案都具有幫助。
風險的定義
【閱讀筆記】深度學習演算法實踐
這本書以一個個假想的案例,帶讀者看看文字分析、對話機器人、臉部辨識、表情分析、強化學習、預測與推薦。內容不深,也有一些建議,可以是作一個個簡單的案例來看。
推薦指數:★★☆☆☆
閱讀日期:2020/05/28
【閱讀筆記】編寫可讀代碼的藝術
編寫可讀代碼的藝術(The Art of Readable Code) - 閱讀筆記
這本書有些地方是不合時宜的、我不太認同的,尤其是關於JS的部份,畢竟ES6後JS這個語言本身變化得非常多。我還是想引用Bob大叔在《Clean Code》的裡寫的:
當然,本書的許多建議是具爭議性的,你或許不會完同意這些意見,…… ,我們也無法主張自己的看法就是最終的權威。……。所以先不論你是否同意我們的想法,如果你根本沒有看到,或尊重我們的看法,你應該要感到羞愧。
– by Robert C. Martin(人稱Bob大叔(Uncle Bob))。Clean Code無暇的程式碼。
總之,儘管有些部份與自己作法略有不同,但不管是內容,還是內文漫畫(cartoon1真的話的很有趣)都有很大閱讀的價值,非常推薦!
推薦指數:★★★★☆
閱讀日期:2020/05/07
【閱讀筆記】軟體測試的藝術
《軟體測試的藝術》,寫過筆記的人也不少,但筆記畢竟主要是給自己看的,就還是紀錄一下自己認為值得反覆咀嚼的內容。值得意外的有點多,尤其是第六章節。很多我無法用一兩句話去記憶、帶過,自己尚未內化或許是原因之一,所以這篇筆記會很亂。總之,雖然剛開頭覺得有些無趣,但是強烈推薦去完整讀讀第六章節。
測試是為發現錯誤而執行程序的過程。
軟體測試更適宜被視為試圖發現程序中錯誤(假設其存在)的破壞性的過程。軟體做了其應該做的,未做其不應該做的。
推薦指數:★★★☆☆ 閱讀日期:2020/04/24
以下為筆記內容
第六章、更高級別的測試
軟體開發週期:
- 最終用戶轉換為書面需求(最終實現目標)(需求分析)
- 評估可行性與成本,消除相牴觸的用戶需求,建立優先級別和平衡關係,轉換為具體目標。(WPS、干特圖)(設計)
- 轉換為規格說明(外部格說明)
- 如果該產品是一個系統,接著就是系統設計。拆分成多的獨立的子程序、部件或子系統,並定義接口。
- 通過定義的每個模塊功能、模塊層次結構與接口,來設計程式或程式集合的結構。
- 設計一份 準確的規格說明 ,定義每個模塊的功能與接口。
- 經過一個或多個子步驟,將模塊接口規格說明轉換為每個模塊的原始碼。(開發)
- 需求規格 說明定義了為什麼要開發程序。
- 目標定義 了程序要做什麼,以及應做的怎樣。
- 外部規格說明 定義了程序對用戶的準確表現。
- 與後續階段相關的文檔越來越詳細地規定了程序是如何建立起來的。
測試方法、階段:
程式設計師應當避免測試自己編寫的程序。(除了單元測試外)
【閱讀筆記】代碼重構的藝術
目前在服義務役當兵,這本有點忘記啥時看完的了,印象中在入營前。總之,利用放假一點時間寫一下筆記。
喔!對了,我也看完「軟體測試的藝術」了。有時間再來寫。
六六三十六,數中有術,術中有數。
陰陽變理,機在其中。
機不可設,設則不中。
– 《三十六計》
推薦指數:★★★☆☆
本書分成三大部份:
- 第一部份:修改機理
- 第二部份:修改代碼的技術
- 第三部份:解依賴的技術
對我而言有幫助的在後兩個部份,但我也還在理解中。一個是個人經驗不足,有些內容沒有那麼深刻的體會。故如果你跟我一樣沒有多少重構經驗,看這本書可能也會像在霧裡看花。但我仍然覺得有些收穫,所以我會給2-3顆星。而如果你已經有一些重構經驗,但重構的有些痛苦,這本書或許會很適合你。
好的技術書籍一般有兩種情況,一種是介紹一些新奇而有趣的技術,另一種是能將現有的技術闡述或概括得通透淋漓。然而, 實際上還有第三種–既非介紹新奇的技術,也非闡述紀有的技術,而是將長期實踐所證明了的大量技術手法囊括在一起。看起來琳瑯滿目五花八門,但又各有各的用武之地。 這樣的書一般較少見,因為需要長期的累積和時間的洗禮。
本書正式這樣一本書。
– p.6 譯者序 節錄
【閱讀筆記】Common Lisp相關好文閱讀筆記
我對Common Lisp的喜愛應該不用多說。我不知道他還可以帶給我多少驚喜。
節錄幾個Common Lisp文章的相關敘述。
超凡脫俗的極限 - Common Lisp 文/田春
在文中最後寫:
原文: 超凡脫俗的極限 文/田春 鏈接已失效。
我閱讀位置:https://open343.github.io/Writing/zh-cmn-Hant/Overworldly-Common-Lisp.html
這應該是我第二次看,第一次看應該是在原本連結處。
語法
中序表達式可以徹底避免運算符優先級,例如 C 語言的表達式 1+23 在 Lisp 中將寫成 (+ 1 ( 2 3)) ,其中的 + 和 * 都是普通函數的名稱,和其他用戶定義的函數沒有區別。 值得注意的是,小括號的使用並不是必須,只是 Lisp 讀取器的一種標識,完全可以定製。如果用戶喜歡用中括號甚至後序表達式來描述 Lisp 程序,也是有可能的,相關的方法請查詢 Common Lisp 的 get-macro-character 和 set-macro-character 函數。
高度賦予程式成員自由性
Common Lisp 是唯一的允許程序員控制從源代碼到目標程序的所有方面的編程語言 。典型的 Lisp 代碼的處理分為三個階段:讀取、編譯、加載以及執行,其中每個階段都允許程序員介入。
- 在讀取階段,用戶可以設置特殊的讀取宏,用簡潔的形式讀取用戶自定義的對象;
- 在編譯階段,通過定義宏可以執行任意代碼來生成被編譯器所讀取的代碼;
- 在程序加載階段,附加的代碼有機會被執行,例如全局變量的初始化;
- 而在最終的程序執行階段,Lisp 系統還仍然有機會繼續編譯和加載程序的其餘部分,例如補丁,因為包括 compile 和 load 在內的函數是語言規範的一部分。
在讀取階段有set-macro-character
等讀取宏(read macro,我更喜歡使用原文。宏或巨集都不太能表達其強大)。
在編譯階段有defmacro
、define-compiler-macro
等可以使用。
[填坑筆記]kk音表解碼&國際拼音
kk音表解碼
課程影片:音標解碼:如何用26個英文字母來帶出所有的美式音標符號 (30分鐘必學課程)
筆記:https://postimg.cc/gallery/2rtdl5o5g/
想跟著寫一次,算是填上坑了
不過掃描後,黃色highlight變得好不明顯XD
國際拼音
【心得筆記】軟體品質相關文章閱讀心得
今天寫的筆記,都是公開文檔。我會推薦都去看看。有些原則不一定需要遵守,保留彈性。內容與我之前寫的筆記也有關,最好先完整閱讀一個,且最好是買本Bob大叔的clean code。
無瑕的程式碼 JavaScript
連結:https://hackmd.io/@trylovetom/SJnKIrajH
受到Bob大叔的clean code啟發,是clean code的js版本。除了提到一些原則外,另有提及一些JS生態使用的工具。筆記僅簡單紀錄紀個我感興趣的部份,推薦要寫JS的還是去完整閱讀。
注意!你不必嚴格遵守每一項原則,有些甚至不被大眾所認同。雖然這只是份指南,卻是來自《無暇的程式碼》作者的多年結晶。
軟體工程只發展了五十年,仍然有很多地方值得去探討。當軟體與建築一樣古老時,也許會有一些墨守成規的原則。但現在,先讓這份指南當試金石,作為你和團隊的 JS 程式碼標準。
還有一件事情:知道這些原則,並不會立刻讓你成為出色的開發者,長期奉行它們,不代表你能高枕無憂不再犯錯。但是,千里之行,始於足下,時常與志同道合們進行討論(Code Review),改善不完備之處。不要因為自己寫出來的程式碼很糟糕而害怕分享,而是要畏懼自己居然寫出了這樣的程式碼!
評估程式碼好壞的方法
透過計算閱讀程式碼時的咒罵次數,來評估軟體品質
這方法與「可不可以不要寫糙 code」有異曲同工之妙。
協助維持軟體品質的工具
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
【心得筆記】精通 Go 程式設計
簡評
怎麼說呢…看完當下的感覺是覺得,這本書很特別,我想這會許是因為這本書與Go語言設計者本身有關係。
Alan Donovan特别感謝:Sameer Ajmani、Chris Demetriou、Walt Drummond和Google公司的Reid Tatge允許他有充裕的時間去寫本書;
Alan A. A. Donovan 是作者之一,從致謝頁來看,同時也是Google員工。而Go語言,又是Google所開發的一個簡潔、高效的程式語言。Alan很可能參與了Go語言的設計與開發,也很可能是最早使用Go語言的一批人。這或許說明為何此種,跟閱讀「松本行弘的程序設計」之後的感覺,是相似的。推薦大家閱讀。
推薦指數:★★★★☆
【心得筆記】這幾天看完的文章 紀錄
紀錄並分享幾個,這幾天看完的系列文章。
iT體人賽,有蠻多很優質的文章,雖然有些東西還是要看時間讀,才比較有感覺。但總之,這次算是把之前先訂閱的系列看個一遍,今天做一下紀錄。
淺入淺出-計算機組織
系列連結:https://ithelp.ithome.com.tw/users/20091778/ironman/921
這篇文章應該是上Coursera的課程,那個北大的課程我也上過(其給予的連結我上去沒看到,這是另外找的),不過沒把問題做完,所以不算完課。雖然當時是準備考研究所去上的,最後也不算是考上我最主要目標的學校科系,但這門課老師說的真的很有趣,有興趣的可以去看看。
另外離散數學這門課也很好,雖然我有完課,但沒買證書。內容也有些忘記,之後可能會在挖出來複習一下。
整體而言,我會更推薦直接去聽課,課程中有動畫,解釋也比較清楚,但是作為基礎導覽或複習,這系列文章可以看看。
可不可以不要寫糙 code
https://ithelp.ithome.com.tw/users/20107637/ironman/1927
糙,注音:ㄘㄠˋ;拼音:cao。
意思是:不細緻的、草率、鹵莽(魯莽)。
特別注意:
- 這個詞與看 code 時發出的狀聲詞若發音相同,一定是巧合。
- 若這一系列介紹的範例 code ,讓你有一種親切感,也一定是巧合,因為文章並不會只是針對某些人,而指的是在座的各位…,沒啦!一定是巧合。
【心得筆記】設計模式:可覆用物件導向軟體基礎
1月9號把一本經典的書籍給看完了,這麼說其實也不太對,因為我幾乎略過所有程式相關的部份,至於原因待會會提到。個人並不是非常推薦這本書– 設計模式:可覆用物件導向軟體基礎 ,就連已經看過「深入淺出設計模式」的我,都認為其中有些描述有點難懂。不過,設計模式本就有其應用的環境(context),如過無法沈浸在其環境,就難裡理解為什麼使用這個模式。比較不好的是,有人反而為了使用模式,而用模式,沒有考慮到其環境是否適合。這本書我認為的價值,是可以釐清模式的價值,與一些被人捧上天的錯誤想法。
推薦指數:★★☆☆☆
(應該可以多給半顆拉!反正都是我的主觀判斷www)
【心得筆記】「松本行弘的程序世界」讀書筆記
這是一本「不只是Ruby的Ruby技術書籍」,要我在下另一個註腳的話,應該會是「從Ruby看計算機概論」。內容涵蓋之廣,物件導向程式設計、動態程式語言、原形程式設計(Propotype-base programming)、鴨子型別(算是一種設計模式)、強大的元編程(Meta Programming)、設計模式、有趣的猴子補丁、文字編碼、Ajax網頁技術、正則表達式(Rege)、平行處理(多工)、資訊安全、資料持久化(Serialization)、函數型程式設計(Functional Programming)。儘管只有快400頁,但可以從一本書裡窺看不同角度的技術世界。自己升大學以前,曾經在暑假看了至少三本計算機概論,好了解自己未來所選的科系,大學更是又讀了一次,在我看來,這本「松本行弘的程序世界」簡直就是技術界的計算機概論。對於只學過一種編程想法(如結構化程式設計或是物件導向程式設計)的人來說,或許會有許多地方看不懂,就當做個入門。不過,我自己到是看的頗為愉快。之前就有人說:Ruby的開發者是語言學家、Python的開發者是數學家。我看完本書後,松本行弘的視野真的很多角度、很廣。研究學習程式語言也是我的興趣之一,看在本書的當下,真覺得我和松大哥很投緣。
上面順序主要照書內出現的順序,下面稍微歸類整理一下:
- 程式語言設計思想
- 動態程式語言
- 物件導向程式設計(Object-oriented programming)
- 原形程式設計(Propotype-base programming)
- 函數型程式設計(Functional Programming)
- 開發設計思想
- 設計模式(Design Pattern)
- 鴨子型別(算是一種設計模式)
- 有趣的猴子補丁(Monkey Pitching)
- 強大的元編程(Meta Programming)
- 計算機概論常見內容
- 文字編碼
- 正則表達式(Rege)
- Ajax網頁技術
- 平行處理(多工)
- 資訊安全
- 資料持久化(Serialization)
這本書我在 2019/12/28 其實就看完了,不過跑去試了一些其他東西,到今天才來寫心得筆記。(看得當下其實就很想寫了🐰)
推薦指數:★★★★★
淺嘗「你所不知道的C語言」系列講座 技巧篇 筆記
今天把 你所不知道的 C 語言:技巧篇 (2019-07-11)看完了,簡單寫下一些筆記。
系列講座單元目錄:
以下內容可以在 https://ethercalc.org/dykc 找到。或是在系列講座直接查看個單元的簡介。有很多很少學校會教的技巧和概念,這次看的是技巧篇,其他內容我應該也會慢慢看完。(可能也會去找影片來看)
你所不知道的 C 語言 | |
---|---|
https://hackmd.io/s/HJpiYaZfl | 系列講座 |
https://hackmd.io/s/HJFyt37Mx | 為何要深入學習 C 語言 |
https://hackmd.io/s/HyBPr9WGl | 指標篇 |
https://hackmd.io/s/SJ6hRj-zg | 函式呼叫篇 |
https://hackmd.io/s/BkuMDQ9K7 | 記憶體管理、對齊及硬體特性 |
https://hackmd.io/s/rJ8BOjGGl | 遞迴呼叫篇 |
https://hackmd.io/s/B1e2AUZeM | goto 和流程控制篇 |
https://hackmd.io/s/BkRKhQGae | 數值系統篇 |
https://hackmd.io/s/Hy72937Me | 編譯器和最佳化原理篇 |
https://hackmd.io/s/H1ZzeiCIQ | C編譯器原理和案例分析 |
https://hackmd.io/s/HJLyQaQMl | 物件導向程式設計篇 |
https://hackmd.io/s/S1maxCXMl | 前置處理器應用篇 |
https://hackmd.io/s/HkK7Uf4Ml | 動態連結器和執行時期篇 |
https://hackmd.io/s/SysiUkgUV | 連結器和執行檔資訊 |
https://hackmd.io/s/Hkcr5cn97 | 執行階段程式庫 (CRT) |
https://hackmd.io/s/Skr9vGiQm | 未定義行為篇 |
https://hackmd.io/s/HyIdoLnjl | 技巧篇 |
https://hackmd.io/s/SkE33UTHf | linked list 和非連續記憶體操作 |
https://hackmd.io/s/B1s8hX1yg | 從打造類似 Facebook 網路服務探討整合開發 |
https://hackmd.io/@sysprog/Sy8pJ0x9G | Stream I/O, EOF 和例外處理 |
允許陣列結尾逗點
C語言流行的標準版本包含:C89、C99,和增加與C++相容性的C11/C18。我不清楚哪些是在哪個版本增加的,基本上下面的特性,在支援C11/C18的gcc編譯器都可以使用。C89或是C99可能會報錯。
看個簡單了程式,其中注意到變數a
是整數陣列,但是在初始化階段,最後是寫下,};
。,
後一個空元素,在早期C語言編輯器應該是不被允許的,不過在GCC可以正常編譯。
Tag: 蒙地卡羅馬可夫
Tag: 讀書心得
【心得筆記】Emacs、Rust、Kotlin成就取得
除了Golang,上週也把Emacs相關的、Rust、Kotlin等給看完取得成就~
就來簡短紀錄一下。
學習GNU Emacs
推薦指數:★☆☆☆☆
讀完日期:2020/02/12
想要學習Gnu Emacs,看一下內建的教程就好。這本我只是略讀,感覺對我幫助不大。總之如果你想學Gnu Emacs,不用讀這本,應該這樣做:
- 下載Gnu Emacs
- M-x help-with-tutorial-spec-language
- 選擇繁體中文
- 然後閱讀它
※ 如果你有開上方的menu-bar,也可以直接選擇help->Emacs Tutorial(C-h t)。
GNU Emacs Lisp編程入門
推薦指數:★☆☆☆☆
讀完日期:2020/02/13
如果你有寫elisp的需求,這本書可以略過,但有的話,可以看下。Emacs Lisp API文檔並不那麼好入門,這個可以帶你入一點點門(幫助有限就是)。
上面兩本書我看的都是比較舊的了,也都是略讀。這本裡面提到的技術手冊我沒找著。
Kotlin語言文檔(v1.3)
推薦指數:★★★☆☆
讀完日期:2020/02/13
【心得筆記】精通 Go 程式設計
簡評
怎麼說呢…看完當下的感覺是覺得,這本書很特別,我想這會許是因為這本書與Go語言設計者本身有關係。
Alan Donovan特别感謝:Sameer Ajmani、Chris Demetriou、Walt Drummond和Google公司的Reid Tatge允許他有充裕的時間去寫本書;
Alan A. A. Donovan 是作者之一,從致謝頁來看,同時也是Google員工。而Go語言,又是Google所開發的一個簡潔、高效的程式語言。Alan很可能參與了Go語言的設計與開發,也很可能是最早使用Go語言的一批人。這或許說明為何此種,跟閱讀「松本行弘的程序設計」之後的感覺,是相似的。推薦大家閱讀。
推薦指數:★★★★☆
【心得筆記】設計模式:可覆用物件導向軟體基礎
1月9號把一本經典的書籍給看完了,這麼說其實也不太對,因為我幾乎略過所有程式相關的部份,至於原因待會會提到。個人並不是非常推薦這本書– 設計模式:可覆用物件導向軟體基礎 ,就連已經看過「深入淺出設計模式」的我,都認為其中有些描述有點難懂。不過,設計模式本就有其應用的環境(context),如過無法沈浸在其環境,就難裡理解為什麼使用這個模式。比較不好的是,有人反而為了使用模式,而用模式,沒有考慮到其環境是否適合。這本書我認為的價值,是可以釐清模式的價值,與一些被人捧上天的錯誤想法。
推薦指數:★★☆☆☆
(應該可以多給半顆拉!反正都是我的主觀判斷www)
【心得筆記】「松本行弘的程序世界」讀書筆記
這是一本「不只是Ruby的Ruby技術書籍」,要我在下另一個註腳的話,應該會是「從Ruby看計算機概論」。內容涵蓋之廣,物件導向程式設計、動態程式語言、原形程式設計(Propotype-base programming)、鴨子型別(算是一種設計模式)、強大的元編程(Meta Programming)、設計模式、有趣的猴子補丁、文字編碼、Ajax網頁技術、正則表達式(Rege)、平行處理(多工)、資訊安全、資料持久化(Serialization)、函數型程式設計(Functional Programming)。儘管只有快400頁,但可以從一本書裡窺看不同角度的技術世界。自己升大學以前,曾經在暑假看了至少三本計算機概論,好了解自己未來所選的科系,大學更是又讀了一次,在我看來,這本「松本行弘的程序世界」簡直就是技術界的計算機概論。對於只學過一種編程想法(如結構化程式設計或是物件導向程式設計)的人來說,或許會有許多地方看不懂,就當做個入門。不過,我自己到是看的頗為愉快。之前就有人說:Ruby的開發者是語言學家、Python的開發者是數學家。我看完本書後,松本行弘的視野真的很多角度、很廣。研究學習程式語言也是我的興趣之一,看在本書的當下,真覺得我和松大哥很投緣。
上面順序主要照書內出現的順序,下面稍微歸類整理一下:
- 程式語言設計思想
- 動態程式語言
- 物件導向程式設計(Object-oriented programming)
- 原形程式設計(Propotype-base programming)
- 函數型程式設計(Functional Programming)
- 開發設計思想
- 設計模式(Design Pattern)
- 鴨子型別(算是一種設計模式)
- 有趣的猴子補丁(Monkey Pitching)
- 強大的元編程(Meta Programming)
- 計算機概論常見內容
- 文字編碼
- 正則表達式(Rege)
- Ajax網頁技術
- 平行處理(多工)
- 資訊安全
- 資料持久化(Serialization)
這本書我在 2019/12/28 其實就看完了,不過跑去試了一些其他東西,到今天才來寫心得筆記。(看得當下其實就很想寫了🐰)
推薦指數:★★★★★
[讀書心得] 設計的心理學 小筆記
在讀這本書的時候,我腦內不斷的出現過去上UI/UX課程內容與以前看過的影片。雖然我確定是第一次看這本書,因為剛買時看書評說並不是非常好懂,就一直放著,真不知道放了多久。這回看下來,收穫頗多,加深了很多知識點。
內容短介
當代知名認知心理學先驅 唐納諾曼 的著作–設計的心理學。帶讀者瞭解生活上各種事物背後的設計與心理。人的行爲模式、心理與設計方法、流程。為何人會犯錯?是錯誤還是失誤?
「人為過失?錯了,是設計不良」透過欲設功能、意指、使用局限降低人犯錯的機會。 「五個為什麼」探討深層目的與原因1。 「一個產品開發的那一天,就已經進度落後」如何在商業環境與設計達到平衡?
推薦指數:★★★★☆
Tag: 輪廓標記
Tag: 錯誤處理
[Common Lisp] 狀況(condition)處理快速筆記
Common Lisp的狀況系統(Condition System)遠比其他語言的錯誤處理來的強大,卻也不那麼容易理解。LAG也學習了不下三次,在重新翻閱「實用Common Lisp」之後,有更深程度的理解。今天先來快速的寫下重點筆記。