細(xì)心的朋友們應(yīng)該會(huì)注意到,在布局文件的<ViewFlipper/>標(biāo)簽中有這么兩個(gè)屬性:android:inAnimation="@anim/in_from_right"android:outAnimation="@anim/out_from_left"根據(jù) 2.1 小節(jié)對(duì)屬性的介紹,我們知道這是用來(lái)設(shè)置出場(chǎng)和入場(chǎng)動(dòng)畫的,所以接下來(lái)我們就來(lái)完善這兩個(gè)動(dòng)畫效果,首先按照以下步驟創(chuàng)建一個(gè)動(dòng)畫資源:右鍵點(diǎn)擊“res”目錄,一次選擇“New” -> “Android Resource Directory”在“Resource type”中選擇“anim”,點(diǎn)“OK”,創(chuàng)建一個(gè)動(dòng)畫資源目錄此時(shí)“res”目錄下就有了“anim”文件夾,在里面創(chuàng)建四個(gè)動(dòng)畫文件:in_from_left.xml:表示從左側(cè)進(jìn)入的動(dòng)畫,代碼如下:<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:duration="1400" android:fromXDelta="-100%" android:toXDelta="0%"/></set>其中<translate/>標(biāo)簽表示一個(gè) TranslateAnimation,專門用來(lái)實(shí)現(xiàn)移動(dòng)補(bǔ)間動(dòng)畫,具體關(guān)于動(dòng)畫的用法在后面的動(dòng)畫專題章節(jié)有詳細(xì)的講解,這里我們關(guān)注 ViewFlipper,對(duì)動(dòng)畫只需要做一點(diǎn)了解即可。其余三個(gè)動(dòng)畫都類似:**in_from_right.xml:**表示從右側(cè)進(jìn)入,代碼如下:<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:duration="1400" android:fromXDelta="100%" android:fromYDelta="0%" /></set>**out_from_left:**從左側(cè)移出:<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:duration="1400" android:fromXDelta="0%" android:toXDelta="-100%"/></set>**out_from_right:**從右側(cè)移出:<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> <translate android:duration="1400" android:fromXDelta="0%" android:fromYDelta="0%" android:toXDelta="100%" android:toYDelta="0%" /></set>
發(fā)布項(xiàng)目,啟動(dòng)服務(wù)器,打開(kāi)瀏覽器,在地址欄中輸入:http://localhost:8888/sm-demo/student/list ??梢栽跒g覽器中看到。
比如 Chrome、IOS 10 已經(jīng)陸續(xù)禁止,需要升級(jí)站點(diǎn)到 HTTPS,error 信息的 message 字段包含Geolocation permission denied信息。注意: Chrome 不會(huì)禁止 localhost 域名 HTTP 協(xié)議下的定位
公開(kāi)密鑰基礎(chǔ)建設(shè) PKI(Public Key Infrastructure)是一組由硬件、軟件、參與者、管理政策與流程組成的基礎(chǔ)設(shè)施,是為了保障網(wǎng)絡(luò)系統(tǒng)的安全而產(chǎn)生的機(jī)構(gòu)。PKI 里面包含了多個(gè)職能機(jī)構(gòu),其中 CA 是證書(shū)機(jī)構(gòu),負(fù)責(zé)管理證書(shū)的整個(gè)生命周期,它是 PKI 的核心組成。在當(dāng)前在線交易盛行的今天,網(wǎng)絡(luò)安全尤為重要。
在瀏覽器中輸入:http://localhost:5000/page1?user=zhangsan, URL 中的查詢參數(shù) user=zhangsan 表示已經(jīng)登錄,顯示如下:
基于 Node 環(huán)境來(lái)安裝 Swagger Editor ,是 Swagger Editor 官方推薦使用的第一種方式,通過(guò) Node 來(lái)安裝 Swagger Editor ,無(wú)論是 Windows 系統(tǒng)還是 OS X 系統(tǒng),都要求電腦中首先要有 Node ,如果你的電腦中還沒(méi)有 Node 環(huán)境,那么請(qǐng)先安裝 Node 。Node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行時(shí)環(huán)境,同時(shí)提供了前臺(tái)界面和后臺(tái)服務(wù)的支持,而 Swagger Editor 的運(yùn)行首先需要一個(gè) http 服務(wù)器,所以在正式安裝 Swagger Editor 時(shí),我們首先需要安裝一個(gè) http 服務(wù)器。
布局文件非常簡(jiǎn)單,核心就是一個(gè) ImageView,用來(lái)承載我們縮放的目標(biāo)圖片。需要注意的是,這里要將圖片的 scaleType設(shè)置成“matrix”,用于后續(xù)做縮放:<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp" tools:context=".MainActivity"> <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:text="手勢(shì)處理示例" android:textSize="35sp" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/textview" android:layout_centerHorizontal="true" android:text="慕課 Android 教程" android:textColor="#ff7aff24" android:textSize="35dp" /> <ImageView android:id="@+id/imageView" android:layout_width="40dp" android:layout_height="40dp" android:layout_below="@+id/textView" android:layout_alignParentStart="true" android:layout_alignParentEnd="true" android:layout_alignParentBottom="true" android:scaleType="matrix" android:src="@drawable/avatar" /></RelativeLayout>
從 HttpRequest 中獲取參數(shù)是我們進(jìn)行 Web 開(kāi)發(fā)中最常用的一種方式。對(duì)于 Django 的視圖函數(shù)來(lái)說(shuō),HTTP 請(qǐng)求的數(shù)據(jù)被 HttpRequest 實(shí)例化后傳到了視圖函數(shù)的第一個(gè)參數(shù)中。為了能觀察相關(guān)信息,我們修改請(qǐng)求的視圖函數(shù):@csrf_exemptdef hello_view(request, *args, **kwargs): # 在第三次使用表單上傳包括文件數(shù)據(jù)時(shí),需要request.GET和request.POST操作,不然會(huì)拋異常 params = "request.GET={}\n".format(request.GET) params += "request.POST={}\n".format(request.POST) params += "request.body={}\n".format(request.body) params += "request.FILES={}\n".format(request.FILES) return HttpResponse(params)我們測(cè)試如下 3 種 HTTP 請(qǐng)求,分別為 GET 請(qǐng)求、POST 請(qǐng)求 和帶文件參數(shù)的請(qǐng)求,結(jié)果如下:[root@server ~]# curl -XGET "http://127.0.0.1:8881/hello/world/?a=xxxx&b=yyyy" request.GET=<QueryDict: {'a': ['xxxx'], 'b': ['yyyy']}>request.POST=<QueryDict: {}>request.body=b''request.FILES=<MultiValueDict: {}>[root@server ~]# curl -XPOST -d "username=shen&password=shentong" "http://127.0.0.1:8881/hello/world/?a=xxxx&b=yyyy" request.GET=<QueryDict: {'a': ['xxxx'], 'b': ['yyyy']}>request.POST=<QueryDict: {'username': ['shen'], 'password': ['shentong']}>request.body=b'username=shen&password=shentong'request.FILES=<MultiValueDict: {}># 本次請(qǐng)求中,需要去掉request.GET和request.POST操作語(yǔ)句,不然請(qǐng)求會(huì)報(bào)錯(cuò)[root@server ~]# curl -XPOST -F "username=shen&password=shentong" "http://127.0.0.1:8881/hello/world/?a=xxxx&b=yyyy" -F "files=@/root/upload_file.txt"request.body=b'------------------------------68c9ede00e93\r\nContent-Disposition: form-data; name="username"\r\n\r\nshen&password=shentong\r\n------------------------------68c9ede00e93\r\nContent-Disposition: form-data; name="files"; filename="upload_file.txt"\r\nContent-Type: text/plain\r\n\r\nupload file test\n\r\n------------------------------68c9ede00e93--\r\n'request.FILES=<MultiValueDict: {'files': [<InMemoryUploadedFile: upload_file.txt (text/plain)>]}>可以看到,跟在 “?” 后的參數(shù)數(shù)據(jù)會(huì)保存到 request.GET 中,這也是 GET 請(qǐng)求帶參數(shù)的方式。對(duì)于 POST 請(qǐng)求的傳參,數(shù)據(jù)一般會(huì)保存在 request.POST 和 request.body 中。對(duì)于最后發(fā)送的上傳文件請(qǐng)求,可以看到,文件內(nèi)容的內(nèi)容數(shù)據(jù)是保存到了 request.body 中。
在數(shù)據(jù)交互中,我們經(jīng)常會(huì)使用 GET 請(qǐng)求來(lái)查詢數(shù)據(jù),現(xiàn)在假設(shè)我們有一個(gè)簡(jiǎn)單的GET請(qǐng)求,查詢接口 http://localhost:8080/simple/get,附帶 query參數(shù) 為 mk=慕課網(wǎng),那么我們可以構(gòu)建代碼塊:xhr.open("GET", "http://localhost:8080/simple/get?mk=慕課網(wǎng)");xhr.send();查看效果圖:從上圖可以看到,瀏覽器控制臺(tái)面板上顯示,我們進(jìn)行 GET 發(fā)送請(qǐng)求的過(guò)程中,在 Headers 上,可以看到 Query String Parameters 附帶的信息完全正確,我們的 GET 請(qǐng)求構(gòu)造成功。
Tornado 主要分成四個(gè)部分:Web 框架(包括 RequestHandler,用于創(chuàng)建Web程序的基類,以及各種支持類);實(shí)現(xiàn) HTTP 的客戶端和服務(wù)器端 (HTTPServer 和 AsyncHTTPClient);一個(gè)異步網(wǎng)絡(luò)庫(kù) (IOLoop 和 IOStream);一個(gè)協(xié)程庫(kù)(tornado.gen) ,使得異步調(diào)用代碼能夠以更直接的方式書(shū)寫,取代回調(diào)鏈接。在 Pycharm 里創(chuàng)建一個(gè)Python 項(xiàng)目,然后創(chuàng)建新文件添加下面的代碼,就創(chuàng)建了一個(gè)最簡(jiǎn)單的服務(wù)。import tornado.ioloopimport tornado.webclass MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world")application = tornado.web.Application([ (r"/", MainHandler),])if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()直接運(yùn)行上面的代碼, 在瀏覽器輸入 http://localhost:8888 , 就會(huì)看到 “Hello, world” 顯示在頁(yè)面上。
pip 工具會(huì)從網(wǎng)站自動(dòng)下載 Python 的第三方模塊,提供下載 Python 第三方模塊的網(wǎng)站被稱為源。默認(rèn)情況下,pip 從國(guó)外網(wǎng)站下載 Python 的第三方模塊,速度非常的慢。為了加快下載速度,可以將 pip 的源改為國(guó)內(nèi)的鏡像源。國(guó)內(nèi)常用的源如下:源的提供方源的 URL阿里云https://mirrors.aliyun.com/pypi/simple清華https://pypi.tuna.tsinghua.edu.cn/simple中國(guó)科技大學(xué)https://pypi.mirrors.ustc.edu.cn/simple華中理工大學(xué)http://pypi.hustunique.com豆瓣http://pypi.douban.com/simple
屬性定義圖像為服務(wù)器端圖片映射,就是定義圖片中可以點(diǎn)擊的區(qū)域,并且將對(duì)標(biāo)發(fā)送到服務(wù)器,需要配合 a 標(biāo)簽使用。972上述代碼,當(dāng)用戶點(diǎn)擊圖片時(shí),瀏覽器會(huì)將點(diǎn)擊的坐標(biāo)點(diǎn)以 x,y 作為參數(shù),以 GET 的方式請(qǐng)求服務(wù)器,例如當(dāng)用戶點(diǎn)擊圖片的坐標(biāo)點(diǎn)(8,8)時(shí),服務(wù)器會(huì)受到一條 HTTP 請(qǐng)求 http://www.baidu.com?8,8,此坐標(biāo)點(diǎn)是相對(duì)定位坐標(biāo)點(diǎn)。
以 nginx 為例,使用如下命令下載 nginx 的 rpm 包:wget http://nginx.org/packages/centos/8/x86_64/RPMS/nginx-1.18.0-1.el8.ngx.x86_64.rpm執(zhí)行結(jié)果如下圖所示:
有了中央倉(cāng)庫(kù),我們?yōu)槭裁催€需要其他的遠(yuǎn)程倉(cāng)庫(kù)呢?我們要找的構(gòu)件可能不存在于中央倉(cāng)庫(kù)中;由于某些原因,訪問(wèn)中央倉(cāng)庫(kù)的速度相對(duì)較慢。這種時(shí)候,我們就可以選擇一個(gè)使用起來(lái)相對(duì)方便的遠(yuǎn)程倉(cāng)庫(kù)來(lái)配置,大大提高了我們的開(kāi)發(fā)效率。國(guó)內(nèi)常用的 Maven 倉(cāng)庫(kù):阿里云鏡像:<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf></mirror>阿里巴巴鏡像:<mirror> <id>ibiblio</id> <mirrorOf>central</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://mirrors.ibiblio.org/pub/mirrors/maven2/</url></mirror>repo2 鏡像:<mirror> <id>repo2</id> <mirrorOf>central</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://repo2.maven.org/maven2/</url> </mirror>我們可以將對(duì)應(yīng)的倉(cāng)庫(kù)的鏡像配置到 settings.xml 文件中的 mirrors 節(jié)點(diǎn)中即可。如下圖所示,我們配置了阿里云的鏡像。
本小節(jié)主要講解了 HTTP 的請(qǐng)求與響應(yīng),對(duì)于我們爬蟲(chóng)程序來(lái)說(shuō),主要使用的就是 get 請(qǐng)求。對(duì)于返回的狀態(tài)碼的了解,可以方便的調(diào)試和分析我們的爬蟲(chóng)程序是否運(yùn)行良好。
客戶端太慢了,超出了服務(wù)端允許的等待時(shí)間,服務(wù)端會(huì)返回 408 并斷開(kāi)連接。常見(jiàn)的有可能網(wǎng)速太慢了,一個(gè)請(qǐng)求發(fā)送太長(zhǎng)時(shí)間還沒(méi)發(fā)完。HTTP/1.1 408 Request TimeoutConnection: closeContent-Type: text/plainToo slow! Try again
當(dāng)給客戶端提供 api 接口時(shí)通常使用的數(shù)據(jù)響應(yīng)格式為 JSON,使用 json() 函數(shù)可以直接將數(shù)組內(nèi)容以 JSON 格式輸出: public function get(){ $reArr = ["name"=>"愛(ài)因詩(shī)賢","age"=>18,"weight"=>65,"title" => "慕課網(wǎng) ThinkPHP"]; return json($reArr); }輸出內(nèi)容如下圖所示:json 函數(shù)默認(rèn)的響應(yīng)碼是 200,如下代碼可以給返回的 JSON 數(shù)據(jù)帶上指定的 HTTP 響應(yīng)碼: public function get() { $height = $this->request->param('height', 0, 'intval'); if ($height == 0) { return json("身高參數(shù)不合法", 404); } }輸出內(nèi)容如下圖所示:Tips: json("身高參數(shù)不合法", 404) 第二參數(shù) 404 表示返回指定的 HTTP 狀態(tài)碼。
X.509 其實(shí)是一種證書(shū)的格式規(guī)范,它主要包含了以下信息:版本號(hào)序列號(hào)簽名算法頒發(fā)者證書(shū)有效期(開(kāi)始日期,終止日期)主題主題公鑰信息(公鑰算法,主體公鑰)頒發(fā)者唯一身份信息(可選)主題唯一身份信息(可選)擴(kuò)展信息(可選)簽名這個(gè)證書(shū)經(jīng)過(guò)加密后變成了一種身份標(biāo)識(shí),用來(lái)使通信雙方彼此信任。我們常見(jiàn)的 SSL 證書(shū)就是 X.509 證書(shū)的一種表現(xiàn)形式。SSL 是對(duì) HTTP 通訊協(xié)議進(jìn)行加密通訊的方法,SSL 和 HTTP 的結(jié)合也就是常見(jiàn)的 HTTPS 了。
無(wú)論是作為后端開(kāi)發(fā)、前端開(kāi)發(fā)、測(cè)試開(kāi)發(fā)程序員或者是運(yùn)維人員,在面試過(guò)程中,大概率都會(huì)被問(wèn)到 HTTP 協(xié)議相關(guān)題目。因?yàn)榘殡S著 2010 年之后移動(dòng)互聯(lián)網(wǎng)在全世界的高速發(fā)展,各種各樣的瀏覽器(Chrome、FireFox、Safari 等)層出不窮,也誕生了諸多服務(wù)端開(kāi)發(fā)的語(yǔ)言(例如 Golang 語(yǔ)言),瀏覽器和服務(wù)端之間的交互是不可避免的,我們對(duì)于不同的瀏覽器和不同的服務(wù)端,總不能每次都創(chuàng)建一種新的交互協(xié)議,所以需要確定統(tǒng)一的協(xié)議規(guī)范,也就是本文的 HTTP 協(xié)議。
Syntax: return code [text];# return code URL;# return URL;Default: —Context: server, location, ifreturn 指令返回后,Http 請(qǐng)求將在 return 的階段終止,后續(xù)階段將無(wú)法進(jìn)行,所以許多模塊得不到執(zhí)行。return 200 "hello, world"
首先,理清楚幾個(gè)概念:WSGI:全稱是 Web Server Gateway Interface,WSGI 只是一種規(guī)范,描述 web server 如何與 web application 通信的規(guī)范。要實(shí)現(xiàn) WSGI 協(xié)議,必須同時(shí)實(shí)現(xiàn) web server 和 web application,當(dāng)前運(yùn)行在 WSGI 協(xié)議之上的 web 框架有 Flask, Django,這也是目前最流行的 python web框架。uwsgi:與WSGI一樣是一種通信協(xié)議,是uWSGI服務(wù)器的獨(dú)占協(xié)議,用于定義傳輸信息的類型(type of information),每一個(gè)uwsgi packet前4byte為傳輸信息類型的描述。uWSGI:是一個(gè)web服務(wù)器,實(shí)現(xiàn)了WSGI協(xié)議、uwsgi協(xié)議、http協(xié)議等。WSGI 協(xié)議其實(shí)是定義了一種 server 與 application 解耦的規(guī)范,即可以有多個(gè)實(shí)現(xiàn) WSGI server 的服務(wù)器,也可以有多個(gè)實(shí)現(xiàn) WSGI application 的框架,那么就可以選擇任意的 server 和 application 組合實(shí)現(xiàn)自己的 web 應(yīng)用。Django,F(xiàn)lask 框架都有自己實(shí)現(xiàn)的簡(jiǎn)單的WSGI server,一般用于服務(wù)器調(diào)試,生產(chǎn)環(huán)境下直接使用WSGI server。Nginx 中將 http 協(xié)議的報(bào)文轉(zhuǎn)換成 uwsgi 協(xié)議的報(bào)文,只需要使用 uwsgi_pass 指令即可。和 proxy_pass 指令類似,前者轉(zhuǎn)發(fā)為 uwsgi 協(xié)議的報(bào)文,后者代理轉(zhuǎn)發(fā) http 協(xié)議的報(bào)文。其余用法一致。Syntax: uwsgi_pass [protocol://]address;Default: —Context: location, if in location用法示例:...http { ... server { listen 9000; location / { # 包含uwsgi請(qǐng)求描述文件 include uwsgi_params; # 配置請(qǐng)求傳遞,socket地址 uwsgi_pass 127.0.0.1:9000; } } ...}...
反向代理你可以簡(jiǎn)單的理解為轉(zhuǎn)發(fā),轉(zhuǎn)發(fā)重要的一點(diǎn)是要配置轉(zhuǎn)發(fā)規(guī)則。Nginx 配置的默認(rèn)位置是在 /conf/nginx.conf,配置中關(guān)于 Http 的主要配置如下: http { .... server { listen 80;#監(jiān)聽(tīng)端口 server_name localhost;#域名 # 禁止訪問(wèn)隱藏文件 # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac). location ~ /\. { deny all; access_log off; log_not_found off; } # 默認(rèn)請(qǐng)求 location / { # 首先嘗試將請(qǐng)求作為文件提供,然后作為目錄,然后回退到顯示 404。 # try_files 指令將會(huì)按照給定它的參數(shù)列出順序進(jìn)行嘗試,第一個(gè)被匹配的將會(huì)被使用。 # try_files $uri $uri/ =404; try_files $uri $uri/ /index.php?path_info=$uri&$args =404; access_log off; expires max; } # 所有動(dòng)態(tài)請(qǐng)求都轉(zhuǎn)發(fā)給tomcat處理 location ~ .(jsp|do)$ { proxy_pass http://test; } upstream test { # 負(fù)載均衡配置 server localhost:8080; server localhost:8081; }}Http:某臺(tái)虛擬服務(wù)器;Server : 定義了服務(wù)器監(jiān)聽(tīng)哪個(gè)端口,哪個(gè)域名(可以有多個(gè)域名解析到同一臺(tái)服務(wù)器上面);Location :根據(jù)請(qǐng)求路徑,做不同的響應(yīng)和轉(zhuǎn)發(fā);Upstream : 里面可以配置多個(gè)監(jiān)聽(tīng)的服務(wù)地址,請(qǐng)求過(guò)來(lái)可以依次亦或根據(jù)配置的權(quán)重進(jìn)行輪詢,從而達(dá)到負(fù)載均衡的效果;Nginx 修改完配置可以不用重啟,運(yùn)行下面命令重新加載下配置。nginx -s reload
這個(gè)閉包里面的是我們項(xiàng)目運(yùn)行所需要的的依賴,往往和上面的repositories是相對(duì)應(yīng)的。buildscript { repositories { //注釋1 maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.5.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files //注意:不要將您的應(yīng)用程序依賴項(xiàng)放在這里; 它們屬于單獨(dú)的模塊build.gradle文件 }}allprojects { repositories { //注釋2 maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } google() jcenter() }}task clean(type: Delete) { delete rootProject.buildDir}這里我們需要注意代碼中的注釋1和注釋2處的 repositories 閉包,往往我們添加一個(gè) maven 庫(kù)的依賴時(shí),需要兩個(gè)閉包中都添加一遍。Tips: 我們添加 Maven 庫(kù)時(shí)可以需要兩個(gè)都添加,比如國(guó)外 Maven 訪問(wèn)太慢,我們可以加入國(guó)內(nèi)阿里云的 Maven 庫(kù)。
import redef parseUrl(url): pattern = '(.*)://(.*)/(.*)' matchObject = re.search(pattern, url) all = matchObject.group(0) protocol = matchObject.group(1) host = matchObject.group(2) path = matchObject.group(3) print('group(0) =', all) print('group(1) =', protocol) print('group(2) =', host) print('group(3) =', path) print()parseUrl('http://idcbgp.cn/wiki')parseUrl('http://idcbgp.cn/courses')在第 3 行,函數(shù) parseUrl(url) 分析 URL 的組成部分URL 由 3 部分構(gòu)成:協(xié)議、主機(jī)名、路徑名在第 4 行,定義了匹配 URL 的模式 ‘(.)://(.)/(.*)’第 1 個(gè) (.*) 匹配協(xié)議第 2 個(gè) (.*) 匹配主機(jī)名第 3 個(gè) (.*) 匹配路徑名匹配對(duì)象 matchObject 的 group(index) 方法返回指定分組號(hào)的分組group(0) 為匹配整個(gè)表達(dá)式的字符串group(1) 為匹配的協(xié)議group(2) 為匹配的主機(jī)名group(3) 為匹配的路徑名程序運(yùn)行輸出:group(0) = http://idcbgp.cn/wikigroup(1) = httpsgroup(2) = idcbgp.cngroup(3) = wikigroup(0) = http://idcbgp.cn/coursesgroup(1) = httpgroup(2) = idcbgp.cngroup(3) = courses
要實(shí)現(xiàn) OAuth2.0 認(rèn)證,第一步要將自己的應(yīng)用注冊(cè)到 Github 管理中心。這里需要提前準(zhǔn)備以下信一個(gè)可用的 Github 賬號(hào);應(yīng)用名稱,本例中將使用:spring-security-demo-1-oauth2-client ;應(yīng)用的主頁(yè),本例中將使用:http://localhost:8080/ ;應(yīng)用描述信息(非必填);認(rèn)證回調(diào)地址,本例中將使用:http://localhost:8080/login/oauth2/code/github 。準(zhǔn)備好信息后,訪問(wèn) Github 的 Register a new OAuth application,填寫好信息后提交,在提交成功后將得到如下反饋?lái)?yè)面,記錄下「Client ID」和「Client Secret」兩個(gè)值。如下圖:提交信息反饋?lái)?yè)
瀏覽器每發(fā)起一次請(qǐng)求都需要跟服務(wù)端建立連接,服務(wù)端要時(shí)刻監(jiān)聽(tīng)有沒(méi)有客戶端連接。傳輸層協(xié)議有 TCP/UDP 兩種,實(shí)現(xiàn)起來(lái)并沒(méi)有強(qiáng)制說(shuō)用哪一種,下面是官方文檔對(duì) Http 連接的說(shuō)明:HTTP communication usually takes place over TCP/IP connections. The default port is TCP 80 .文檔中指明了連接通常用的是 TCP, TCP 不用考慮數(shù)據(jù)包亂序,丟失這些問(wèn)題,實(shí)現(xiàn)起來(lái)更簡(jiǎn)單,高效。在代碼層我們可以用 Socket 來(lái)實(shí)現(xiàn)我們的 TCP 傳輸服務(wù)。
在客戶端和服務(wù)器的的互動(dòng)過(guò)程,客戶端需要通過(guò)某種手段讓服務(wù)器端發(fā)生 “狀態(tài)轉(zhuǎn)移”(State Transfer)。例如,在論壇中,論壇當(dāng)前包含的主題以及相應(yīng)的回復(fù),即為服務(wù)端的狀態(tài),增加、修改、刪除主題或者回復(fù),都會(huì)引起 “狀態(tài)轉(zhuǎn)移”。根據(jù) HTTP 標(biāo)準(zhǔn),有 4 個(gè) HTTP 請(qǐng)求方法,可以用于服務(wù)端的 “狀態(tài)轉(zhuǎn)移”:請(qǐng)求方法功能GET獲取資源POST新建資源PUT更新資源DELETE刪除資源
在集成 Swagger Editor 到 SpringBoot 中時(shí),為什么我們需要將 SpringBoot 項(xiàng)目的配置文件 applicaiton.properties 來(lái)修改成 YAML 配置源文件呢 ?這是因?yàn)槲覀兊?Swagger Editor 只支持以 .yml 文件格式結(jié)尾的配置文件來(lái)編寫 Swagger Editor ,這也是我一再?gòu)?qiáng)調(diào) YAML 配置源文件的原因。在修改好配置源文件之后,接下來(lái)我們需要編寫配置源文件,并且在編寫完配置源文件之后,我們需要將該配置源文件進(jìn)行重命名,重命名的規(guī)則一般都是項(xiàng)目名稱 + Swagger Editor + 配置源文件版本號(hào)。簡(jiǎn)易配置 Swagger Editor 源文件Swagger Editor 的配置源文件我們?cè)?Spring Boot 集成 Swagger Codegen 小節(jié)中做了簡(jiǎn)短的介紹:就是我們?cè)谂渲?Swagger Codegen 服務(wù)端代碼生成規(guī)則那里的服務(wù)端代碼其實(shí)就是 Swagger Editor 配置源文件的一部分,也就是說(shuō)Swagger Codegen 服務(wù)端的代碼生成規(guī)則是通過(guò) Swagger Editor 配置源文件來(lái)實(shí)現(xiàn)的。所以,在掌握了 Swagger Editor 的基本語(yǔ)法和使用技巧之后,我們就可以在編寫好 Swagger Editor 配置源文件之后來(lái)生成我們項(xiàng)目的服務(wù)端代碼,可謂是一舉兩得,這也是 Swagger 官方為我們所考慮的一方面。還是那句話,在 Swagger Editor 中,配置 Swagger Editor 需要對(duì) yml 配置源文件有一定的了解和使用,所以,有不清楚的同學(xué)請(qǐng)自行查閱相關(guān)資料。接下來(lái)我們通過(guò) Swagger Editor 官方的 Demo 來(lái)為大家配置 Swagger Editor 。Swagger Editor 基本配置信息swagger: "2.0"info: description: "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key special-key to test the authorization filters." version: "1.0.0" title: "Swagger Petstore" termsOfService: "http://swagger.io/terms/" contact: email: "apiteam@swagger.io" license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html"host: "petstore.swagger.io"basePath: "/v2"tags: name: "pet" description: "Everything about your Pets" externalDocs: description: "Find out more" url: "http://swagger.io"schemes: "https" "http"Swagger Editor 接口配置信息paths: /pet: post: tags: - "pet" summary: "Add a new pet to the store" description: "" operationId: "addPet" consumes: "application/json" "application/xml" produces: "application/xml" "application/json" parameters: in: "body" name: "body" description: "Pet object that needs to be added to the store" required: true schema: $ref: "#/definitions/Pet" responses: "405": description: "Invalid input"對(duì)于以上所使用的屬性我們會(huì)在本節(jié)的后半部分進(jìn)行詳細(xì)介紹,這里大家可以先做簡(jiǎn)單的了解即可。
編寫一個(gè)程序 basis.py 分別在處理請(qǐng)求前、處理請(qǐng)求后注冊(cè)鉤子函數(shù):from flask import Flask, request, render_templateapp = Flask(__name__)@app.route('/')def index(): print('execute request') return 'Hello World'@app.before_first_requestdef before_first_request(): print('before_first_request')@app.before_requestdef before_request(): print('before_request')@app.after_requestdef after_request(response): print('after_request') return responseif __name__ == '__main__': app.run()在第 10 行,通過(guò) @app.before_first_request 注冊(cè)在第一次處理請(qǐng)求時(shí)執(zhí)行的鉤子函數(shù),該函數(shù)打印字符串 ‘before_first_request’。在第 14 行,通過(guò) @app.before_request 注冊(cè)在每次處理請(qǐng)求前執(zhí)行的鉤子函數(shù),該函數(shù)打印字符串 ‘before_request’。在第 18 行,通過(guò) @app.after_request 注冊(cè)在每次處理請(qǐng)求后執(zhí)行的鉤子函數(shù),該函數(shù)打印字符串 ‘a(chǎn)fter_request’。鉤子函數(shù) after_request(response) 的輸入?yún)?shù)是一個(gè) Response 對(duì)象,函數(shù)返回的也是一個(gè) Response 對(duì)象。
線性布局分為垂直和水平布局兩種方式,在使用過(guò)程中除了方向不同,其余類似。本節(jié)僅演示垂直樣式,水平樣式相信你能夠觸類旁通。顧名思義,垂直布局就是將內(nèi)部 View 從上到下依次排成一列,為了便于理解,直接上代碼,在我們新建的工程中,找到“res->layout->activity_main.xml”,編寫代碼如下:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:text="Here" android:background="#E71B0C"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:text="Is" android:background="#E7430F"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:text="Imooc" android:background="#E6D11B"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:background="#88F10D" android:text="Android"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:background="#03A9F4" android:text="Study"/></LinearLayout>直接編譯,效果如下:如圖,在屏幕中有 5 個(gè) TextView 按照垂直方向依次排成一列。注意,Layout 都是繼承自 ViewGroup 的,在上一節(jié)我們說(shuō)過(guò) ViewGroup 也是 View,所以我們可以推理出 Layout 里面也是可以放 Layout 的。按照這個(gè)邏輯我們還可以在垂直布局中嵌套水平布局,比如我們希望把“Here Is”和“Android Study”這兩個(gè)短語(yǔ)寫到一排:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#E71B0C" android:text="Here" android:textSize="30sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#E7430F" android:text="Is" android:textSize="30sp" /> </LinearLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#E6D11B" android:text="Imooc" android:textSize="30sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#88F10D" android:text="Android" android:textSize="30sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#03A9F4" android:text="Study" android:textSize="30sp" /> </LinearLayout></LinearLayout>直接運(yùn)行:我們將“Here”和“Is”、“Android”和“Study”這四個(gè) TextView 兩兩一組分別放到了一個(gè)水平樣式的 LinearLayout 中,這樣驗(yàn)證了,Layout 是可以嵌套使用的。