功能描述: @PathVariable 注解用來獲取 URL 中變量的值。什么是 URL 中的變量?還是用一個(gè)實(shí)例來說話:@RequestMapping("/user/{userId}")public String getUserById(@PathVariable("userId") String userId){}{userId} 就是變量占位符。具體請(qǐng)求時(shí),可使用一個(gè)真實(shí)值替換。如請(qǐng)求格式:http://localhost:8888/user/1 時(shí),@PathVariable 可以為 getUserById() 方法中的 userId 參數(shù)注入值 1。@PathVariable 注解同樣有一個(gè) required 屬性,表示是否強(qiáng)制 URL 中有變量的存在。boolean required() default true;@PathVariable 注解非常有用,請(qǐng)你一定要記住它。
Django 中傳遞參數(shù)給視圖函數(shù)的方式主要可分為以下兩種形式:URL 傳參和非 URL 傳參兩種。第一種基于 Django 中的 URLconf 配置,可以通過 URL 路徑將對(duì)應(yīng)匹配的參數(shù)傳到視圖函數(shù)中;而另外一種就是屬于HTTP 請(qǐng)求攜帶的參數(shù)了,請(qǐng)求參數(shù)可以放到 URL 中以 ? 格式加到 URL 的最后面,也可以將參數(shù)放到請(qǐng)求 body 中,最后統(tǒng)一由視圖函數(shù)中的 request 參數(shù)保存并傳到視圖函數(shù)中。
在nginx.conf的中加入如下配置:...stream { server { listen 3000; return '3000 server get ip: $remote_addr!\n'; } server { listen 30; # 注意,只寫ip和port,不要加上[http:]之類的,這里是四層的協(xié)議 proxy_pass 127.0.0.1:3000; }}...啟動(dòng) nginx 后,我們?cè)谕饷嫱ㄟ^ telnet 命令訪問該主機(jī)的 3000 和 30端口,可以看到如下結(jié)果:[shen@shen ~]$ telnet 180.76.152.113 3000Trying 180.76.152.113...Connected to 180.76.152.113.Escape character is '^]'.3000 server get ip: 103.46.244.108![shen@shen ~]$ telnet 180.76.152.113 30Trying 180.76.152.113...Connected to 180.76.152.113.Escape character is '^]'.3000 server get ip: 127.0.0.1!Connection closed by foreign host.可以看到,訪問30端口時(shí),nginx 幫我們轉(zhuǎn)發(fā) tcp 層流量到 3000端口,最后返回了相關(guān)響應(yīng)字符串。
<EditText xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/input_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="請(qǐng)輸入密碼" android:imeOptions="actionDone" android:inputType="textPassword" android:maxLines="5" android:textSize="20sp" />代碼比較簡(jiǎn)單,主要實(shí)現(xiàn)了以下幾個(gè)屬性:android:hint="請(qǐng)輸入密碼"用來提示用戶,本輸入框的功能是輸入密碼,當(dāng)你點(diǎn)擊EditText往里面輸入字符之后,提示就會(huì)消失。android:inputType="textPassword"設(shè)置當(dāng)前輸入的類為密碼,那么當(dāng)用戶輸入文本后,系統(tǒng)會(huì)用“*”或者“·”替代。android:maxLines="3"設(shè)置當(dāng)前的輸入框最多使能容納3行內(nèi)人,如果多余 3 行,則會(huì)采用滾動(dòng)條的形式上下滑動(dòng)。效果如下:
3.2.1 生成 SSL 證書這里的證書包含服務(wù)端證書和客戶端證書。服務(wù)端證書用于配置 Tomcat,使瀏覽器驗(yàn)證服務(wù)器的真實(shí)性。客戶端證書需要安裝到用戶瀏覽器中,用來開啟 SSL 客戶端認(rèn)證。服務(wù)端配置方式如下:<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="${catalina.home}/conf/server.jks" keystoreType="JKS" keystorePass="password" truststoreFile="${catalina.home}/conf/server.jks" truststoreType="JKS" truststorePass="password"/>即使客戶端瀏覽器不提供證書,clientAuth 也同樣可以置為 true,此時(shí)客戶端如果不提供 X.509 認(rèn)證平局,則其不會(huì)被授權(quán)訪問 Spring Security 資源。
有時(shí)候我們希望接口允許有任意的屬性,語法是用 [] 將屬性包裹起來:// 語法interface Clothes { color?: string; size: string; readonly price: number; [propName: string]: any;}// 任意屬性 activitylet myClothes: Clothes = { size: 'XL', price: 98, activity: 'coupon'}代碼解釋: 這里的接口 Clothes 可以有任意數(shù)量的屬性,并且只要它們不是 color size 和 price,那么就無所謂它們的類型是什么。項(xiàng)目案例:使用 axios 庫發(fā)起 HTTP 傳輸?shù)臅r(shí)候,可以寫入一個(gè)自定義的屬性,就是因?yàn)樵创a中定義了一個(gè)任意屬性:this.$axios({ method: 'put', url: '/cms/user', data: { nickname: this.nickname, }, showBackend: true,})
此時(shí)我們可以根據(jù) JSP 模板引擎,按模板規(guī)則顯示商品信息了。實(shí)例:<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>商品列表</title></head><body> <div>商品列表:</div> <c:forEach var="item" items="${goodsList}"> ${item.name}--${item.price}--${item.pic} </c:forEach></body></html>注意我們通過 JSP 的模板語法,輸出了商品列表信息。關(guān)于 JSP 模板引擎更多的語法規(guī)則,感興趣的同學(xué)可以后續(xù)查閱更多資料。
Protocols 描述了如何以異步的方式處理網(wǎng)絡(luò)中的事件。HTTP、DNS 以及 IMAP 是應(yīng)用層協(xié)議中的例子。Protocols實(shí)現(xiàn)了 IProtocol 接口,它包含如下的方法:makeConnection(): 在 transport 對(duì)象和服務(wù)器之間建立一個(gè)連接;connectionMade(): 連接建立起來后調(diào)用;dataReceived(): 接收數(shù)據(jù)時(shí)調(diào)用;connectionLost(): 關(guān)閉連接時(shí)調(diào)用;Factory 和 Protocol 有嚴(yán)格的不同。Factory 的工作是管理連接事件,并且創(chuàng)建 Protocol 對(duì)象處理每一個(gè)成功的連接。一旦連接建立,Protocol 對(duì)象就接管下面的工作了,包括收發(fā)數(shù)據(jù)和決定是否關(guān)閉連接。
Node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行時(shí)。Node.js 并不是運(yùn)行在瀏覽器里的一個(gè)庫或框架。Node.js 可以提供了一系列服務(wù)端能力,如 HTTP 服務(wù)、讀寫本地文件等,開發(fā)者可以利用 JavaScript 來使用這些能力,因?yàn)榍岸碎_發(fā)者的主要語言就是 JavaScript,所以利用 Node.js 可以降低學(xué)習(xí)成本,讓前端開發(fā)者更容易接觸到服務(wù)端開發(fā)。
以上是設(shè)置 View 與父布局的相對(duì)位置,當(dāng) RelativeLayout 中有了 View 之后,我們同樣可以設(shè)置 View 與其他兄弟 View 的位置關(guān)系。android:layout_above="@id/center":這個(gè)屬性設(shè)置當(dāng)前 View 擺放在 id 為 center 的 View 的上方。android:layout_below="@id/center":設(shè)置當(dāng)前View擺放在 id 為 center 的 View 的下方。android:layout_toLeftOf="@id/center":設(shè)置當(dāng)前 View 擺放在 id 為 center 的 View 的左邊。android:layout_toRightOf="@id/center":設(shè)置當(dāng)前 View 擺放在 id 為 center 的 View 的右邊。注:可以看到這個(gè)屬性是需要指定一個(gè) id 的,所以我們需要給被依賴的兄弟 View 賦予一個(gè) id。但是要注意的是與賦予 id 時(shí)用“+id”不同,在指定兄弟 View 的時(shí)候,不需要寫“+id”,即直接寫 id 即可。參考代碼如下:<?xml version="1.0" encoding="utf-8"?><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" tools:context=".MainActivity"> <TextView android:id="@+id/center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="#FFdddddd" android:text="centerInParent" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/center" android:layout_centerInParent="true" android:background="#F30C5D" android:text="above" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/center" android:layout_centerInParent="true" android:background="#ECEC18" android:text="below" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_toLeftOf="@id/center" android:background="#14CEE6" android:text="left" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_toRightOf="@id/center" android:background="#25EC0F" android:text="right" android:textSize="20sp" /></RelativeLayout>
URL 的中文名稱為統(tǒng)一資源定位符。簡(jiǎn)單來說,它就是一個(gè)地址,是我們請(qǐng)求互聯(lián)網(wǎng)上某一個(gè)資源地址或者某一個(gè)服務(wù)接口的完整路徑。我們找一個(gè)網(wǎng)站的實(shí)際 URL 例子,來說明下完整 URL 的組成部分:URL 示例這個(gè)慕課網(wǎng)上的完整的 URL 地址為: https://coding.imooc.com/class/evaluation/393.html?page=5#Log。URL 的格式如下:schema://host[:port]/path…/[?query-string]#fragmentschema:表示協(xié)議,常見的有 http/https 協(xié)議,還有 ftp 協(xié)議,ws/wss 協(xié)議(websocket)等等;host:域名或者直接是 IP 地址。本例子使用的是慕課網(wǎng)的一個(gè)子域名:coding.imooc.com;port:不寫會(huì)使用默認(rèn)端口,非默認(rèn)地址一定要寫明端口號(hào)。本例中使用默認(rèn)端口 443;path:資源地址,會(huì)有多個(gè) / 表示路徑層級(jí)。本例中的路徑為 /class/evaluation/393.html;query-string:如果 URL 帶參數(shù),放到 ? 之后, # 號(hào)之前,使用 key=value 形式,多個(gè)參數(shù)之間使用 & 進(jìn)行連接。本例中的參數(shù)是 ?page=5;錨點(diǎn):或稱片段(fragment),HTTP 請(qǐng)求不包括錨部分,從 # 開始到最后,都是錨部分。本例中的錨部分是 Log。錨部分不是一個(gè) URL 必須的部分。
除了 Django 定義的簡(jiǎn)單類型,我們還可以自定義參數(shù)類型轉(zhuǎn)換器來支持更為復(fù)雜的 URL 場(chǎng)景。比如前面的 int 類型并不支持負(fù)整數(shù),我希望開發(fā)一個(gè)能匹配正負(fù)數(shù)的類型,具體的步驟如下:在 hello_app/urls.py 中定義一個(gè) SignInt 類。該類有一個(gè)固定屬性 regex,用于匹配動(dòng)態(tài)值;兩個(gè)固定方法:to_python() 方法和 to_url() 方法:# hello_app/urls.pyclass SignInt: regex = '-*[0-9]+' def to_python(self, value): # 將匹配的value轉(zhuǎn)換成我們想要的類型 return int(value) def to_url(self, value): # 反向生成url時(shí)候回用到 return value注冊(cè)該定義的轉(zhuǎn)換器,并給出一個(gè)簡(jiǎn)短的名字,比如 sint:# hello_app/urls.pyfrom django.urls import converters, register_converterregister_converter(SignInt, 'sint')...最后,我們就可以在 URLconf 中使用該類型來配置動(dòng)態(tài)的 URL 表達(dá)式:# hello_app/urls.pyurlpatterns = [ path('articles/<sint:signed_num>/', views.signed_convert),]# hello_app/views.pydef signed_convert(request, signed_num, **kwargs): return HttpResponse('hello, 自定義類型轉(zhuǎn)換器,獲取參數(shù)={}\n'.format(signed_num))啟動(dòng) Django 服務(wù)后,執(zhí)行相關(guān)請(qǐng)求,可以看到 Django 的路由系統(tǒng)能成功匹配到帶負(fù)數(shù)和正數(shù)的 URL:[root@server ~]# curl http://127.0.0.1:8881/hello/articles/1998/hello, 自定義類型轉(zhuǎn)換器,獲取參數(shù)=1998[root@server ~]# curl http://127.0.0.1:8881/hello/articles/-1998/hello, 自定義類型轉(zhuǎn)換器,獲取參數(shù)=-1998
接下來的幾個(gè)屬性能夠?qū)?View 固定在相對(duì)于父布局任意一條邊固定距離的位置。android:layout_alignParentTop=“true”:使用layout_alignParentTop屬性可以讓你的View與父布局的頂端對(duì)齊,同時(shí)由于RelativeLayout的長(zhǎng)寬是match_parent,所以最終的效果就是顯示在屏幕的最上方。android:layout_alignParentBottom=“true”:使用layout_alignParentBottom屬性可以讓你的View與父布局的底部對(duì)齊,最終的效果就是顯示在屏幕的最下方。android:layout_alignParentLeft=“true”:使用layout_alignParentBottom屬性可以讓你的View與父布局的左側(cè)對(duì)齊,最終的效果就是顯示在屏幕的最左側(cè)。android:layout_alignParentRight=“true”:使用layout_alignParentRight屬性可以讓你的View與父布局的右側(cè)對(duì)齊,最終的效果就是顯示在屏幕的最右側(cè)。注:以上幾個(gè)對(duì)齊屬性都可以多個(gè)搭配使用,比如我們上圖中的幾個(gè)View,分別是右上對(duì)齊、左下對(duì)齊和右中對(duì)齊參考代碼如下:<?xml version="1.0" encoding="utf-8"?><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" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:background="#F75549" android:text="right|top" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" android:layout_centerVertical="true" android:background="#F1E14D" android:text="left|bottom" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerInParent="true" android:layout_centerVertical="true" android:background="#14CEE6" android:text="right|centerInParent" android:textSize="20sp" /></RelativeLayout>
了解 TCP: TCP 協(xié)議適用于客戶端數(shù)量相對(duì)比較少,并且通信頻繁的業(yè)務(wù)場(chǎng)景;Http 協(xié)議則適用于客戶端數(shù)量比較大的業(yè)務(wù)場(chǎng)景。因?yàn)?Http 是短連接,請(qǐng)求完成即會(huì)釋放連接資源,不再占用服務(wù)器資源,但是,TCP 則不會(huì),連接成功,則可以多次請(qǐng)求,不會(huì)釋放,除非特殊原因?qū)е逻B接斷開。面臨問題: 既然長(zhǎng)連接是不會(huì)釋放連接資源,那么如果很多客戶端只是完成了連接,但是并沒有實(shí)際的業(yè)務(wù)請(qǐng)求操作,那么服務(wù)器的資源還是被占用,導(dǎo)致服務(wù)器性能下降。解決辦法: 把那些長(zhǎng)期占用連接資源,但是并沒有實(shí)際業(yè)務(wù)操作的連接斷開掉,等它們需要做業(yè)務(wù)操作的時(shí)候,再重連服務(wù)器。這樣可以達(dá)到即使釋放沒用的資源,提高服務(wù)器的性能。總結(jié),心跳機(jī)制主要有以下兩個(gè)方面的作用定時(shí)剔除哪些沒用的連接,減輕服務(wù)端的壓力;適用于中間件(比如:RPC 框架),服務(wù)端規(guī)定時(shí)間內(nèi)沒用收到客戶端的心跳數(shù)據(jù),則可以認(rèn)為其宕機(jī),服務(wù)端剔除對(duì)于的映射關(guān)系。
通常,在生產(chǎn)環(huán)境中使用 Nginx 進(jìn)行反向代理和負(fù)載均衡或者各種其他處理時(shí),良好的日志記錄是非常關(guān)鍵的一環(huán)。通過精心配置的 Nginx 日志,我們可以獲取用戶的真實(shí) ip、瀏覽器信息,請(qǐng)求處理時(shí)間,請(qǐng)求URL等,這樣方便我們排查和回溯錯(cuò)誤。具體要記錄哪些信息,可以通過 Nginx 中的 log_format 指令定義,由它定義日志的格式。而對(duì)于使用哪種日志格式和設(shè)置日志的保存路徑則由 access_log 指令指定的。另外在 Nginx 中還有一個(gè)配置服務(wù)器和請(qǐng)求處理過程中的錯(cuò)誤信息的指令,那就是 error_log指令。最后,如果在配置的日志文件路徑中使用了變量,我們可以通過open_log_file_cache指令來設(shè)置緩存,提升性能。對(duì)于大型的網(wǎng)站而言,大量的 http 請(qǐng)求意味著大量的日志記錄,及時(shí)按天或按大小進(jìn)行 Nginx 日志備份也至關(guān)重要的。在 Nginx 的日志模塊主要有2個(gè), ngx_stream_log_module 和 ngx_http_log_module,分別表示四層的日志模塊和七層的日志模塊,其指令和用法都是一致的,接下來我們只針對(duì) http 請(qǐng)求的日志進(jìn)行說明和使用。
目前官方最新版本(截止到 2019 年 12 月 12 日)為 1.17.6,我們直接去官網(wǎng)找到下載地址。不推薦直接使用 yum 安裝,因?yàn)榇嬖谌缦聝蓚€(gè)問題:版本太舊,以 CentOS 為例,直接 yum 安裝的版本是 1.12.2 版本,已經(jīng)嚴(yán)重脫離了時(shí)代;無法自定義安裝模塊,安裝目錄等等,不方便后續(xù)的實(shí)驗(yàn)。# 下載nginx安裝包$ wget http://nginx.org/download/nginx-1.17.6.tar.gz # 解壓安裝包$ tar -xzf nginx-1.17.6.tar.gz
我們?cè)陧?xiàng)目的根目錄下創(chuàng)建 server.js,用來啟動(dòng) vue 項(xiàng)目:const fs = require("fs");const path = require("path");const bodyParser = require("body-parser");const express = require("express");const app = express();const list = require("./mock/list.json");app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: false })); // 服務(wù)開啟后訪問指定編譯好的dist文件下的數(shù)據(jù)app.use(express.static(path.resolve(__dirname, "./dist")));app.get("/todo/list", (req, res) => { res.json({ data: list });});app.get("*", function(req, res) { const html = fs.readFileSync( path.resolve(__dirname, "./dist/index.html"), "utf-8" ); res.send(html);});app.listen(8081);運(yùn)行命令:node start.js然后,訪問 http://localhost:8081/#/ 就可以正常預(yù)覽項(xiàng)目了。
由于不限制選中數(shù)量,Checkbox 控件不存在類似 RadioGroup 的父容器,我們可以直接在布局文件中寫<Checkbox/>標(biāo)簽,如下:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/group" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="20dp" android:orientation="horizontal"> <CheckBox android:id="@+id/cb_android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="true" android:text="Android" /> <CheckBox android:id="@+id/cb_ios" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="iOS" /></LinearLayout>我們看到可以有兩個(gè)選項(xiàng)同時(shí)被選中,和 RadioButton 一樣,通過android:checked屬性設(shè)置默認(rèn)選中的選項(xiàng)。
PREACCESS、ACCESS 和 POST_ACCESS 是和 Http 請(qǐng)求訪問權(quán)限相關(guān)的階段。PREACCESS 階段是在連接之前要做的訪問控制, 這個(gè)階段有 limit_conn 和 limit_req 等模塊工作。ACCESS 階段是解決用戶能不能訪問,比如根據(jù)用戶名、密碼限制用戶訪問(auth_basic 模塊)、根據(jù) ip 限制用戶訪問(access 模塊)以及第三方模塊認(rèn)證限制用戶的訪問(auth_request模塊)。POST_ACCESS 是在 ACCESS 之后要做的一些工作。
在 CSS 中我們可以通過 @import 來導(dǎo)入一個(gè)樣式文件,Sass 擴(kuò)展了 CSS 的 @import 規(guī)則,使得可以導(dǎo)入 CSS 后綴的樣式文件和 Scss 后綴的樣式文件,并且提供了對(duì) mixin 、函數(shù)和變量的訪問。與 CSS 的 @import 不同的是, CSS 使用 @import 導(dǎo)入文件是在頁面渲染的時(shí)候發(fā)起多個(gè) http 請(qǐng)求來獲取文件內(nèi)容,而 Sass 的導(dǎo)入 @import 是在編譯時(shí)獲取文件內(nèi)容導(dǎo)入的。
比較早之前,部署 Java web 服務(wù)只是單純使用 Tomcat 做 Web 服務(wù)器,前后端代碼融合在一個(gè)工程之中。Tomcat 啟動(dòng)后對(duì)外提供一個(gè)端口接收和相應(yīng) http請(qǐng)求。隨著 Nginx 的越來越流行,同時(shí)加上其優(yōu)秀的反響代理和負(fù)載均衡功能,我們?cè)诰€上的 Java web 通常會(huì)結(jié)合二者,即使用 Nginx + Tomcat 的方式來部署 Java web 服務(wù)。最近兩年,隨著微服務(wù)化和前后端工程分離思想的流行,使用 Spring Boot 和 Vue 框架進(jìn)行 Java web 開發(fā)的人的人越來越多。由于前后端分離后需要解決請(qǐng)求跨域的問題,往往會(huì)使用 Nginx 做一層反向代理,這樣可以減少一些代碼風(fēng)險(xiǎn)。所以,目前主流的 Java web開發(fā)模式是:基于 Vue 等優(yōu)秀的前端框架完成頁面開發(fā);使用 Spring Boot 等 java web 框完成后端服務(wù)開發(fā);前端工程打包后是一堆靜態(tài)文件,可以直接由 Nginx 進(jìn)行代理訪問;后端服務(wù)啟動(dòng)后會(huì)占用端口等待請(qǐng)求,Nginx 將使用反向代理功能將前端發(fā)起的 http 請(qǐng)求轉(zhuǎn)到對(duì)應(yīng)的后臺(tái)服務(wù)去處理。如果在多臺(tái)機(jī)器上部署了相同的服務(wù),還可以使用 Nginx 中的負(fù)載均衡功能,將請(qǐng)求均勻分發(fā)到上游的服務(wù),實(shí)現(xiàn)系統(tǒng)的高可用性。
同樣,在 Scrapy 中為我們內(nèi)置了不少的下載中間件,可以方便地配置下載參數(shù),比如 Cookie、代理等。我們現(xiàn)在來介紹一些常用的下載中間件。CookiesMiddleware:該中間件主要用于給請(qǐng)求加上 Cookie,這樣可以方便我們的爬蟲程序使用 Cookie 去訪問網(wǎng)站。它記錄了向 Web Server 發(fā)送的 Cookie,并在之后的 Request 請(qǐng)求中帶上該 Cokkile,就像我們操作瀏覽器那樣。該中間件在 settings.py 中的配置有2個(gè):COOKIES_ENABLE:默認(rèn)為 True,表明啟用 cookies 中間件,如果為 False,則不會(huì)使用 cookies。Tips:如果 Request.meta 參數(shù)的 dont_merge_cookies 的值為 True,那么無論 COOKIES_ENABLE 指定為何值,cookies 在這個(gè)請(qǐng)求的來回中都不會(huì)做任何處理;COOKIES_DEBUG:默認(rèn)為 False。如果為 True,則會(huì)記錄所有請(qǐng)求發(fā)送的 cookies 和響應(yīng)接收到的 cookies;HttpProxyMiddleware:該中間件通過在 Request.meta 中添加 proxy 屬性值為該請(qǐng)求設(shè)置 HTTP 代理;HttpCacheMiddleware:該中間件為所有 HTTP 請(qǐng)求和響應(yīng)提供 low-level 緩存,它需要和緩存存儲(chǔ)后端以及緩存的策略相結(jié)合;DefaultHeadersMiddleware:該中間件通過配置文件中 DEFAULT_REQUEST_HEADERS 的值來設(shè)置所有請(qǐng)求默認(rèn)的請(qǐng)求頭;DownloadTimeoutMiddleware:該中間件主要用來設(shè)置下載超時(shí)時(shí)間。對(duì)應(yīng) settings.py 中的配置值為:DOWNLOAD_TIMEOUT或者 spider 的 download_timeout 屬性好了,常用的下載中間件就介紹這么多了,其余的可以繼續(xù)參考官方文檔,寫的非常詳細(xì)。
geo 坐標(biāo)系需要自行提供地理信息數(shù)據(jù),使用上有一定的不便,因此 echarts 提供了另一種地理坐標(biāo)系實(shí)現(xiàn) —— bmap。bmap 擴(kuò)展將百度地圖帶入 echarts,以百度地圖為底圖繪制地理坐標(biāo)系,所以使用上就不用再關(guān)注地理數(shù)據(jù)了,而且依托于百度地圖提供的強(qiáng)大功能,bmap 在伸縮、移動(dòng)、精度等方面更出色。使用時(shí),除了 echarts 文件外,還需要引入百度地圖依賴、bmap 擴(kuò)展依賴,以 CDN 為例:<!-- 引入百度地圖的jssdk --><!-- 配置方法可參考: http://lbsyun.baidu.com/index.php?title=jspopular3.0 --><script src="http://api.map.baidu.com/api?v=2.0&ak="></script><!-- 引入 ECharts --><script src="http://cdn.bootcss.com/echarts/4.5.0/echarts.js"></script><!-- 引入 bmap 擴(kuò)展 --><script src="http://cdn.bootcss.com/echarts/4.5.0/extension/bmap.min.js"></script>引入后,就可以通過 bmap 配置地理坐標(biāo)系。bmap 所支持的配置項(xiàng)比較少,包括:配置名類型默認(rèn)值說明centerarray當(dāng)前視圖的中心點(diǎn),用經(jīng)緯度表示roamboolean|stringfalse是否開啟鼠標(biāo)縮放和平移漫游。zoomnumber1當(dāng)前視角的初始化縮放比例mapStyleobject舊版地圖的自定義樣式接口,詳見: http://developer.baidu.com/map/jsdevelop-11.htmmapStyleV2object新版地圖的自定義樣式,詳見: http://developer.baidu.com/map/jsdevelop-11.htmbmap 包含了百度地圖所支持的所有地理區(qū)域信息,所以應(yīng)用時(shí)只需通過 center 指定視圖中心點(diǎn),通過 zoom 控制視圖區(qū)域,即可實(shí)現(xiàn)地理坐標(biāo)系,示例:1321示例效果:此外,還可以通過 myChart.getModel().getComponent('bmap').getBMap(); 接口獲取 bmap 對(duì)應(yīng)的地圖實(shí)例,實(shí)現(xiàn)與地圖的交互。在上述示例基礎(chǔ)上,添加額外代碼:// 獲取地圖實(shí)例var map = myChart.getModel().getComponent('bmap').getBMap();// 添加交通狀況層var traffic = new BMap.TrafficLayer();map.addTileLayer(traffic);// 重置視圖中心map.centerAndZoom(new BMap.Point(114.48, 38.03), 8);示例效果;完整的開發(fā)指南,請(qǐng)參考 百度地圖。
如果是轉(zhuǎn)發(fā),數(shù)據(jù)模型只需要是請(qǐng)求作用域級(jí)別的,視圖解析器便能從數(shù)據(jù)模型中拿到所需要的數(shù)據(jù)。對(duì)于重定向,因?yàn)榭缌苏?qǐng)求,視圖無法讀出請(qǐng)求作用域級(jí)別的數(shù)據(jù)模型中的數(shù)據(jù)。如果要讓數(shù)據(jù)模型中的數(shù)據(jù)被視圖識(shí)別出來,則需要提升數(shù)據(jù)模型的作用域,如升級(jí)為會(huì)話作用域級(jí)別。所謂會(huì)話作用域,意味著數(shù)據(jù)會(huì)存放在服務(wù)器的會(huì)話緩存區(qū)。如果數(shù)據(jù)使用的頻率不是很高,會(huì)產(chǎn)生空間上的浪費(fèi)。有沒有一種較佳的方案,即不浪費(fèi)服務(wù)器空間,又能傳遞數(shù)據(jù)了?答案是肯定的。最原始的方式便是采用查詢字符串的方式。如下面的實(shí)例:@RequestMapping("/response03") public String response03(ModelMap model) throws IOException { // 發(fā)送給客戶端的響應(yīng)數(shù)據(jù) String hello = "Hello"; model.addAttribute("data", hello); return "redirect:/hello.jsp"; }model 中的數(shù)據(jù)只是請(qǐng)求作用域級(jí)別,重定向后的 hello.jsp 中無法獲取此數(shù)據(jù), Spring MVC 內(nèi)部處理方式是把數(shù)據(jù)附加在 hello.jsp 后面。打開瀏覽器,輸入請(qǐng)求 http://localhost:8888/sm-demo/response03 地址,查看瀏覽器的地址欄中的 URL 變成 http://localhost:8888/sm-demo/hello.jsp?data=Hello。Tips:數(shù)據(jù)附加在 URL 后面是 Spring MVC 自動(dòng)完成的。把 hello.jsp 頁面中的數(shù)據(jù)讀取方式改成下面的方式(從查詢參數(shù)中讀?。?。<div style="color:red">${param.data} </div> 這種方式有 2 個(gè)缺點(diǎn):安全性低,數(shù)據(jù)赤裸裸地暴露在地址欄中;如果數(shù)據(jù)量多,則整個(gè) URL 變得臃腫不易維護(hù)。多數(shù)據(jù)實(shí)例:@RequestMapping("/response03") public String response03(ModelMap model) throws IOException { // 發(fā)送給客戶端的響應(yīng)數(shù)據(jù) String hello = "Hello"; model.addAttribute("data", hello); model.addAttribute("id", 1); return "redirect:/hello.jsp"; }當(dāng)請(qǐng)求后,URL 會(huì)變成 http://localhost:8888/sm-demo/hello.jsp?data=Hello&id=1 。
XMLHttpRequest 實(shí)質(zhì)上就是一種具有發(fā)送異步請(qǐng)求功能的技術(shù),是一個(gè)可以在 javaScript 、JScript 、VBScript 等腳本語言中使用的 API 對(duì)象。它可以通過異步發(fā)送 HTTP 請(qǐng)求,完成前后端的交互。在我們的客戶端界面上,無刷新交互只是一種表現(xiàn),而異步發(fā)送請(qǐng)求才是這個(gè)技術(shù)的根本。目前為止,XMLHttpRequest 早已成為正式的規(guī)范。并且在大多數(shù)瀏覽器上都得到了支持。
將 select 查詢寫在 mapper.xml 文件中,比如:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.imooc.mybatis.mapper.UserMapper"> <select id="selectUserAgeById" parameterType="java.lang.Integer" resultType="java.lang.Integer"> SELECT age FROM imooc_user WHERE id = #{id} </select></mapper>其中名為 selectUserAgeById 的 select 標(biāo)簽(一般以 id 做為名稱),接收 Integer 類型的參數(shù)(parameterType),并返回 Integer 類型的結(jié)果(resultType);再看 select 標(biāo)簽中的查詢語句,接收 id 參數(shù),類型為 int,返回 age,類型為 int,二者一一對(duì)應(yīng)。
響應(yīng)頭部信息HTTP/1.1 200 OKDate:Sun, 23 Feb 2020 07:31:24 GMTConnection: keep-aliveContent-Encoding: gzipContent-Length: 129Content-Type: application/json; charset=UTF-8...返回了請(qǐng)求的狀態(tài),200狀態(tài)碼對(duì)應(yīng)的就是成功,還有一些鏈接狀態(tài),內(nèi)容的編碼,長(zhǎng)度,媒體類型等。響應(yīng)的正文{result: 0, data: ["Vue", "Python", "Java", "flutter", "springboot", "docker", "React", "小程序"],…}data: ["Vue", "Python", "Java", "flutter", "springboot", "docker", "React", "小程序"]msg: "成功"result: 0返回了消息的具體信息,這個(gè)消息有可能是一串 html 文本,也可能是 json 串,圖片,附件都有可能,一般是跟 content-type 對(duì)應(yīng)。
通常對(duì)于 Python 第三方模塊的學(xué)習(xí)方式都是一樣的。第一步都是先安裝,然后是不斷的使用和參考官方文檔,待熟練掌握后便可以翻看其源碼深入學(xué)習(xí)其實(shí)現(xiàn)原理,最后達(dá)到徹底掌握該模塊的地步。[store@server2 chap02]$ pip3 install requests -i http://pypi.douban.com/simple/接下來我們參考官方文檔的第一個(gè)實(shí)例進(jìn)行測(cè)試,該實(shí)例主要是測(cè)試 requests 庫的一些方法及其使用場(chǎng)景,后面我們會(huì)使用 requests 庫對(duì)網(wǎng)頁的數(shù)據(jù)進(jìn)行手工爬取以比較和框架爬蟲之間的區(qū)別。后續(xù)都將會(huì)在 CentOS7.8 和 Python 3 的環(huán)境下:[store@server2 chap02]$ python3Python 3.6.8 (default, Apr 2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linuxType "help", "copyright", "credits" or "license" for more information.>>> import requests>>>Scrapy 百度百科接下來我們使用 requests 模塊的 get() 方法模擬 http 的 get 請(qǐng)求,獲取這樣的頁面結(jié)果:>>> headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'}>>> r1 = requests.get(url='https://baike.baidu.com/item/scrapy', headers=headers)>>> r1.status_code200>>> r1.text[:1000]'<!DOCTYPE html>\n<!--STATUS OK-->\n<html>\n\n\n\n<head>\n<meta charset="UTF-8">\n<meta http-equiv="X-UA-Compatible" content="IE=Edge" />\n<meta name="referrer" content="always" />\n<meta name="description" content="Scrapy是適用于Python的一個(gè)快速、高層次的屏幕抓取和web抓取框架,用于抓取web站點(diǎn)并從頁面中提取結(jié)構(gòu)化的數(shù)據(jù)。Scrapy用途廣泛,可以用于數(shù)據(jù)挖掘、監(jiān)測(cè)和自動(dòng)化測(cè)試。Scrapy吸引人的地方在于它是一個(gè)框架,任何人都可以根據(jù)需求方便的修改。它也提供了多種類型爬蟲的基類,如BaseSpider、sitemap爬蟲等,最新版本又提供了web2.0爬蟲的支持。...">\n<title>scrapy_百度百科</title>\n<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />\n<link rel="icon" sizes="any" mask >\n\n<meta name="keywords" content="scrapy scrapy基本功能 scrapyScrapy架構(gòu) scrapy如何開始">\n<meta name="image" content="https://bkssl.bdimg.com/cms/static/baike.png">\n<meta name="csrf-token" content="">\n<meta itemprop="dateUpdate" content="2020-03-19 08:23:19" />\n\n<!--[if lte IE 9]>\n<script>\r\n (function() {\r\n var e = "abbr,article,aside,audio,canvas,datalist,details,dialog,eventsource,figure,footer,header,hgroup,mark,menu,meter,nav,outpu注意:這里 headers 非常重要,很多網(wǎng)站第一步會(huì)檢查 headers,如果請(qǐng)求頭中沒有 User-Agent 就會(huì)直接判定為爬蟲并采取相應(yīng)措施進(jìn)行限制。如下是沒有加上 headers 的請(qǐng)求結(jié)果:沒有 headers 的結(jié)果看到了么,簡(jiǎn)簡(jiǎn)單單的 get() 方法就能模擬 HTTP 的 get 請(qǐng)求,那么是不是還有 post()、put()、delete() 這些方法呢?答案是肯定的。
路由是用來定義 RESTful Web API 不同接口所對(duì)應(yīng)的不同路徑地址。在本案例中,我們是要獲得學(xué)生的信息,根據(jù)第 3 節(jié)中介紹的設(shè)計(jì)規(guī)范,地址應(yīng)設(shè)計(jì)為:http://www.demo.com/api/students 。在 ProjectDemo 的 urls.py 中定義路由信息。from django.contrib import adminfrom django.urls import path, includefrom rest_framework import routersfrom AppDemo.views import StudentsViewSetrouter = routers.DefaultRouter() # 創(chuàng)建路由器router.register(r'students', StudentsViewSet) # 在路由器中注冊(cè)視圖集路由地址urlpatterns = [ # 拼接路由路徑 path('api/', include(router.urls)),]
業(yè)務(wù)碼不屬于 Http 協(xié)議的成員,是實(shí)踐中的產(chǎn)物。它是定義在返回的消息實(shí)體中的,并沒有固定的格式,但無非就是下面3種模塊?!惧e(cuò)誤級(jí)別(可選)】-【功能模塊(必要)】-【具體錯(cuò)誤編號(hào)(必要)】錯(cuò)誤碼一般由 5~6 位整數(shù)組成,例子如下:模塊模塊編碼錯(cuò)誤編碼描述庫存10001庫存不足庫存10002盤盈庫存10002盤虧資金20001參數(shù)不正確