第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定
3.3 第三部分 Response Messages

HTTP Status Code 列表示接口請求返回的狀態(tài)編碼,不同的編碼會對應不同的意義,和 Reason 列相對應:201 - 表示已建立接口連接,401 - 表示接口無權限訪問,403 - 表示接口訪問被禁止,404 - 表示在當前路徑下找不到該接口,接口無法返回信息。Response Model 列表示我們使用 @Response 注解定義的內(nèi)容,表示接口的響應策略,這里只需要知道表示什么意思就行,一般很少使用。Headers 列表示接口返回信息的響應頭類型,這里我們簡單了解就行。在左下角有一個 Try it out! 按鈕,該按鈕就是用來調試接口的。具體如何調試接口本節(jié)不做介紹,我會使用專門一節(jié)的內(nèi)容來介紹如何使用 Swagger-UI 進行接口調試。

3.2 Notification 完整示例

如果你安裝了慕課網(wǎng) App,那么一定收到過一些精彩課程的推送信息,接下來我們就一起通過 Notification 完成一個新課程的更新通知。3.2.1 編寫布局首先提供一個 Button 用于觸發(fā)新課程的推送,然后一個 Button 用于取消這條推送,為了樣式的美觀,我們在加上一條App的標題和內(nèi)容信息。整個的布局比較簡單,主要由 4 個元素組成,代碼如下:<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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="MainActivity"> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Android Study" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:textSize="30dp" /> <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="慕課網(wǎng)新課程通知" android:textColor="#ff87ff09" android:textSize="30dp" android:layout_below="@+id/tv_title" android:layout_centerHorizontal="true" android:layout_marginTop="48dp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="取消課程通知" android:id="@+id/bt_cancel" android:layout_marginTop="62dp" android:layout_below="@+id/bt_show" android:layout_centerHorizontal="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/bt_show" android:text="彈出課程通知" android:layout_below="@+id/tv_content" android:layout_centerHorizontal="true" android:layout_marginTop="42dp" /></RelativeLayout>樣式效果如下:3.2.2 編寫 MainActivity 邏輯MainActivity 里面要做的事也比較簡單,大體上只有兩件事:在點擊“彈出課程通知”的時候創(chuàng)建 Builder 并通過設置相應的屬性及提示信息,接著構造 Notification 并展示;點擊“取消課程通知”,則隱藏掉已經(jīng)彈出的課程通知。代碼如下:package com.emercy.myapplication;import android.app.Activity;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.Button;public class MainActivity extends Activity { NotificationManager mManager; private static final int NOTIFICATION_ID = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((Button)findViewById(R.id.bt_show)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addNotification(); } }); ((Button) findViewById(R.id.bt_cancel)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mManager != null) { mManager.cancel(NOTIFICATION_ID); } } }); } private void addNotification() { Notification.Builder builder = new Notification.Builder(this) .setSmallIcon(R.drawable.icon) .setContentTitle("慕課網(wǎng) Android 教程更新") .setContentText("狀態(tài)欄通知:Notification"); Intent notificationIntent = new Intent(this, NotificationActivity.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(contentIntent); mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mManager.notify(NOTIFICATION_ID, builder.build()); }}彈出的通知樣式如下:注意: 在代碼中我們?yōu)?Notification 設置了 PendingIntent,而 PendingIntent 是通過一個 Intent 構造出來的,通過這個 Intent 我們可以將 Notification 和 NotificationActivity 綁定上,從而實現(xiàn)點擊 Notification 跳轉到相應 Activity 的功能,頁面樣式如下:3.2.3 編寫 NotificationActivity 及其布局頁面當用戶在下拉通知菜單中點擊我們彈出的 Notification 后,就可以進入推送的新課程頁面了,這里就簡單放一個TextView表示新課程的內(nèi)容:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:layout_width="match_parent" android:layout_height="400dp" android:text="想要學習更多 Android 精彩內(nèi)容,請登錄慕課網(wǎng)官方網(wǎng)站,跟著超哥學 Android" /></LinearLayout>然后創(chuàng)建一個空白的 NotificationActivity,設置布局為以上文件:package com.emercy.myapplication;import android.os.Bundle;import android.app.Activity;public class NotificationActivity extends Activity{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.notification); }}在通知列表中點擊我們的 Notification 會跳轉到課程的詳情頁,也就是 NotificationActivity,如下:

4. Nginx中的負載均衡配置

Nginx 的 stream 模塊和 http 模塊分別支持四層和七層模塊的負載均衡。其用法和支持的負載均衡策略大致相同。首先使用 upstream 指令塊 和 server 指令指定上游的服務,upstream 指令的用法如下:Syntax: upstream name { ... }Default: —Context: http官網(wǎng)示例如下:upstream backend { server backend1.example.com weight=5; server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; server unix:/tmp/backend3; server backup1.example.com backup;}這里定義了4臺上游服務器,分別用域名, ip+port、socket 形式指定地址,后面跟上若干配置參數(shù)。默認情況下,upstream 指令塊中采用的是加權 Round-Robin 負載均衡算法。該算法通過加權輪詢的方式訪問 upstream 中 server 指令指定的上游服務。此時,在server 指令中我們可以添加一些關于服務的靜態(tài)配置,比如指定服務的權重(weight)、server 的最大并發(fā)連接數(shù)(max_conns)、max_fails 和 fail_timeout 等。除了默認的 Round-Robin 算法外,Nginx 中常用的負載均衡策略還有基于客戶端 ip 地址的 Hash 算法。該算法以客戶端的 ip 地址作為 hash 算法的關鍵字,映射到特定的上游服務器中,當然也可以根據(jù)客戶段的其他 key 來進行 hash 算法。涉及的配置指令為 ip_hash 和 hash,用法如下:Syntax: ip_hash;Default: —Context: upstreamSyntax: hash key [consistent];Default: —Context: upstream最后 Nginx 中一種常用的動態(tài)負載均衡算法是最少連接數(shù)算法。該算法會從所有的上游服務器中找到并發(fā)連接數(shù)最少的一個,然后將請求轉發(fā)給它,如果出現(xiàn)多個最少連接數(shù)的服務器,則會在這些最少連接數(shù)的服務器中繼續(xù)應用 Round-Robin 算法。配置該策略的指令為 least_conn,其指令格式如下:Syntax: least_conn;Default: —Context: upstream當然, Nginx 中的負載均衡策略還有很多,就不在此一一介紹了。可以仔細研讀官方文檔進行進一步學習

<a href="http://idcbgp.cn/wiki/djangolesson/usevirtualenv.html">3. 虛擬開發(fā)環(huán)境搭建</a>

虛擬開發(fā)環(huán)境可以說是這幾年 Python 項目開發(fā)的標配,在這一小節(jié)中會帶著大家搭建課程中用到的開發(fā)環(huán)境。并且介紹什么是 “虛擬環(huán)境”?為什么要使用虛擬環(huán)境,使用虛擬環(huán)境的好處有哪些?以及 Python 用來管理虛擬環(huán)境的工具是什么,通過本節(jié)課的學習我們可以熟練的使用工具來搭建自己的開發(fā)環(huán)境。

5. 小結

本小節(jié)對 Swagger 中的 ApiResponse 和 ApiResponses 注解,及其該注解中的常用屬性,做了詳細介紹,針對兩個注解中,經(jīng)常在實際項目開發(fā)中使用的屬性,采用圖文并茂的方式進行了重點介紹和應用剖析。Tips : 值得注意的是 @ApiResponse 注解一般不可以單獨拿來使用,需要搭配 @ApiResponses 注解一起來使用,這樣才能在 Swagger-ui 界面看到使用效果。在學習 ApiResponse 和 ApiResponses 注解及其常用屬性時,各位同學應該在清楚常見的 http 狀態(tài)碼及其描述都有哪些以及什么是接口的請求頭、響應頭的基礎上進行學習,因為這兩個注解都是針對接口的返回數(shù)據(jù)及其格式而言的,其他地方無法使用。

1.2 技巧二:擴展函數(shù)讓你的開發(fā)效率倍增

擴展函數(shù)可以說是 Kotlin 中最吸引開發(fā)者之一的語法特性,有了它代碼開發(fā)效率不僅僅一點點,此外它還能讓你的代碼變得更具有可維護性。關于擴展函數(shù)的定義就不展開了,之前文章中有。下面我會列出幾個場景,Kotlin 擴展如何讓你代碼變得更少更簡潔。場景一: ImageView 的擴展函數(shù) loadUrl這里以 ImageView 加載網(wǎng)絡圖片場景舉例假如有 3 個 Activity (可以擴展若干個 Activity) 都需要加載網(wǎng)絡圖片,也是 Android 開發(fā)中最為頻繁的場景。相信這樣加載圖片代碼寫法一定讓你值得收藏。(這里就以 Glide 圖片庫舉例)在 Java 中實現(xiàn) ImageView 加載網(wǎng)絡圖片://MainActivity加載圖片public class MainActivity extends AppCompatActivity { private ImageView mIvPoster; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mIvPoster = findViewById(R.id.movie_iv_poster); Glide.with(context).load("http://goo.gl/gEgYUd").into(mIvPoster); }} //Main2Activity加載圖片public class MainActivity extends AppCompatActivity { private ImageView mIvPoster; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); mIvPoster = findViewById(R.id.movie_iv_poster); Glide.with(context).load("http://goo.gl/gEgYUd").into(mIvPoster); }} //Main3Activity加載圖片public class MainActivity extends AppCompatActivity { private ImageView mIvPoster; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main3); mIvPoster = findViewById(R.id.movie_iv_poster); Glide.with(context).load("http://goo.gl/gEgYUd").into(mIvPoster);//雖然Glide可以做到很簡單也是一行代碼,但是每次都需要寫 Glide.with(context)這些代碼。 }} 想象下上述代碼如果很多 Activity 每次都需要編寫重復的代碼。此外還有一個問題就是比如后面圖片從 Glide 遷移到其他圖片庫,并且寫法上也和 Glide 一樣,這時候的重構只能每處使用 Glide 加載圖片地方都得修改,如果整個項目是大型項目各個業(yè)務到處都是 Glide 圖片寫法。就會造成重構成本加大。如果 Kotlin 的擴展函數(shù)不僅讓寫法更簡單,反而也能解決后續(xù)維護和遷移成本,那么是不是絕妙呢。使用 Kotlin 擴展函數(shù)實現(xiàn) ImageView 加載網(wǎng)絡圖片://先定義一個圖片加載的擴展函數(shù)fun ImageView.loadUrl(url: String) { Glide.with(context).load(url).into(this)//也是這么簡單的一行,但是僅僅需要寫這么一次就可以,后續(xù)無需再答復。}//MainActivity加載圖片class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) movie_iv_poster.loadUrl(""http://goo.gl/gEgYUd"")//僅僅需要這么一行,無論在哪個Activity都需要這么一行就能實現(xiàn)圖片加載就可以了。 //此外這樣代碼更具有可讀性,loadUrl就像是ImageView組件中自帶的函數(shù)。 }} //Main2Activity加載圖片class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) movie_iv_poster.loadUrl(""http://goo.gl/gEgYUd"") }} //Main3Activity加載圖片class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main3) movie_iv_poster.loadUrl(""http://goo.gl/gEgYUd"") }} 可以看到 Kotlin 的 ImageView 擴展函數(shù) loadUrl 更具可讀性,就像是 ImageView 組件中自帶加載網(wǎng)絡圖片的函數(shù)。此外及時后續(xù) Glide 圖片加載庫遷移了,上層業(yè)務代碼使用 loadUrl 地方都可以不用修改,保證了上層業(yè)務代碼接口穩(wěn)定性,只要修改 loadUrl 擴展函數(shù)內(nèi)部的實現(xiàn)即可,非常簡單。場景二: TextView 的擴展函數(shù) SpannableStringBuilder我們都知道 Android 開發(fā)中,經(jīng)常會利用 TextView 實現(xiàn)同一個文本中不同文字樣式標注,比如部分加粗、變顏色、變字體樣式、加下劃線等。遇到這樣的需求時我們可以很自然地想到使用 TextView 中的 SpannableStringBuilder 來實現(xiàn),但是相信大家一定對于 SpannableStringBuilder 實現(xiàn)代碼比較麻煩,每次都需要指定 star,end 然后就會去算字第幾個啥的,是不是很麻煩,看看 Kotlin 擴展函數(shù)如何助你一臂之力。在 Java 中實現(xiàn)以上場景 UI 效果:public class Main2Activity extends AppCompatActivity { private TextView mTvSpan; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); mTvSpan = findViewById(R.id.span_tv); SpannableStringBuilder sb = new SpannableStringBuilder(); String text1 = "我們發(fā)布了"; String text2 = "Jetpack Compose for Desktop"; String text3 = "第一個里程碑"; String text4 = "版本的正式版"; sb.append(text1); sb.append(text2); sb.append(text3); sb.append(text4); //以下代碼可以說是編寫Span代碼基本模板,寫起來也是比較麻煩的。 ForegroundColorSpan colorSpan1 = new ForegroundColorSpan(Color.parseColor("#333333")); StyleSpan styleSpan1 = new StyleSpan(Typeface.ITALIC); sb.setSpan(colorSpan1, 0, text1.length(), SpannableStringBuilder.SPAN_EXCLUSIVE_INCLUSIVE); sb.setSpan(styleSpan1, 0, text1.length(), SpannableStringBuilder.SPAN_EXCLUSIVE_INCLUSIVE); ForegroundColorSpan colorSpan2 = new ForegroundColorSpan(Color.parseColor("#ff9900")); StyleSpan styleSpan2 = new StyleSpan(Typeface.BOLD); sb.setSpan(colorSpan2, text1.length(), text1.length() + text2.length(), SpannableStringBuilder.SPAN_EXCLUSIVE_INCLUSIVE); sb.setSpan(styleSpan2, text1.length(), text1.length() + text2.length(), SpannableStringBuilder.SPAN_EXCLUSIVE_INCLUSIVE); UnderlineSpan underlineSpan = new UnderlineSpan(); sb.setSpan(underlineSpan, text1.length() + text2.length(), text1.length() + text2.length() + text3.length(), SpannableStringBuilder.SPAN_EXCLUSIVE_INCLUSIVE); ForegroundColorSpan colorSpan3 = new ForegroundColorSpan(Color.parseColor("#ff0000")); sb.setSpan(colorSpan3, text1.length() + text2.length() + text3.length(), text1.length() + text2.length() + text3.length() + text4.length(), SpannableStringBuilder.SPAN_EXCLUSIVE_INCLUSIVE); mTvSpan.setText(sb); }}上述 Java 實現(xiàn)上面效果大概需要近 45 行代碼,而且每次寫起來也比較麻煩,那么使用 Kotlin 擴展函數(shù)是不是更方便呢?一起來看下:Kotlin 擴展函數(shù)實現(xiàn)上述效果:class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //可以看到Kotlin屏蔽了具體start和end指定以及一些模板,僅僅需要核心代碼十幾行即可搞定,而且后續(xù)可持續(xù)復用 span_tv.text = buildSpannedString { inSpans(ForegroundColorSpan(Color.parseColor("#333333")), StyleSpan(Typeface.ITALIC)) { append("我們發(fā)布了") } inSpans(ForegroundColorSpan(Color.parseColor("#ff9900")), StyleSpan(Typeface.BOLD)) { append("Jetpack Compose for Desktop") } underline { append("第一個里程碑") } color(Color.parseColor("#ff0000")) { append("版本的正式版") } } }}下面給出 SpannbleStringBuilder 的擴展函數(shù):inline fun buildSpannedString(builderAction: SpannableStringBuilder.() -> Unit): SpannedString { val builder = SpannableStringBuilder() builder.builderAction() return SpannedString(builder)}inline fun SpannableStringBuilder.inSpans( vararg spans: Any, builderAction: SpannableStringBuilder.() -> Unit): SpannableStringBuilder { val start = length builderAction() for (span in spans) setSpan(span, start, length, Spannable.SPAN_INCLUSIVE_EXCLUSIVE) return this}inline fun SpannableStringBuilder.italic(builderAction: SpannableStringBuilder.() -> Unit) = inSpans(StyleSpan(Typeface.ITALIC), builderAction = builderAction)inline fun SpannableStringBuilder.underline(builderAction: SpannableStringBuilder.() -> Unit) = inSpans(UnderlineSpan(), builderAction = builderAction)inline fun SpannableStringBuilder.color( @ColorInt color: Int, builderAction: SpannableStringBuilder.() -> Unit) = inSpans(ForegroundColorSpan(color), builderAction = builderAction)其實關于擴展函數(shù)的場景還有很多很多,所以官方特地整理一個庫: androidx-core-ktx, 這里面收錄了 Android 中常用的 Kotlin 擴展函數(shù)。這里還給出一些常用的擴展函數(shù)://TextView加粗fun TextView.isBold() { this.paint.isFakeBoldText = true}//TextView設置左、上、右、下 圖標+文字的樣式,包括支持定義圖標的顏色fun TextView.setCompoundDrawablesKt( leftDrawable: Drawable? = null, topDrawable: Drawable? = null, rightDrawable: Drawable? = null, bottomDrawable: Drawable? = null, tintColor: Int = -1) { this.setCompoundDrawables(leftDrawable?.getTintDrawable(tintColor)?.apply { setBounds(0, 0, intrinsicWidth, intrinsicHeight) }, topDrawable?.getTintDrawable(tintColor)?.apply { setBounds(0, 0, intrinsicWidth, intrinsicHeight) }, rightDrawable?.getTintDrawable(tintColor)?.apply { setBounds(0, 0, intrinsicWidth, intrinsicHeight) }, bottomDrawable?.getTintDrawable(tintColor)?.apply { setBounds(0, 0, intrinsicWidth, intrinsicHeight) })}//設置View的Marginfun View.setMargins(leftMargin: Int = 0, topMargin: Int = 0, rightMargin: Int = 0, bottomMargin: Int = 0) { val layoutParams = (this.layoutParams ?: return) as? ViewGroup.MarginLayoutParams ?: return if(leftMargin > 0) { layoutParams.leftMargin = leftMargin } if(topMargin > 0) { layoutParams.topMargin = topMargin } if(rightMargin > 0) { layoutParams.rightMargin = rightMargin } if(bottomMargin > 0) { layoutParams.bottomMargin = bottomMargin } this.layoutParams = layoutParams}//常用的Context擴展,比如Toast以及設置顏色和Drawablefun Context.getColorCompat(@ColorRes color: Int): Int = ContextCompat.getColor(this, color)fun Context.getDrawableCompat(@DrawableRes drawable: Int): Drawable { return ContextCompat.getDrawable(this, drawable) ?: ColorDrawable()}fun Context.showToast(text: String, isShort: Boolean = true) { Toast.makeText(this, text, isShort.yes { Toast.LENGTH_SHORT }.otherwise { Toast.LENGTH_LONG }) .show()}

4.4 Selenium Web 應用程序測試

Selenium 是一個用于 Web 應用程序測試的工具,它使用 JavaScript 模擬真實用戶對瀏覽器進行操作。Selenium 支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。Selenium 支持使用 Python 語言編寫動作測試腳本,測試腳本執(zhí)行時,瀏覽器自動按照腳本代碼做出點擊、輸入、打開、驗證等操作,就像真實用戶所做的一樣,從終端用戶的角度測試應用程序。用戶使用 Python 編寫測試腳本:測試應用程序的瀏覽器兼容性,驗證應用程序是否能夠工作在不同瀏覽器和操作系統(tǒng)之上創(chuàng)建回歸測試檢驗軟件功能和用戶需求Selenium 主要用于測試 Web 應用程序,學習 web 自動化測試前,需要補充 Web 相關的知識,包括:HTTP 協(xié)議HTML 的基礎知識,如何使用 Javascript 操縱 DOMFirebug 或者 Chrome 開發(fā)者工具的使用,用于抓取元素

2.1 簡介

Flask 程序可以運行在 Debug 模式下,Debug 模式提供了如下功能:當 Flask 程序出現(xiàn)錯誤時,在瀏覽器中提示錯誤的詳細信息修改 Flask 程序代碼后,F(xiàn)lask 程序會重新加載,不需要重啟 Flask 程序,即可在瀏覽器中看到修改后的效果調用 Flask 應用的 run 方法時,設置參數(shù) Debug = True,啟動 Flask 程序的調試模式。編寫程序 debug-on.py 如下:from flask import Flaskapp = Flask(__name__)if __name__ == '__main__': app.run(debug = True)在第 5 行,app.run(debug = True),啟動 Flask 程序的調試模式。運行程序,輸出如下:$ python3 debug.py * Serving Flask app "debug" (lazy loading) * Environment: production * Debug mode: on * Restarting with stat * Debugger is active! * Debugger PIN: 316-471-540 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)在第 4 行,Debug mode: on,表示 Flask 程序已經(jīng)進入了調試模式。

1. 前言

在響應式布局這一領域內(nèi),grid 布局簡直有著當仁不讓的天生優(yōu)勢,即使是chinese-layout的底層也依賴的是 grid 布局,grid 可在短短幾行代碼之中就快速實現(xiàn)出各種布局。當然,各種布局肯定也包括響應式,不過由于 grid 布局較為復雜,一言難盡,所以在這里貼上兩個較為流行的 grid 入門教程地址:阮一峰博客:http://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html張鑫旭博客:https://www.zhangxinxu.com/wordpress/2018/11/display-grid-css-css3/很多人擔心Grid的兼容性:其實可以看到絕大部分瀏覽器都已經(jīng)支持了,即使是最被吐槽的IE瀏覽器,也可以通過增加-ms-前綴來進行支持,如:display: -ms-grid;

3. 小結

處理 Ajax 請求,我們應該在適當?shù)臅r機進行處理。我們應該在 xhr.readyState == 4 ,并且 xhr.status === 200 || xhr.status === 304 的時候正確獲取響應的內(nèi)容;XMLHttpRequest.readyState 體現(xiàn)著當前請求以及服務端響應的狀態(tài);XMLHttpRequest.status 即 XMLHttpRequest 響應中的數(shù)字狀態(tài)碼。這個數(shù)字狀態(tài)碼是一個無符號短整型狀態(tài)碼,代表著我們的 Ajax 請求的狀態(tài)成功與否;HTTP 狀態(tài)碼有很多,包括 404、 500 等,每一個包含著不一樣的含義;獲取服務器響應內(nèi)容,我們可以使用 responseText 、 responseXML 和 response 。其中,responseText 返回一個純文本的值,responseXML 返回一個包含請求檢索的 HTML 和 XML 的 Document,而 response 返回響應正文。返回類型可以有 DOMString、 Blob 、ArrayBuffer 、Document 或 JavaScript Object ,這取決于 responseType。

2.2 @CookieValue

功能描述: @CookieValue 用來自動綁定請求包中的 Cookie 值。Cookie 一般由 WEB 應用程序在服務器端創(chuàng)建,通過響應包傳遞給瀏覽器,并可以在瀏覽器端以文件的形式存儲。在后續(xù)請求過程中,請求包又可以攜帶此 Cookie 返回給服務器。Cookie 所能保存的數(shù)據(jù)量有限且只能是字符類型,但其應用場景較多,如 WEB 程序中的 Session 組件的狀態(tài)維護、用戶免輸入登錄、歷史記錄顯示等功能都可以通過 Cookie 實現(xiàn)。如果要獲取請求包中的 Cookie 中的值,控制器中只需要如下編碼便可:@RequestMapping(value="/sessionId")public String getSessionId(@CookieValue("JSESSIONID") String sessionId){}當請求格式類似于 http://localhost:8888/saveUser?sessionId 時,getSessionId()方法中的 sessionId 參數(shù)會被注入 Cookie 中所攜帶的 JSESSIONID 的值。只要指定 Cookie 的名稱,@CookieValue 便能自動獲取此 Cookie 的值。

4. POST 路由定義

4.1 首先在 app\controller 目錄下新建目錄 Study 目錄表示學習項目的目錄空間,如下圖所示:4.2新建一個 StudyController 的類,并且繼承 app\BaseController 類:<?phpnamespace app\controller\Study;use app\BaseController;class StudyController extends BaseController{}如下圖所示:4.3 在 StudyController 類中新建一個 add 方法用于測試 post 請求方式function add(){ echo "這里是post請求方式測試方法";}4.4 在 route 目錄下新建一個屬于路由文件,這個文件名可自定義,這里取名 study.php ,并在文件中引入 think\facade\Route 類,定義一個 POST 請求方式的路由:Route::post('imooc','app\controller\StudyController@postInfo');如下圖所示:Tips: 其中 Route::post 表示路由請求方式為 post 方式,study 表示請求域名后面跟上的路由名稱,如 http://tp6.com/study,其中 app\controller\Study\StudyController@add 表示該路由指向的控制器方法名的路徑。4.5 通過 postman 軟件工具請求:

1.2 端口映射訪問容器

將宿主機的本地端口,與指定容器的服務端口進行映射綁定,之后訪問宿主機端口時,會將請求自動轉發(fā)到容器的端口上,實現(xiàn)外部對容器內(nèi)網(wǎng)絡服務的訪問。創(chuàng)建名為 n0 的 nginx 容器,映射宿主機 8000 端口到它的 80 端口docker run -d -t -p 8000:80 --name n0 nginxTips:指定的宿主機端口必須是未被占用的端口,否則操作會失敗,且生成一個無法正常啟動的容器 n0, 需要手動刪除。使用 docker port n0 查看 n0 的端口映射信息,顯示如下:80/tcp -> 0.0.0.0:8000打開瀏覽器,地址欄輸入 http://localhost:8000 或 http:// 宿主機 IP:8000, 都能訪問到 n0 的 nginx 服務。如果需要綁定多個容器端口,可以連續(xù)使用 -p 參數(shù)多次指定docker run -d -t -p 8001:80 -p 8433:443 --name n1 ngin如果不想主動指定宿主機端口,可以使用 -P 參數(shù),宿主機隨機使用一個可用端口與容器端口進行映射docker run -d -t -P --name n2 nginx如果只想使用宿主機上特定的網(wǎng)口與容器進行映射docker run -d -t -p 192.168.1.13:8002:80 --name n3 nginxTips:此處 192.168.1.13 指代 宿主機映射網(wǎng)口的 IP 地址,需要根據(jù)網(wǎng)口的實際 IP 更改 *。我們執(zhí)行 docker ps 可能出現(xiàn)如下幾個的 nginx 容器:再執(zhí)行 iptables -t nat -nL 查看下防火墻:比對上面兩個的輸出,不難發(fā)現(xiàn),這種端口轉發(fā)方式的本質是通過配置 iptables 規(guī)則轉發(fā)實現(xiàn)的,效率較低,如果容器的服務端口數(shù)量過多,需要配置較多的映射,占用大量宿主機端口,也不便于管理。不再使用的容器記得刪除掉,釋放資源和空間docker rm -f n0 n1 n2 n3

4. 選擇請求方法

默認情況下,Postman 會為新請求選擇 GET 方法。API 請求用的都是 HTTP 方法。 最常見的方法包括 GET、POST、PATCH、PUT和 DELETE。GET 方法從 API 獲取數(shù)據(jù);POST 方法向 API 發(fā)送添加新數(shù)據(jù);PATCH 和 PUT 方法用來更新數(shù)據(jù);DELETE 方法用來刪除已存在的數(shù)據(jù);除了這些 Postman 默認支持的請求方法,你還可以自定義請求方法。單擊方法下拉列表 -> 編輯方法名稱 -> 保存新方法。要刪除一個方法,請將鼠標懸停在列表中的方法上,然后選擇“垃圾桶”圖標。如果請求不需要參數(shù)或身份驗證,可以繼續(xù)單擊“發(fā)送”請求以獲取響應;否則,指定 API 的參數(shù)和主體數(shù)據(jù);如果需要,設置所需的身份驗證和請求頭。

1. Httpd 簡介

Httpd 是 C 語言編寫的遵從 Http 協(xié)議的服務器,是一個高度模塊化軟件,由 Server 和 Module 組成。這些模塊大都是動態(tài)模塊,因此可以隨時加載。源碼開源地址:https://github.com/apache/httpd ;官網(wǎng)地址:https://httpd.apache.org;Httpd 作為起步比較早的一個 Web 開源項目,代碼的穩(wěn)定性/社區(qū)/文檔 都是比較可靠的,他支持的功能非常豐富,并且可以按需地引入自己所需要的模塊。Httpd 一般比較的對象是 Nginx 服務器,他們兩個是靜態(tài)資源服務器的首選:Nginx 輕量且并發(fā)能力高于 Httpd;Nginx 能夠實現(xiàn)負載均衡;Httpd 支持的功能模塊比較豐富;Httpd 的 rewrite 功能強于 Nginx。當然,也有的網(wǎng)站架構同時用到了 Nginx 和 Httpd ,用 Nginx 作為負載均衡,將流量分發(fā)到后面的 Httpd Web服務端。

2.1 作用于 TableLayout 的屬性

android:stretchColumns:當表格的某些列寬度比較窄的時候,你可以通過給 TableLayout 設置stretchColumns屬性來設置某些列向行方向伸展,最多可占據(jù)一整行。該屬性的參數(shù)直接填入需要拉伸的列的序號即可(從 0 開始),另外支持多輸入,各個列序號通過“,”分隔。如果需要對所有列拉伸,可以直接用“*”表示,如下:<!-- 針對所有列做拉伸 -->android:stretchColumns="*"<!-- 針對第1列、4列、13列做拉伸 -->android:stretchColumns="0,3,12"android:shrinkColumns:這是與 stretchColumns 相對的屬性,如果某些列的所占的空間太大,那么可以通過該屬性設置可收縮的列。當該列子控件的內(nèi)容太多(比如 TextView 中文本太長),已經(jīng)擠滿所在行,那么該子控件的內(nèi)容將往列方向顯示(TextView 折行顯示)。注:雖然從名字上講,一個是擴展,一個是收縮,但二者并不沖突。一個列可以同時具備stretchColumns及shrinkColumns屬性。若此,那么當該列的內(nèi)容過多時,將“多行”顯示其內(nèi)容。當然這里不是真正的多行,而是系統(tǒng)根據(jù)需要自動調節(jié)該行的 layout_height。android:collapseColumns:隱藏部分列的內(nèi)容,使用方法和上述兩個屬性一樣,直接填入列的序號即可,支持多個列。這 3 個屬性的用法示例如下:<?xml version="1.0" encoding="utf-8"?><TableLayout 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:shrinkColumns="0" android:stretchColumns="1,2" android:collapseColumns="3" tools:context=".MainActivity"> <TableRow> <TextView android:id="@+id/center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#EE0D0D" android:text="column1, Emercy Android Study" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#DBEE14" android:text="column2" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#7492CC" android:text="column3" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#09A234" android:text="column4" android:textSize="20sp" /> </TableRow> <TableRow> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#FF80AB" android:text="row2" android:textSize="20sp" /> </TableRow></TableLayout>示例中定義另一個兩行的表格,根據(jù)元素最多的行可以計算出表格的列數(shù),然后設置了shrinkColumns為第一行,stretchColumns為第二、三行,最后collapseColumns為最后一行,最終顯示的效果如下:

1.3 無法保證數(shù)據(jù)完整性

客戶端 A 像服務端 B 充值 100。A 的網(wǎng)絡報文被黑客竊取了,修改了中間的金額,后面繼續(xù)把報文傳向 B,此時就造成了嚴重的金額損失。上面的例子如果沒有做數(shù)據(jù)完整性校驗就很容易被攻陷。當然很多系統(tǒng)即使用 Http 協(xié)議,也可以自己在業(yè)務層參數(shù)中定義簽名信息,實現(xiàn)自己的簽名算法保障數(shù)據(jù)的完整性。signature = MD5(privateKey + MD5( 參數(shù)1 + 參數(shù)2 + 參數(shù)2 +...))簽名算法有很多種,系統(tǒng)的簽名算法是機密性的信息,不能對外透漏。例如上面的算法中,將請求的所有參數(shù)依次相加然后做 MD5 運算,取得的值再與約定的一個私密 key 再進行一次 MD5,最后生成一個簽名 signature。這個算法客戶端和服務端都是知道的,所以服務端接收到請求后,就拿著獲取到的參數(shù)做執(zhí)行一遍通用的算法,然后將生成的簽名與客戶端傳過來的簽名做比較,相同的話就證明請求數(shù)據(jù)沒有被人改動過。但是這邊有個問題,瀏覽器上面的 Js 代碼能容易被查看到,即使加密也很容易破解,我們的算法和私鑰都很容易泄漏。如果是安卓或者IOS程序,亦或是2個后臺服務的通信,這種情況算法用戶是看不到,但是軟件開發(fā)是一個團隊協(xié)作的工作,一般負責的開發(fā)人員都知道,可靠性就沒辦法太高。相比,Https 的簽名算法是公開的,他生成的簽名算法還會用公鑰去加密。同樣,如果 Http 也要整這么麻煩也是可以的,只是需要在自己的業(yè)務邏輯中去實現(xiàn),而 Https 借助的是安全套接字層,對應用層完全透明,我們的應用不需要多做什么就可以實現(xiàn)可靠性傳輸了。

5. 自定義配置項

我們還可以在配置文件中使用自定義配置,例如我們開發(fā)了一個微信公眾號后臺應用,需要在程序中配置公眾號的 appid 和 secret 。配置文件如下:實例:# 公眾號appidwxmp.appid=111# 公眾號secretwxmp.secret=222我們定義一個組件,通過 @Value 注解注入配置項的值。實例:/** * 微信公眾號參數(shù) */@Component//注冊為組件public class WxMpParam { @Value("${wxmp.appid}")//注入wxmp.appid配置項 private String appid; @Value("${wxmp.secret}")//注入wxmp.secret配置項 private String secret; //省略get set方法}通過控制器測試配置項是否注入成功。實例:@RestController public class HelloController { @Autowired private WxMpParam wxMpParam; @GetMapping("/hello") public Map hello() { Map<String, String> map = new HashMap<String, String>(); map.put("appid",wxMpParam.getAppid()); map.put("secret",wxMpParam.getSecret()); return map; }}此時我們訪問 http://127.0.0.1:8000/spring-boot-profile/hello ,瀏覽器顯示如下,說明我們的配置注入成功。瀏覽器顯示返回數(shù)據(jù)

3.1 布局文件的編寫

首先布局文件主要包括 3 個部分:Button——用于點擊回到頂部;Button——用于點擊跳轉底部;ScrollView——包含過多的子 View,支持滾動。然后我們將兩個用于跳轉的 Button 放入一個 LinearLayout 中,和 ScrollView 同層,這樣兩個 Button 就不會響應 ScrollView 的滑動,然后在 ScrollView 中添加一個 LinearLayout 用來管理需要滑動的 n 個 View,布局文件如下:<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="30dp"> <LinearLayout android:id="@+id/button_group" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="80dp" android:text="ScrollView" android:textSize="25dp" android:textStyle="bold" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="20dp" android:text="Welcome to Imooc Android" /> <!-- 在Java代碼中動態(tài)添加若干個Button,超出屏幕范圍即可滑動 --> </LinearLayout> </ScrollView> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <Button android:id="@+id/bt_to_top" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="滾動到頂部" /> <Button android:id="@+id/bt_to_bottom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="跳轉到底部" /> </LinearLayout></FrameLayout>

4. tcp/udp 配置初步

Nginx 從 1.9.0 版本開始,新增加了一個 stream 模塊,用來實現(xiàn)四層協(xié)議的轉發(fā)、代理或者負載均衡等。這個模塊使用和 http 指令塊類似。我們同樣在之前的配置準備一個 nginx.conf 文件,里面只有一個 stream 的指令塊,如下:user root;worker_processes 2;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;events { worker_connections 1024;}stream { server { listen 3000; return '3000 server get ip: $remote_addr!\n'; }}上述指令塊只有一個 stream 塊,監(jiān)聽了 3000 端口。對于 tcp 連接,可以使用 proxy_pass 轉發(fā) tcp/udp 協(xié)議。也可以直接使用 return 指令返回,這里只是簡單返回相應字符串。測試結果在 Windows 下,打開命令窗口,然后輸入telnet 180.76.152.113 3000 命令,正常應該會有相應的字符串響應。如果出現(xiàn)下面的錯誤,需要在Window 中打開 telnet 客戶端,具體操作見下圖。解決找不到 telnet 命令方法 1在這里選擇勾上 Telnet 客戶端即可。最后 telent 命令返回結果:

1.2 Nexus 安裝

接下來,我們來安裝 Nexus 。首先,我們可以去 Sonatype 的官網(wǎng)上下載對應的 Nexus 版本。不過這里還是要吐槽一下這個地址的訪問速度非常慢。這里,我們使用的是nexus-2.11.2-03-bundle.tar.gz版本。首先,我們將安裝包上傳到服務器的對應目錄中;我們將安裝包進行解壓,執(zhí)行命令tar -xvf nexus-2.11.2-03-bundle.tar.gz,解壓后,對 nexus-2.11.2-03 目錄進行重命名 mv nexus-2.11.2-03 nexus;編輯環(huán)境變量,vi /etc/profile,在文件的最后,根據(jù)自己服務器的情況,添加如下的配置;進入到 bin 目錄中,cd /usr/local/src/nexus/bin;在 bin 目錄中,有 Nexus 的可執(zhí)行文件,我們來執(zhí)行 nohup ./nexus start &,來啟動 Nexus,啟動成功后,在瀏覽器中,輸入http://ip:port/nexus可以瀏覽私服的網(wǎng)址;至此,我們就安裝好了 Nexus。

7.4 創(chuàng)建商品頁面

手工添加 src/main/webapp 及子目錄如下,同時目錄下放一個 goods.jsp 用于測試。注意該目錄是一個 Source Folder 源代碼目錄,不是普通文件夾目錄。spring-boot-jsp 項目結構實例:<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>商品列表</title></head><body>商品列表</body></html>注意,我們還需要添加一個視圖解析器,實現(xiàn) JSP 頁面往指定目錄跳轉。實例:@SpringBootApplicationpublic class SpringBootJspApplication { public static void main(String[] args) { SpringApplication.run(SpringBootJspApplication.class, args); } @Bean // 注冊視圖解析器 public InternalResourceViewResolver setupViewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/jsp/");// 自動添加前綴 resolver.setSuffix(".jsp");// 自動添加后綴 return resolver; }}此時我們啟動項目,然后訪問 http://127.0.0.1:8080/goods ,即可顯示對應頁面內(nèi)容。

5.2 WebSocket 業(yè)務類

public class MyWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{ private SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //1.獲取Channel通道 final Channel channel=ctx.channel(); //2.創(chuàng)建一個定時線程池 ScheduledExecutorService ses=Executors.newScheduledThreadPool(1); //3.一秒鐘之后只需,并且每隔5秒往瀏覽器發(fā)送數(shù)據(jù) ses.scheduleWithFixedDelay(new Runnable() { public void run() { String sendTime=format.format(new Date()); channel.writeAndFlush(new TextWebSocketFrame("推送時間=" + sendTime)); } },1,5, TimeUnit.SECONDS); } //接受瀏覽器消息 @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("收到消息 " + msg.text()); } //當web客戶端連接后,觸發(fā)方法 @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } //當web客戶端斷開后,觸發(fā)方法 @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { }}代碼說明:其實 WebSocket 對于的 Handler 跟我們普通業(yè)務的 Handler 沒有什么區(qū)別,這里主要使用定時線程池定時往瀏覽器推送消息,這個是傳統(tǒng)的 Http+Ajax 請求無法實現(xiàn)的逆向推送效果。

2. 安裝 CUDA 工具

首先我們需要像 Windows 安裝一樣,找到自己的 CUDA 版本。cat /usr/local/cuda/version.txtTips:不同用戶的驅動的安裝位置不一樣,這只是默認的安裝路徑。然后根據(jù)自己的 CUDA Driver 的版本,找到適合自己 CUDA Driver 版本的 CUDA 工具包,然后我們便可以運行以下命令來進行安裝。wget http://developer.download.nvidia.com/compute/cuda/11.0.2/local_installers/cuda_11.0.2_450.51.05_linux.runsudo sh cuda_11.0.2_450.51.05_linux.run這只是安裝的一種方式,其實官方網(wǎng)站給我們提供了非常完備的安裝方案,每當我們選擇一個版本與安裝方式之后,官方便會給我們詳細的安裝指引,從而指引我們完成安裝。配置環(huán)境變量:export PATH=/usr/local/cuda-11.0/bin:$PATHexport LD_LIBRARY_PATH=/usr/local/cuda-11.0/lib64:$LD_LIBRARY_PATH當然,這里面的路徑是你自己的 CUDA 的安裝路徑。

2.2 背景色

Markdown 文檔中定義文字背景色需要通過修改 style 樣式實現(xiàn)。實例 3:#### 使用 `style` 屬性修改文字的背景色<font style="background: red">紅色</font><font style="background: green">綠色</font><font style="background: blue">藍色</font><font style="background: rgb(200,100,100)">使用 rgb 顏色值</font><font style="background: #FF00BB">使用十六進制顏色值</font>其渲染結果如下:實例 4:利用 style 的豐富樣式,我們可以定義出豐富的文字形式。#### 更豐富背景樣式## <font style="background: url('http://www.wenliku.com/d/file/patterns/2019-06-26/d8fac26c38c9b2a7e2393fc9af766e8f.jpg') ">I wish you a Merry Christmas</font>使用圖片作背景## <font style="background: linear-gradient( to right, #ff1616, #ff7716, #ffdc16, #36c945, #10a5ce, #0f0096, #a51eff, #ff1616);">太陽太陽,給我們帶來,七色光彩</font>漸變背景色其渲染結果如下:

第一步 安裝 HaProxy 組件

在集成 HaProxy 組件之前,我們需要在自己的機器上下載并安裝 HaProxy 組件,可以通過以下命令實現(xiàn):wget http://www.haproxy.org/download/1.6/src/haproxy-1.6.5.tar.gz下載完成之后,我們會得到一個壓縮包,接著,我們需要將該壓縮包進行解壓,解壓命令如下:tar -zxvf haproxy-1.6.5.tar.gz 接著,我們需要在 HaProxy 組件安裝目錄下,執(zhí)行以下命令:make TARGET=linux31 make install經(jīng)過上述安裝命令之后,如果沒有提示任何錯誤,則表明 HaProxy 組件已經(jīng)安裝成功了。Tips: 如果在安裝 HaProxy 過程中,提示缺少 gcc 編譯環(huán)境,這就表明我們的機器上沒有安裝 gcc 編譯環(huán)境,HaProxy 組件無法繼續(xù)進行安裝,我們只需要將 gcc 編譯環(huán)境安裝上去就可以了: yum install gcc 。

4. RabbitMQ 安裝成功的必要性測試

通過 HomeBrew 將 RabbitMQ 安裝到自己的 Mac 電腦中后,我們需要來對已經(jīng)安裝的 RabbitMQ 做一個驗證,以檢測我們的 RabbitMQ 是否安裝成功,且可以正常使用了。驗證的方法很簡單,我們需要在命令行啟動 RabbitMQ 服務,命令如下:rabbitmq-server輸入以上命令之后,看到命令行如下圖所示的提示,則表明 RabbitMQ 服務啟動成功了:然后我們在本機瀏覽器中,訪問以下地址,如果可以打開 RabbitMQ 的管控臺界面,說明我們的 RabbitMQ 服務已經(jīng)成功安裝到了我們的 Mac 電腦中,且可以愉快的玩耍了。http://localhost:15672Tips: RabbitMQ 自帶的默認管控臺登錄用戶和密碼均為 guest ,同學們?nèi)绻信d趣可以使用 guest 登錄進去,看看里面的世界是什么樣的。

1.3 Scrapy 中內(nèi)置的 Spider 中間件

接下來,我們來看看 Scrapy 中內(nèi)置的 Spider 中間件,目前最新的 Scrapy-2.2.0 中一共有 5 個內(nèi)置的 Spider 中間件:Scrapy內(nèi)置的Spider中間件我們來分別介紹下這 5 個內(nèi)置的 Spider 中間件,如果能利用好這些中間件以及進行合理的配置,可以簡化不少代碼和提高網(wǎng)站爬取的成功率和效率。DepthMiddleware爬取深度中間件,該中間件用于追蹤被爬取網(wǎng)站中每個 Request 的爬取深度。通過對該中間件相關參數(shù)的設置,可以限制爬蟲爬取的最大深度,且可以根據(jù)深度控制請求的優(yōu)先級等。該中間件對應的參數(shù)設置有:DEPTH_LIMiT:允許爬取的最大深度,如果為0,則不限制;DEPTH_STATS:是否收集爬取深度統(tǒng)計數(shù)據(jù);DEPTH_PRIORITY:是否根據(jù) Request 深度對其安排相應的優(yōu)先級進行處理。HttpErrorMiddleware該中間件的作用是過濾掉所有不成功的 HTTP 響應,但這會增加開銷,消耗更多的資源,并使 Spider 邏輯更加復雜;根據(jù) HTTP 標準,響應碼在 200~300 之間都是成功的響應。如果想處理這個范圍之外的 Response,可以通過 Spider 的 handle_httpstatus_list 屬性值或者配置文件中的 HTTPERROR_ALLOWED_CODES 值來指定 Spider 能處理的 Response 狀態(tài)碼:class MySpider(CrawlSpider): handle_httpstatus_list = [404]例如上面這樣的寫法就是只處理響應碼為 404 的 Response。此外 HttpErrorMiddleware 中間件的配置值有兩個,分別為:HTTPERROR_ALLOWED_CODES:默認值為[],傳遞此列表中包含的非200狀態(tài)代碼的所有響應;HTTPERROR_ALLOW_ALL:默認為 False,傳遞所有的 Response而不考慮其狀態(tài)響應碼的值。OffsiteMiddleware過濾請求中間件。用于過濾掉所有 Spider 覆蓋主機域名外的 URL 請求;RefererMiddleware位置參考中間件。它的作用是根據(jù)生產(chǎn)的 Response 的 URL 來填充 Request Referer 信息;UrlLengthMiddleware網(wǎng)址長度限制中間件。它用于過濾 URL 長度大于 URLLENGTH_LIMIT 的值得 Request;在 scrapy/settings/default_settings.py 中我們可以看到這樣的配置:SPIDER_MIDDLEWARES_BASE = { # Engine side 'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50, 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500, 'scrapy.spidermiddlewares.referer.RefererMiddleware': 700, 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800, 'scrapy.spidermiddlewares.depth.DepthMiddleware': 900, # Spider side}這些 Spider 中間件都默認啟用,且從 Engine 端到 Spider 端的順序如上配置所示。如果想禁止某個內(nèi)置的 Spider 中間件,我們直接在 settings.py 文件中將該 Spider 的值設置為 None 即可,示例如下:# 配置位置: 爬蟲項目/settings.pySPIDER_MIDDLEWARES = { 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,}

5. 前端頁面開發(fā)

本節(jié)主要介紹 Spring Boot 中 JdbcTemplate 的用法,所以前端頁面僅給出代碼和注釋,不再進行詳細介紹了。前端只有一個頁面,使用 Bootstrap 的樣式和插件,通過 jQuery 的 $.ajax 方法訪問后端接口,邏輯并不復雜。此處簡單展示下瀏覽商品部分的前端代碼,感興趣的同學可以從 Git倉庫 查看完整代碼。實例: //瀏覽商品 function viewGoods() { var row = ""; //先清空表格 $('#GoodsTable').find("tr:gt(0)").remove(); $.ajax({ type: "GET", url: "http://127.0.0.1:8080/goods", dataType: "json", contentType: "application/json; charset=utf-8", success: function (res) { console.log(res); $.each(res, function (i, v) { row = "<tr>"; row += "<td>" + v.id + "</td>"; row += "<td>" + v.name + "</td>"; row += "<td>" + v.price + "</td>"; row += "<td>" + v.pic + "</td>"; row += "<td><a class='btn btn-primary btn-sm' href='javascript:editGoods(" + v.id + ")' >編輯</a>"; row += "<a class='btn btn-danger btn-sm' href='javascript:removeGoods(" + v.id + ")' >刪除</a></td>"; row += "</tr>"; console.log(row); $("#GoodsTable").append(row); }); }, error: function (err) { console.log(err); } }); }

2. 發(fā)送 HTML 格式郵件

發(fā)送郵件如下代碼所示:import smtplibfrom email.mime.text import MIMETexthost_server = 'smtp.qq.com' # 主機地址# 發(fā)件人郵箱sender = "xxx@qq.com"# 發(fā)件人郵箱密碼、授權碼code = "xlogucqphohxcabi"# 收件人user = "xxxx@163.com"# 準備郵件數(shù)據(jù)# 郵件標題mail_title = "第二封郵件"# 內(nèi)容mail_content = """<p>HTML格式郵件內(nèi)容</p><hr/><p><a >百度一下</a></p><ul> <li>top1</li> <li>top2</li></ul>"""# SMTPsmtp = smtplib.SMTP(host_server)# 登錄smtp.login(sender, code)# 發(fā)送msg = MIMEText(mail_content, 'html', 'utf-8')msg['Subject'] = mail_titlemsg['From'] = sendermsg['To'] = usersmtp.sendmail(sender, user, msg.as_string())代碼解釋:在上一小發(fā)送普通郵件代碼的基礎上,將發(fā)送內(nèi)容由普通文本變更為 HTML 格式內(nèi)容,首先修改 mail_content 設置為 HTML 內(nèi)容,修改 MIMEText 構建參數(shù)為 html,其他內(nèi)容不變。執(zhí)行完成后,打開收件郵件即可收到第二封郵件,如下圖所示。

直播
查看課程詳情
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網(wǎng)微信公眾號