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

為了賬號安全,請及時綁定郵箱和手機立即綁定
3. 注意事項

如果在 Marketplace 中搜索不到任何插件時,可以嘗試下面的操作。點擊 File -> Settings -> Appearance & Behavior -> System Settings -> Updates,在出現(xiàn)的頁面中將Use secure connection 這一項去掉勾選。然后選擇 HTTP Proxy,選中 Auto-detect proxy settings。出現(xiàn)上述情況的原因, 是因為 PyCharm 會在 plugin 搜索插件時檢查網絡, 所以要關閉網絡檢查。另外如果機器設置了代理,也需要把自動監(jiān)控代理設置的選項選中,總之,這與你的工作的網絡環(huán)境是相關的。如果仍然不能正常搜索到插件,就去官網下載插件到本地,然后導入安裝。

4.3 設置 Cookie

@app.route('/set_cookie')def set_cookie(): html = render_template('js_cookie.html') response = Response(html) response.set_cookie('mooc', 'idcbgp.cn') return response設置訪問頁面 /set_cookie 的處理函數(shù) set_cookie。在第 3 行,首先獲取頁面模板 js_cookie.html 的內容;在第 4 行,通過類 Response 創(chuàng)建一個 response 對象;在第 5 行,方法 respose.set_cookie () 產生一個 HTTP 的 Set-Cookie 消息頭;在第 6 行,頁面處理函數(shù)返回 response 對象,該對象包含有 Set-Cookie 消息頭。Flask 框架將 response 對象發(fā)送給客戶端,在返回給瀏覽器的響應中,包含有 Set-Cookie 消息頭,瀏覽器收到這個消息頭后,會在本地存儲 Cookie 的值。因此,在 templates/js_cookie.html 文件中,可以通過 Javascript 讀取 Cookie 的值。

3. RESTful 風格后端接口

前后端分離時,后端接口可不能太隨意,目前后端接口編寫大多遵循 RESTful 風格。做后端接口的公司這么多,如果大家都定義自己的規(guī)范,是不利于公司之間的合作的。如果大家都能遵循一個規(guī)范來開發(fā)接口,很明顯相關人員都能省心不少。RESTful 就是一種非常流行的 HTTP 接口規(guī)范,簡單明了,使用的人也多,用它準沒錯。規(guī)范的意義,就是提供標準,提高效率。汽車行業(yè)標準化程度已經很高了,軟件行業(yè)還需努力?。▓D片來源于網絡,版權歸原作者所有)

2.5 CGI 的 Sessions

CGI::Session可以保存為用戶和 CGI 環(huán)境的持久會話狀態(tài),在需要關閉會話后,因此可以確保將數(shù)據(jù)寫入內存,當會話結束時,您需要刪除數(shù)據(jù)。#!/usr/bin/rubyrequire 'cgi'require 'cgi/session'cgi = CGI.new("html4")sess = CGI::Session.new( cgi, "session_key" => "a_test", "prefix" => "rubysess.")lastaccess = sess["lastaccess"].to_ssess["lastaccess"] = Time.nowif cgi['bgcolor'][0] =~ /[a-z]/ sess["bgcolor"] = cgi['bgcolor']endcgi.out{ cgi.html { cgi.body("bgcolor" => sess["bgcolor"]) { "The background of this page" + "changes based on the 'bgcolor'" + "each user has in session." + "Last access time: #{lastaccess}" } }}# ---- 輸出結果 ----Content-Type: text/htmlContent-Length: 216Set-Cookie: a_test=8239f376c9e2cb2b77a0512bb3b8fa0b; path=<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><HTML><BODY>The background of this pagechanges based on the 'bgcolor'each user has in session.Last access time: </BODY></HTML>會話數(shù)據(jù)存在于服務器上的臨時文件目錄中,prefix 參數(shù)指定會話的前綴,作為臨時文件的前綴。在服務器上,以便您可以輕松識別不同的會話臨時文件。

5.3 創(chuàng)建控制器方法,指向商品頁面

創(chuàng)建控制器類,由于是商品相關的控制器,所以命名為 GoodsController ,代碼如下:實例:/** * 商品控制器 */@Controller // 標注為控制器public class GoodsController { /** * 獲取商品列表 */ @RequestMapping("/goods") // 請求路徑 public String goods() { return "goods";// 跳轉到goods.ftl頁面 }}我們具體解釋下該類的作用。@Controller 注解標注在 GoodsController 類上,會為該類注冊一個控制器組件,放入 Spring 容器中。該組件具備處理請求的能力,其中的方法可以響應 HTTP 請求;@RequestMapping ("/goods") 注解標注在方法 goods () 上,所以請求路徑如果匹配 /goods ,則由該方法進行處理;返回值是字符串 "goods" ,由于我們已經引入 FreeMarker 依賴,所以該返回值會跳轉到 goods.ftl 頁面。Tips: 注意需要在 application.properties 文件中設置模板文件的后綴,即: spring.freemarker.suffix=.ftl 。如果不添加該配置,直接 return "goods.ftl"; 會報錯。

2.2 TextView 的背景

針對 TextView 有一個android:background屬性,可以為我們的文本框設置一個自定義的背景圖,通常有兩種設置方法:直接設置一個圖片作為背景圖;設置一個 drawable 資源。(關于 drawable 的介紹可以參考第四節(jié))直接設置圖片比較好理解,就不做過多的演示,大家可以自行嘗試,這里主要講解一下采用 drawable 資源的用法。首先我們找到“src” -> “main” -> “res” -> “drawable”目錄,在里面右鍵新建一個“Drawable Resource File”,輸入文件名:text_background;可能 IDE 會默認生成部分代碼,比如<selector/>,刪除默認生成的代碼,添加一個<shape/>標簽,用來指定 TextView 的外形;在<shape/>標簽中添加<stroke/>,設置 TextView 外形的邊框樣式,設置邊框的寬度和顏色;在<shape/>中繼續(xù)添加<padding/>標簽,用來設置 TextView 中文本和邊框的距離;最后在<shape/>標簽中添加<gradient/>,為 TextView 的背景添加色彩漸變。最終整體代碼修改如下:<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 設置邊框顏色和寬度 --> <stroke android:width="10dp" android:color="#000000" /> <!-- 設置邊距 --> <padding android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp" /> <!-- 添加漸變效果 --> <gradient android:angle="45" android:endColor="#EEC5C5" android:startColor="#0A22A5" /></shape>至此,drawable 資源編寫完畢,我們就可以在 TextView 中使用了。直接修改TextView的代碼,添加android:background屬性:<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@drawable/text_backgroud" android:padding="20dp" android:shadowColor="#5919C9" android:shadowDx="20.0" android:shadowDy="20.0" android:shadowRadius="4.5" android:text="跟著超哥學Android" android:textColor="#BECF0B" android:textSize="40sp" />最終效果:

3. 通過 XML 完成一個 FrameLayout

我們的 FrameLayout 需要滿足以下要求:一個圖片,作為 FrameLayout 的前景;一個 Android Logo 圖片資源(ImageView);一個文本資源,和 Logo 重疊,可以看到后添加的 View 會擋住前面添加的 View;一個文本資源,和前景圖片重疊,可以看到前景圖片時鐘會在內部 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" android:foreground="@drawable/emercy" android:foregroundGravity="bottom|right" android:orientation="vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="centerCrop" android:src="@mipmap/ic_launcher" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp" android:background="#4C374A" android:padding="10dp" android:text="Welcome to iMooc" android:textColor="#FFFFFF" android:textSize="20sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left|bottom" android:background="#1E64D8" android:padding="10dp" android:text="Emercy Android" android:textColor="#FFFFFF" android:textSize="18sp" /></FrameLayout>效果如下:這就是 FrameLayout 的基本用法。

5. 個人經驗

如果項目需求比較簡單,建議直接下載使用編譯文件。如果項目環(huán)境允許使用公共 CDN,建議使用 CDN 方式引入編譯文件,可獲得更好 HTTP 性能。如果開發(fā)環(huán)境已經配備 webpack、rollup 等工程化環(huán)境,則應優(yōu)先使用 npm安裝,并將 ECharts 與項目中使用到的其他第三方框架一起打包,減少客戶端請求數(shù)。使用時,可根據(jù)需求,在 echarts.js、echarts.common.js、echarts.simple.js 之間選擇最合適的編譯版本。個人經驗是優(yōu)先使用 echarts.common.js,但需求無法滿足時再使用 echarts.js,如果運行環(huán)境比較苛刻,如 3G 網絡下,則自行構建最輕量級的版本,構建方式可參閱第二節(jié) ECharts 自定義構建。

2. 語法詳情

Sass 的導入和 CSS 中的導入語法類似,只不過在 Sass 中可以導入用逗號分隔的多個文件, 我們舉個例子看下:@import 'a.scss', 'b.scss';上面的代碼意思是導入 a.scss 和 b.scss 文件,那么導入后 a 和 b 中的任何mixin 、函數(shù)和變量都是可以使用的。我們知道在 CSS 中也有 @import 語句,在以下幾種情況 Sass 會認為 @import 是 CSS 語句:使用 url()文件的擴展名是 .css@import 包含 media queries文件名以 http:// 開頭在使用的時候要注意上面的幾種情況,如果導入的擴展名是 .scss 或 .sass 那么肯定用的是 Sass 提供的 @import 。如果導入文件沒有指定文件擴展名,那么 Sass 會嘗試尋找文件名相同的擴展名為 .sass 或 .scss 的文件。

3.1 Django 中 Cookie 操作相關源碼

從前面的操作 Cookie 講解中,我們只用到了和增和查兩部分的方法,分別對應 HttpResponse 和 HttpRequest 兩個類。接下來,我們去對應的源碼中查找所涉及的和 Cookie 相關的代碼。request.COOKIES['xxx']request.COOKIES.get('xxx', None)# 源碼位置:django/core/handlers/wsgi.pyclass WSGIRequest(HttpRequest): # ... @cached_property def COOKIES(self): raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '') return parse_cookie(raw_cookie) # ...# 源碼位置:django/http/cookie.pyfrom http import cookies# For backwards compatibility in Django 2.1.SimpleCookie = cookies.SimpleCookie# Add support for the SameSite attribute (obsolete when PY37 is unsupported).cookies.Morsel._reserved.setdefault('samesite', 'SameSite')def parse_cookie(cookie): """ Return a dictionary parsed from a `Cookie:` header string. """ cookiedict = {} for chunk in cookie.split(';'): if '=' in chunk: key, val = chunk.split('=', 1) else: # Assume an empty name per # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 key, val = '', chunk key, val = key.strip(), val.strip() if key or val: # unquote using Python's algorithm. cookiedict[key] = cookies._unquote(val) return cookiedict上面的代碼并不復雜,在 WSGIRequest 類中的 COOKIES 屬性是先從客戶端請求中取出 Cookie 信息,調用 get_str_from_wsgi() 方法是從 WSGI 中拿到對應的 Cookie 字符串。接下來用 parse_cookie() 方法將原始 Cookie 字符串中的 key=value 解析出來做成字典形式并返回。這就是為什么我們能像操作字典一樣操作 request.COOKIES 的原因。下面的方法是實驗1中調用的 get_signed_cookie() 的源碼,也不復雜,同樣是從self.COOKIES 中取出對應 key 的 value 值,然后使用對應的 salt 解密即可。# 源碼位置:django/http/request.py class HttpRequest: # ... def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None): """ Attempt to return a signed cookie. If the signature fails or the cookie has expired, raise an exception, unless the `default` argument is provided, in which case return that value. """ try: cookie_value = self.COOKIES[key] except KeyError: if default is not RAISE_ERROR: return default else: raise try: value = signing.get_cookie_signer(salt=key + salt).unsign( cookie_value, max_age=max_age) except signing.BadSignature: if default is not RAISE_ERROR: return default else: raise return value # ...接下來是涉及到創(chuàng)建 Cookie 的方法,我們需要查找 HttpResponse 類或者相關的父類:# 源碼位置:django/http/response.pyclass HttpResponseBase: # ... def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False, samesite=None): """ Set a cookie. ``expires`` can be: - a string in the correct format, - a naive ``datetime.datetime`` object in UTC, - an aware ``datetime.datetime`` object in any time zone. If it is a ``datetime.datetime`` object then calculate ``max_age``. """ self.cookies[key] = value if expires is not None: if isinstance(expires, datetime.datetime): if timezone.is_aware(expires): expires = timezone.make_naive(expires, timezone.utc) delta = expires - expires.utcnow() # Add one second so the date matches exactly (a fraction of # time gets lost between converting to a timedelta and # then the date string). delta = delta + datetime.timedelta(seconds=1) # Just set max_age - the max_age logic will set expires. expires = None max_age = max(0, delta.days * 86400 + delta.seconds) else: self.cookies[key]['expires'] = expires else: self.cookies[key]['expires'] = '' if max_age is not None: self.cookies[key]['max-age'] = max_age # IE requires expires, so set it if hasn't been already. if not expires: self.cookies[key]['expires'] = http_date(time.time() + max_age) if path is not None: self.cookies[key]['path'] = path if domain is not None: self.cookies[key]['domain'] = domain if secure: self.cookies[key]['secure'] = True if httponly: self.cookies[key]['httponly'] = True if samesite: if samesite.lower() not in ('lax', 'strict'): raise ValueError('samesite must be "lax" or "strict".') self.cookies[key]['samesite'] = samesite def set_signed_cookie(self, key, value, salt='', **kwargs): value = signing.get_cookie_signer(salt=key + salt).sign(value) return self.set_cookie(key, value, **kwargs) def delete_cookie(self, key, path='/', domain=None): # Most browsers ignore the Set-Cookie header if the cookie name starts # with __Host- or __Secure- and the cookie doesn't use the secure flag. secure = key.startswith(('__Secure-', '__Host-')) self.set_cookie( key, max_age=0, path=path, domain=domain, secure=secure, expires='Thu, 01 Jan 1970 00:00:00 GMT', ) # ...從上面的代碼可以看到,最核心的方法是 set_cookie(),而刪除 cookie 和 設置加鹽的 cookie 方法最后都是調用 set_cookie() 這個方法。而這個方法也比較簡單,就是將對應的傳遞過來的參數(shù)值加到 self.cookies 這個字典中。最后我們思考下,難道就這樣就完了嗎?是不是還需要有一步是需要將 self.cookies 中的所有 key-value 值組成字符串,放到頭部中,然后才返回給前端?事實上,肯定是有這一步的,代碼如下。在用 “#” 號包圍起來的那一段代碼正是將 self.cookies 中的所有 key-value 值組成字符串形式,然后放到頭部的 “Set-Cookie” 中,正是有了這一步的動作,我們前面設置的 self.cookie 內部的 key-value 值才能真正生效。# 源碼位置:django/core/handlers/wsgi.pyclass WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) response = self.get_response(request) response._handler_class = self.__class__ status = '%d %s' % (response.status_code, response.reason_phrase) ############################################################################## response_headers = [ *response.items(), *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()), ] ############################################################################# start_response(status, response_headers) if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'): response = environ['wsgi.file_wrapper'](response.file_to_stream) return response

3. 適用場景

并非所有場景都適合使用 sse 處理,在消息推送接收不頻繁的情況下選用 ajax 輪詢或者 sse 或者 websocket 其實差別不太大。sse 應該適用于服務端向客戶端發(fā)送消息頻繁而客戶端幾乎無需向服務端發(fā)送數(shù)據(jù)的場景下,例如:新郵件通知;訂閱新聞通知;天氣變化;服務器異常通知;網站公告;等等。sse 的優(yōu)缺點:SSE 使用 HTTP 協(xié)議,除 IE 外的大部分瀏覽器都支持;SSE 屬于輕量級,使用簡單;SSE 默認支持斷線重連;SSE 一般只用來傳送文本,二進制數(shù)據(jù)需要編碼后傳送;SSE 支持自定義發(fā)送的消息類型。

2.1 @RequestParam

功能: 自動綁定請求包中的請求參數(shù)中的值。請求參數(shù)可以出現(xiàn)在請求包中的 2 個位置:用 ? 的方式附加在 URL 后面。如 http://localhost:8888/saveUser?name=abc 。name 就是請求參數(shù),abc 是值;以類似于 name=abc 的方式存儲在請求包的報文體中(實體部分)。Tips: 當客戶端以 POST 方法請求時,數(shù)據(jù)將存儲在實體部分。實例:@RequestMapping(value="/saveUser")public String save(@RequestParam("userName") String userName,@RequestParam("password") password){}當請求格式類似于 http://localhost:8888/saveUser?userName=abc&password=123456 時,save()方法中的 userName 和 password 2 個參數(shù)會被分別注入 abc 和 123456 這 2 個值。客戶端在表單中以 POST 方法發(fā)送請求時,數(shù)據(jù)將會以 userName=abc&password=123456 的格式存放在請求包的實體部分,也會自動注入到 save() 方法的參數(shù)中。Tips: 一定要保證 @RequestParam(“參數(shù)名”) 中的 “參數(shù)名” 和請求包中的參數(shù)名的命名相同。@RequestParam 注解中有 3 個常用的屬性:value(): 指定要解析的請求包中的參數(shù)名;@AliasFor("name")String value() default "";required(): 表示請求包中是否必須存在由 value() 指定的參數(shù)名,默認值是 true。如果沒有此參數(shù)則會拋出異常;boolean required() default true;defaultValue(): 表示如果不能正確獲取到指定參數(shù)的值時,則提供一個默認值。設置該參數(shù)時,自動將 required 設為 false。String defaultValue() default ValueConstants.DEFAULT_NONE;綜合實例:@RequestMapping(value="/saveUser")public String save(@RequestParam(value = "userName",defaultValue="mk",required = false) String userName,){}Tips: 當不能確定是否存在名為 “userName” 的參數(shù)時,可使用 defaultValue 提供默認值,并設置 required 為 false。

2.1 返回字符串

給出一個控制器的實例代碼,這個代碼很普通,不同之處在于方法前面使用了 @ResponseBody 注解。 于是,這個方法的命運被改變了。@Controller@RequestMapping("/json")public class JsonAction { @RequestMapping("/test01") @ResponseBody public String testJson() { return "Hello"; }}控制器方法有 @ResponseBody 注解和沒有此注解,Spring MVC 對控制器方法的返回值的理解迥然不同。沒有 @ResponseBody 注解:Spring MVC 會把返回值 “hello” 理解為視圖邏輯名,通過視圖解析器找到視圖物理位置;有 @ResponseBody 注解: Spring MVC 會把返回值直接寫入響應包后交給瀏覽器。意味著不需要視圖解析器的參與。測試一下上面實例。打開瀏覽器,在地址欄上輸入:http://localhost:8888/sm-demo/json/test01 。你在瀏覽器將看到如下圖所示結果:

3.4 Wing

也是在當今市場上流行和強大的 IDE,具有許多適合 Python 開發(fā)人員要求的功能, 可以工作在 Windows, macOS 與 Linux 上。 它是商業(yè)軟件,Wing Personal 與 Wing 101 兩個版本是為了學生與初學者的免費版本。優(yōu)點:在試用版過期的情況下,Wing 為開發(fā)人員提供了大約 10 分鐘的時間來遷移其應用程序;它有一個源瀏覽器,有助于顯示腳本中使用的所有變量;功能強大的調試器,提供了一個額外的異常處理選項卡,可幫助開發(fā)人員調試代碼。缺點:在科學計算方面沒有集成一些常用工具與庫;商業(yè)版本功能強大,也意味占用內存比較大。 圖片來源:http://wingware.com 官網

3. 注意事項

由于 HTML 中的元素中,圖片相對于別的文本類型的元素所占用的網絡空間較大,所以圖片加載可能會比較慢,而且 HTML 中的元素網絡請求是同步進行的,所以當定義了一些較大的圖片或者圖片元素較多時,網頁本身會出現(xiàn)卡頓的情況,所以針對圖片的加載需要做出一些優(yōu)化:盡可能少使用圖片,或者使用體積較小的圖片;壓縮圖片體積;通過懶加載的方式異步加載圖片;通過設置 HTTP 緩存時間,防止圖片重復請求服務器資源;通過使用 CSS 背景圖的方式,將所需圖片壓縮到一張圖片,減少請求圖片次數(shù)。

2.4 運行測試

為了避免不同開發(fā)環(huán)境的干擾,我們統(tǒng)一使用 Maven 命令行的方式編譯和執(zhí)行項目。打開控制臺,在 pom.xml 同級目錄(工程根目錄)中輸入命令 mvn spring-boot:run 來啟動 Spring Boot 。此步執(zhí)行命令如下:mvn spring-boot:run訪問 http://localhost:8080/hello 可以看到如下頁面,此時系統(tǒng)要求我們輸入用戶名密碼完成身份認證。身份認證Spring Security 項目在默認配置下,會自動生成一個名為「user」的用戶,并分配其隨機密碼,此密碼可以從控制臺的日志信息中找到:...Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336...填入用戶名密碼,點擊登錄,將看到認證結果頁:認證結果此時,我們完成了第一個「Spring Security」實例。

4.4 清單文件

需要注意的是,Service 是一個組件,凡是添加組件都需要在 AndroidManifest.xml 中注冊(動態(tài)注冊除外),否則無法使用:<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.emercy.myapplication"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".PlayerService" android:enabled="true" android:exported="true" /> </application></manifest>**注意:**如果你的音頻文件是一個遠程的 url,還需要增加網絡權限: <uses-permission android:name="android.permission.INTERNET" />到此,整個初級的播放器就開發(fā)完成了。大家如果感興趣還可以考慮增加其他的功能,比如快進、快退、切歌、增加通知欄、展示歌詞等等。

1. 前言

上一節(jié),我們主要講解了 Http 協(xié)議,本節(jié)我們來講解 Netty 支持的另外一個 WebSocket 協(xié)議,WebSocket 是 HTML5 開始支持的全雙工協(xié)議。在真實的開發(fā)當中,其實 WebSocket 使用非常的廣泛,特別是在目前前端技術越來越完善的情況下,很多應用都是基于前端技術去實現(xiàn),比如:APP、小程序等主流應用,前端的技術完全可以開發(fā)出類似原生技術一樣的產品,并且開發(fā)效率上比原生更加的快、用戶體驗更好。這些應用涉及通信、聊天、推送等業(yè)務,則可以使用 WebSocket 來實現(xiàn),因此,WebSocket 已經是目前非常主流的瀏覽器和服務端建立長連接的通信技術。WebSocket 協(xié)議架構圖如下所示:

4.1 mapper 配置

首先,我們需要在 mybatis-config.xml 配置文件中添加上對應的 mapper 配置:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/imooc?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- mapper 對應的配置 --> <mappers> <mapper class="com.imooc.mybatis.mapper.UserMapper"/> </mappers></configuration>注意,mapper 可以有多個,對應的標簽項應是 mappers,在 mappers 下面才有一個個的 mapper,如上面的 UserMapper;通過mapper 標簽中的 class 屬性,我們指定其對應的接口類,class 屬性值為 UserMapper 的類全路徑。

2.1 HTTPS 請求流程

面試官提問: HTTPS 的請求流程和 HTTP 協(xié)議的請求流程有什么區(qū)別?題目解析:參考 HTTPS 的官方文檔,我們將整個請求的流程簡單抽象為以下幾個步驟,抓住其中的核心步驟: (HTTPS 簡化通信模型)步驟(1):客戶端發(fā)送一個 HTTPS 請求,例如請求 https://imooc.com,連接到服務器端的 443 端口(和 HTTP 協(xié)議不同,HTTP 默認 80 端口)。步驟(2):服務器端收到握手信息,使用預先配置好的數(shù)字證書,即圖中的公鑰和私鑰。如果是自己頒發(fā)的證書,那么需要客戶端通過瀏覽器的彈窗驗證,如果是組織申請獲得,默認直接通過。步驟(3):傳輸證書給客戶端,證書組裝了多種信息,包含證書的頒發(fā)機構、證書有效時間、服務器端的公鑰,證書簽名等。步驟(4):客戶端解析證書,也就是通過 TLS/SSL 協(xié)議,判定公鑰是否有效,如果發(fā)現(xiàn)異常,會彈出警告框。如果校驗沒有問題,那么客戶端會生成一個隨機數(shù),然后用上一步傳輸過來的公鑰對隨機數(shù)進行加密。步驟(5):客戶端將上個步驟隨機數(shù)加密后的內容傳輸給服務器端,這個隨機數(shù)就是兩端通信的核心。步驟(6):服務器端用自己的私鑰進行解密,獲取解密前的隨機數(shù)。然后組裝會話秘鑰,這里私鑰和客戶端會話秘鑰是相同的。步驟(7):將服務器端用私鑰加密后的內容傳輸給客戶端,在客戶端用之前生成的隨機數(shù)組裝私鑰還原。步驟(8):客戶端用之前的私鑰解密獲取的信息,也就獲取了通信內容。上述過程中,SSL 和 TLS 協(xié)議是核心模塊,具體的證書交互流程相對復雜,面試場景基本不會涉及。我們需要關注的是為什么 HTTPS 同時使用非對稱加密和對稱加密,有兩個原因:(1)對稱加密流程兩邊需要使用相同的密鑰,單純使用對稱加密,無法實現(xiàn)密鑰交換。(2)非對稱加密:滿足安全要求,但是非對稱加密的計算耗時高于對稱加密的 2-3 個數(shù)量級(相同安全加密級別),對于實際的應用場景,例如電商網站,對網絡交互高耗時容忍度是非常低的。所以 HTTPS 才先使用非對稱交換密鑰,之后再使用對稱加密通信。

HTML5 離線存儲

本章介紹一下 HTML5 新增的離線存儲特性 Localstorage,主要包括 Localstorage 的發(fā)展史、兼容性、優(yōu)缺點以及使用場景。說到 Localstorage,為什么要使用 Localstorage 呢?因為開發(fā)程序當然就要存儲數(shù)據(jù)了,但是 Web 開發(fā)場景比較特殊,數(shù)據(jù)正常情況下是要通過 HTTP 協(xié)議發(fā)送給服務器端,存儲在服務器上,但是呢,如果所有數(shù)據(jù)都存到服務器一方面會浪費服務器資源,另一方面也會降低網頁響應速度,所以設計網頁時會抽取一些不太重要的或者臨時性的數(shù)據(jù)使用離線存儲方式放在瀏覽器上??偟膩碚f,Localstorage 是一個簡單的離線存儲技術,通過官方提供的增刪改查的 API 可以方便的操作它,需要考慮的難點是每個瀏覽器的容量限制,使用時做好容錯即可。

2. Numpy 在線安裝

如果已經完成了 Python 環(huán)境搭建,在本地環(huán)境上安裝 Numpy 也非常方便。在保持網絡通暢的前提下,在 cmd 窗口中輸入:C:\Users>pip install numpyPIP 工具會根據(jù) Python 的版本,自動搜索匹配的 Numpy 版本,進行安裝。PIP(英文 Python Install Packages 的簡稱)是一個現(xiàn)代的、通用的 Python 包管理工具??紤]到默認情況下,PIP 是從 Python 官方的第三方庫的倉庫中選擇合適版本下載,在某些網絡情況下并不好用。因此也可以利用國內的鏡像點進行在線安裝,這里以阿里云的鏡像站點為例,完成Numpy安裝演示:C:\Users>pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com numpy

3.2 Swagger Editor 使用技巧

我們在使用 Swagger Editor 時,一般都是在沒有借口文檔的情況下進行,所以這就要求我們對接口所開展的業(yè)務有很清晰的了解才行。在 Swagger Editor 中,我們會對不同屬性編寫不同的描述信息,而涉及到名字的屬性一定要注意,例如:title 屬性,在對這一類型屬性進行描述時,我們應該根據(jù)項目需求文檔來進行描述,不能自己隨意起名字,只有按照項目需求文檔來描述的 Swagger Editor 屬性才能說是準確的,是服務于當前項目的。即使我們把 Swagger Editor 引入到了 SpringBoot 項目中去,但是如果想要運行 Swagger Editor ,我們還是要依賴于 http-server ,因為 Maven 并沒有提供對 Swagger Editor 的支持服務。

1. 準備一個例子

下面例子是通過并發(fā)的方式從有道的網站獲取多個英語單詞的解釋, 將以下代碼復制到項目中的文件中, 比如創(chuàng)建一個文件 debug_demo.pyimport requestsimport refrom concurrent.futures import ThreadPoolExecutorimport timedef download_html(word): time.sleep(5) output = [] headers = { 'User-Agent': 'Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) ' 'Chrome / 72.0.3626.121Safari / 537.36' } url = 'http://dict.youdao.com/w/eng/{}/'.format(word) try: r = requests.get(url, headers=headers) if r.status_code == 200: pattern = re.compile(' <span class="keyword">(.*?)</span>.*?<span class="pronounce">(.*?)\n.' '*?<span class="phonetic">(.*?)</span>.*?<span class="pronounce">(.*?)\n.*?' '<span class="phonetic">(.*?)</span>.*?<div class="trans-container">.*?<ul>.*?' '<li>(.*?)</li>.*?<li>(.*?)</li>', re.S) word = re.findall(pattern, r.text) print(word) output.append(word) except Exception as e: pass return outputif __name__ == '__main__': text = input("請輸入要查詢的單詞,中間用逗號隔開:") start = time.time() words = text.split(',') pool = ThreadPoolExecutor(4) threads = [pool.submit(download_html, word) for word in words] for i in threads: print(i.result) end = time.time() print(end - start)

第一步 安裝 KeepAlived 組件

在集成 KeepAlived 組件之前,我們需要在自己的機器上下載并安裝 KeepAlived 組件,可以通過以下命令實現(xiàn):wget http://www.keepalived.org/software/keepalived-1.2.18.tar.gz下載完成之后,我們會得到一個壓縮包,接著,我們需要將該壓縮包進行解壓,解壓命令如下:tar -zxvf keepalived-1.2.18.tar.gz接著,我們需要安裝 KeepAlived 組件,安裝命令如下:// 進入到 KeepAlived 安裝目錄下cd keepalived-1.2.18/ && ./configure// 編譯并安裝 KeepAlivedmake && make install經過上述安裝命令之后,如果沒有提示任何錯誤,則表明 KeepAlived 組件已經安裝成功了。Tips: 如果在安裝 KeepAlived 過程中,提示缺少安全套件,無法進行安裝,那就表明我們缺少 ssl 安全協(xié)議,我們只需要將這個安全協(xié)議安裝上去就可以了: yum install -y openssl openssl-devel

2.2 ContextMixin 和 TemplateResponseMixin

接下來,我們查看下 Django 提供的兩個 mixin:ContextMixin 和 TemplateResponseMixin。其內容比較簡單,源碼如下:# 源碼位置:django/views/generic/base.pyclass ContextMixin: """ A default context mixin that passes the keyword arguments received by get_context_data() as the template context. """ extra_context = None def get_context_data(self, **kwargs): kwargs.setdefault('view', self) if self.extra_context is not None: kwargs.update(self.extra_context) return kwargs class TemplateResponseMixin: """A mixin that can be used to render a template.""" template_name = None template_engine = None response_class = TemplateResponse content_type = None def render_to_response(self, context, **response_kwargs): """ Return a response, using the `response_class` for this view, with a template rendered with the given context. Pass response_kwargs to the constructor of the response class. """ response_kwargs.setdefault('content_type', self.content_type) return self.response_class( request=self.request, template=self.get_template_names(), context=context, using=self.template_engine, **response_kwargs ) def get_template_names(self): """ Return a list of template names to be used for the request. Must return a list. May not be called if render_to_response() is overridden. """ if self.template_name is None: raise ImproperlyConfigured( "TemplateResponseMixin requires either a definition of " "'template_name' or an implementation of 'get_template_names()'") else: return [self.template_name]ContextMixin 比較簡單,只提供了一個屬性 extra_context 和一個方法 get_context_data(),它主要的功能是根據(jù)額外提供的參數(shù),組成新的上下文字典,調用 get_context_data() 方法即可實現(xiàn)。TemplateResponseMixin 是用來渲染模板的,它的屬性與方法如下:屬性:template_name:模板文件名;template_engine: 模板引擎;response_class: 返回的 Response,默認是 TemplateResponse;content_type:返回客戶端的數(shù)據(jù)類型。方法:render_to_response():默認直接調用 TemplateResponse(),完成模板渲染后返回響應給客戶端;get_template_names():獲取模板名稱,返回列表的形式;如果沒有設置 template_name 屬性值,則會報錯。因此對于繼承該 mixin 對象的子類,必須要設置 template_name 屬性值。Mixin 的特點就是功能簡單,它們可以混合加到其他類中,那么其他類就具備這些 Mixin 的功能,組合得到一個更高級的類。同樣我們在前一小節(jié)實驗的基礎上改造下 views.py 中的內容,如下:class TestView(TemplateResponseMixin, View): template_name = 'test.html' def get(self, request, *args, **kwargs): return self.render_to_response(context={'content': '正文1', 'spyinx': {'age': 29}})這里我們額外繼承 TemplateResponseMixin 類。首先需要添加 template_name 屬性,指定要渲染的模板文件,然后調用從 TemplateResponseMixin 中繼承過來的 render_to_response() 方法并返回。從源碼角度來看,這個視圖實現(xiàn)的功能和上一個小節(jié)中直接返回 TemplateResponse 實例是一樣的。# 啟動 first_django_app 工程...# 打開xshell另一個窗口,發(fā)送http請求[root@server first_django_app]# curl http://127.0.0.1:8888/hello/test-cbv/<p>正文1</p><div>29</div>

3.1. Freemarker 視圖技術

本小節(jié)和大家一起聊聊在 Spring MVC 中如何使用 Freemarker 視圖技術 。打開 pom.xml 文件,添加 Freemarker 依賴包;<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version></dependency><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.1.13.RELEASE</version></dependency>新建 spring-freemarker.xml 文件,放在 src/main/resources 目錄中。新建 freemarker.properties 文件放在 src/main/resources /conf 目錄中;Tips: Spring MVC 支持 JAVA 、XML 配置,也支持兩者混合使用。Freemarker 的相關配置這里使用 XML 方法。打開 WebConfig 文件,在類名前加上 @ImportResource(value = {“classpath:spring-freemarker.xml”}) 這樣配置類和 XML 文件就可以混合使用;@Configuration@EnableWebMvc@ComponentScan(basePackages = { "com.mk.web.action" })@ImportResource(value = {"classpath:spring-freemarker.xml"})public class WebConfig implements WebMvcConfigurer { //……}在 spring-freemarker.xml 中配置 3 個 Bean。<bean id="freemarkerConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location" value="classpath:conf/freemarker.properties" /></bean><!-- freeMarker 的模板路徑 --><bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="freemarkerSettings" ref="freemarkerConfiguration" /> <property name="templateLoaderPath"> <value>/WEB-INF/ftl/</value> </property></bean><!-- freeMarker 視圖解析器 --><bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> <property name="contentType" value="text/html; charset=utf-8" /> <property name="cache" value="true" /></bean> freemarker.properties 文件內容。用來設置 Freemarker 的工作環(huán)境;#設置標簽類型tag_syntax=auto_detect#模版緩存時間,單位:秒template_update_delay=0default_encoding=UTF-8output_encoding=UTF-8locale=zh_CN#設置數(shù)字格式 ,防止出現(xiàn) 000.00number_format=\##變量為空時,不會報錯classic_compatible=true在項目的 WEB-INF 目錄下新建用來存放 freemarker 模板文件的 ftl 目錄;編寫 FreemarkerAction 控制器;@Controllerpublic class FreemarkerAction { @RequestMapping("/fm") public String sayHello(ModelMap map){ map.put("name", "mk"); return "mk.ftl"; } }在 WEB-INF/ftl 下創(chuàng)建一個名為 mk.ftl 的模板文件;<pre name="code" class="java"> <html> <body> <h1>holle world ${name}</h1><br/> </body> </html> </pre> Tips: 每一種視圖技術都會提供自己的模板語法。FreeMarker 更多的語法元素大家可查閱 FreeMarker 官方網站 http://freemarker.foofun.cn/toc.html 。本文僅介紹在 Spring MVC 中如何使用 FreeMarker 。打開瀏覽器,地址欄中輸入 http://localhost:8888/sm-demo/fm 。瀏覽器中將顯示;

2.1 基礎項目構建

我們使用 IDEA 來構建項目,選擇 Spring Initializr 來初始化 Spring Boot 項目。這是我們的項目信息:pom.xml初始化完成后,在 pom.xml 文件中加入我們需要的依賴:<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.cdd</groupId> <artifactId>zookeeper-config</artifactId> <version>0.0.1-SNAPSHOT</version> <name>zookeeper-config</name> <description>zookeeper-config Demo project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <!-- Web 依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 持久層框架 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!-- curator 客戶端 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>5.1.0</version> </dependency> <!-- curator 客戶端 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.1.0</version> </dependency> <!-- druid 連接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.22</version> </dependency> <!-- mysql 數(shù)據(jù)庫 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> <!-- fast json --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.73</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>目錄結構依賴導入完畢后,我們開始編寫代碼,下圖為本項目的目錄結構:實體類 Imooc首先我在 pojo 目錄下創(chuàng)建我們的 Java 實體類 Imooc:package cn.cdd.zookeeper.config.pojo;import java.io.Serializable;public class Imooc implements Serializable { private Integer id; private String username; private String password; private String phone; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; }}持久層接口 ImoocDao在持久層目錄 dao 下創(chuàng)建持久層接口 ImoocDao :package cn.cdd.zookeeper.config.dao;import cn.cdd.zookeeper.config.pojo.Imooc;import org.springframework.stereotype.Repository;import java.util.List;@Repositorypublic interface ImoocDao { List<Imooc> getAllImooc();}掃描持久層目錄完成 dao 層的編寫后,我們需要在 Spring Boot 主類上加入注解 @MapperScan 來掃描這個目錄:package cn.cdd.zookeeper.config;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan(basePackages = "cn.cdd.zookeeper.config.dao")public class ZookeeperConfigApplication { public static void main(String[] args) { SpringApplication.run(ZookeeperConfigApplication.class, args); }}XML 映射文件完成上面的代碼后就可以編寫持久層的 XML 映射文件了,在 resources 中的 mapper 目錄下新建 ImoocDao.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="cn.cdd.zookeeper.config.dao.ImoocDao"> <resultMap id="BaseResultMap" type="cn.cdd.zookeeper.config.pojo.Imooc"> <id column="id" jdbcType="INTEGER" property="id"/> <result column="username" jdbcType="VARCHAR" property="username"/> <result column="password" jdbcType="VARCHAR" property="password"/> <result column="phone" jdbcType="VARCHAR" property="phone"/> <result column="address" jdbcType="VARCHAR" property="address"/> </resultMap> <select id="getAllImooc" resultMap="BaseResultMap"> select * from imooc; </select></mapper>Service 層接口 ImoocService 以及實現(xiàn)類 ImoocServiceImpl接下來編寫 Service 層接口 ImoocService 以及實現(xiàn)類 ImoocServiceImpl 代碼:package cn.cdd.zookeeper.config.service;import cn.cdd.zookeeper.config.pojo.Imooc;import java.util.List;public interface ImoocService { List<Imooc> getAllImooc();}實現(xiàn)類 ImoocServiceImplpackage cn.cdd.zookeeper.config.service.impl;import cn.cdd.zookeeper.config.dao.ImoocDao;import cn.cdd.zookeeper.config.pojo.Imooc;import cn.cdd.zookeeper.config.service.ImoocService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class ImoocServiceImpl implements ImoocService { @Autowired private ImoocDao imoocDao; @Override public List<Imooc> getAllImooc() { return imoocDao.getAllImooc(); }}Controller 控制層 ImoocController實現(xiàn)類完成后,我們就可以編寫 Controller 控制層代碼了:package cn.cdd.zookeeper.config.controller;import cn.cdd.zookeeper.config.pojo.Imooc;import cn.cdd.zookeeper.config.service.ImoocService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController@RequestMapping("/imooc")public class ImoocController { @Autowired private ImoocService imoocService; @GetMapping("/getAll") public List<Imooc> getAllImooc() { return imoocService.getAllImooc(); }}application.yaml 配置文件以上代碼都完成后,我們來編寫 application.yaml 配置文件:spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/imooc?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: "root" password: "021998"server: port: 8888 servlet: context-path: /mybatis: mapper-locations: classpath:mapper/*.xml數(shù)據(jù)庫及數(shù)據(jù)表基礎部分代碼編寫完成,我們還需要數(shù)據(jù)庫和數(shù)據(jù)表,在 MySQL 中新建數(shù)據(jù)庫 imooc,然后在 imooc 庫中執(zhí)行以下命令創(chuàng)建數(shù)據(jù)表 imooc :/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 80019 Source Host : localhost:3306 Source Schema : imooc Target Server Type : MySQL Target Server Version : 80019 File Encoding : 65001 Date: 25/09/2020 00:08:37*/SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure for imooc-- ----------------------------DROP TABLE IF EXISTS `imooc`;CREATE TABLE `imooc` ( `id` int(0) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of imooc-- ----------------------------INSERT INTO `imooc` VALUES (1, 'Java', '123', '123', '北京');INSERT INTO `imooc` VALUES (2, 'Go', '321', '321', '上海');INSERT INTO `imooc` VALUES (3, 'Python', '456', '654', '深圳');SET FOREIGN_KEY_CHECKS = 1;基礎功能測試接下來我們就可以測試這個 Spring Boot 項目了,啟動主類 ZookeeperConfigApplication 的 main 方法,打開瀏覽器,訪問 http://localhost:8888/imooc/getAll,我們就可以查詢到數(shù)據(jù)庫的數(shù)據(jù):[{"id":1,"username":"Java","password":"123","phone":"123","address":"北京"},{"id":2,"username":"Go","password":"321","phone":"321","address":"上海"},{"id":3,"username":"Python","password":"456","phone":"654","address":"深圳"}]測試完成后,我們就可以把數(shù)據(jù)源信息交給 Zookeeper 管理,并對保存信息的節(jié)點開啟監(jiān)聽。

2.3 第三部分 底部區(qū)域

在底部最開始的地方,我們可以看到 “user:User Controller” 等字樣,這是我們使用 @ApiOperation 注解的 tags 屬性來為接口描述的分組名稱,當我們點擊 user 之后,字體會加粗顯示,同時會列出所有屬于該 user 分組的接口列表,如下圖所示:而 User Controller 則是我們這些接口所屬的 Java 類的名稱,這里的名稱通過 @Api 注解描述。在最底部區(qū)域可以看到 “BASE URL” 字樣,這里的 base url 指的是 Swagger-UI 界面的訪問路徑,即:http://host:port/imooc/imooc-user-service/swagger-ui.html (這里的路徑是自己配置的),右側的 API VERSION 代表當前接口的版本。在上述部分,我們介紹了 Swagger-UI 界面的所有基本元素,接下來我們來看看我們開發(fā)的接口在 Swagger-UI 界面中都有哪些內容。

3. Spring Security 實現(xiàn)

在 Spring Security 項目中,默認使用 StrictHttpFirewall 對象,該對象對一些疑似惡意攻擊的請求也進行了拒絕處理。假如該對象對我們的項目來說過于嚴格,那我們可以通過配置的方式定制哪些請求需要被拒絕,當然相應的,我們的應用程序也更容易受到攻擊。我們可以才用以下方式變更配置,如允許分號:@Beanpublic StrictHttpFirewall httpFirewall() { StrictHttpFirewall firewall = new StrictHttpFirewall(); firewall.setAllowSemicolon(true); return firewall;}StrictHttpFirewall 對象提供了一個允許被跨域訪問的 HTTP 方法列表,默認允許的方法有:DELETEGETHEADOPTIONSPATCHPOSTPUT如果希望修改此項默認策略,我們可以通過自定義 StrictHttpFirewall 對象實現(xiàn)。例如,僅允許 GET 和 POST 方法:@Beanpublic StrictHttpFirewall httpFirewall() { StrictHttpFirewall firewall = new StrictHttpFirewall(); firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST")); return firewall;}也可以通過如下方法禁用所有方法的驗證功能:StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)

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

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

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號