如果在 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)境是相關的。如果仍然不能正常搜索到插件,就去官網下載插件到本地,然后導入安裝。
@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 的值。
前后端分離時,后端接口可不能太隨意,目前后端接口編寫大多遵循 RESTful 風格。做后端接口的公司這么多,如果大家都定義自己的規(guī)范,是不利于公司之間的合作的。如果大家都能遵循一個規(guī)范來開發(fā)接口,很明顯相關人員都能省心不少。RESTful 就是一種非常流行的 HTTP 接口規(guī)范,簡單明了,使用的人也多,用它準沒錯。規(guī)范的意義,就是提供標準,提高效率。汽車行業(yè)標準化程度已經很高了,軟件行業(yè)還需努力?。▓D片來源于網絡,版權歸原作者所有)
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ù)指定會話的前綴,作為臨時文件的前綴。在服務器上,以便您可以輕松識別不同的會話臨時文件。
創(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"; 會報錯。
針對 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" />最終效果:
我們的 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 的基本用法。
如果項目需求比較簡單,建議直接下載使用編譯文件。如果項目環(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 自定義構建。
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 的文件。
從前面的操作 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
并非所有場景都適合使用 sse 處理,在消息推送接收不頻繁的情況下選用 ajax 輪詢或者 sse 或者 websocket 其實差別不太大。sse 應該適用于服務端向客戶端發(fā)送消息頻繁而客戶端幾乎無需向服務端發(fā)送數(shù)據(jù)的場景下,例如:新郵件通知;訂閱新聞通知;天氣變化;服務器異常通知;網站公告;等等。sse 的優(yōu)缺點:SSE 使用 HTTP 協(xié)議,除 IE 外的大部分瀏覽器都支持;SSE 屬于輕量級,使用簡單;SSE 默認支持斷線重連;SSE 一般只用來傳送文本,二進制數(shù)據(jù)需要編碼后傳送;SSE 支持自定義發(fā)送的消息類型。
功能: 自動綁定請求包中的請求參數(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。
給出一個控制器的實例代碼,這個代碼很普通,不同之處在于方法前面使用了 @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 。你在瀏覽器將看到如下圖所示結果:
也是在當今市場上流行和強大的 IDE,具有許多適合 Python 開發(fā)人員要求的功能, 可以工作在 Windows, macOS 與 Linux 上。 它是商業(yè)軟件,Wing Personal 與 Wing 101 兩個版本是為了學生與初學者的免費版本。優(yōu)點:在試用版過期的情況下,Wing 為開發(fā)人員提供了大約 10 分鐘的時間來遷移其應用程序;它有一個源瀏覽器,有助于顯示腳本中使用的所有變量;功能強大的調試器,提供了一個額外的異常處理選項卡,可幫助開發(fā)人員調試代碼。缺點:在科學計算方面沒有集成一些常用工具與庫;商業(yè)版本功能強大,也意味占用內存比較大。 圖片來源:http://wingware.com 官網
由于 HTML 中的元素中,圖片相對于別的文本類型的元素所占用的網絡空間較大,所以圖片加載可能會比較慢,而且 HTML 中的元素網絡請求是同步進行的,所以當定義了一些較大的圖片或者圖片元素較多時,網頁本身會出現(xiàn)卡頓的情況,所以針對圖片的加載需要做出一些優(yōu)化:盡可能少使用圖片,或者使用體積較小的圖片;壓縮圖片體積;通過懶加載的方式異步加載圖片;通過設置 HTTP 緩存時間,防止圖片重復請求服務器資源;通過使用 CSS 背景圖的方式,將所需圖片壓縮到一張圖片,減少請求圖片次數(shù)。
為了避免不同開發(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」實例。
需要注意的是,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ā)完成了。大家如果感興趣還可以考慮增加其他的功能,比如快進、快退、切歌、增加通知欄、展示歌詞等等。
上一節(jié),我們主要講解了 Http 協(xié)議,本節(jié)我們來講解 Netty 支持的另外一個 WebSocket 協(xié)議,WebSocket 是 HTML5 開始支持的全雙工協(xié)議。在真實的開發(fā)當中,其實 WebSocket 使用非常的廣泛,特別是在目前前端技術越來越完善的情況下,很多應用都是基于前端技術去實現(xiàn),比如:APP、小程序等主流應用,前端的技術完全可以開發(fā)出類似原生技術一樣的產品,并且開發(fā)效率上比原生更加的快、用戶體驗更好。這些應用涉及通信、聊天、推送等業(yè)務,則可以使用 WebSocket 來實現(xiàn),因此,WebSocket 已經是目前非常主流的瀏覽器和服務端建立長連接的通信技術。WebSocket 協(xié)議架構圖如下所示:
首先,我們需要在 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 的類全路徑。
面試官提問: 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 新增的離線存儲特性 Localstorage,主要包括 Localstorage 的發(fā)展史、兼容性、優(yōu)缺點以及使用場景。說到 Localstorage,為什么要使用 Localstorage 呢?因為開發(fā)程序當然就要存儲數(shù)據(jù)了,但是 Web 開發(fā)場景比較特殊,數(shù)據(jù)正常情況下是要通過 HTTP 協(xié)議發(fā)送給服務器端,存儲在服務器上,但是呢,如果所有數(shù)據(jù)都存到服務器一方面會浪費服務器資源,另一方面也會降低網頁響應速度,所以設計網頁時會抽取一些不太重要的或者臨時性的數(shù)據(jù)使用離線存儲方式放在瀏覽器上??偟膩碚f,Localstorage 是一個簡單的離線存儲技術,通過官方提供的增刪改查的 API 可以方便的操作它,需要考慮的難點是每個瀏覽器的容量限制,使用時做好容錯即可。
如果已經完成了 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
我們在使用 Swagger Editor 時,一般都是在沒有借口文檔的情況下進行,所以這就要求我們對接口所開展的業(yè)務有很清晰的了解才行。在 Swagger Editor 中,我們會對不同屬性編寫不同的描述信息,而涉及到名字的屬性一定要注意,例如:title 屬性,在對這一類型屬性進行描述時,我們應該根據(jù)項目需求文檔來進行描述,不能自己隨意起名字,只有按照項目需求文檔來描述的 Swagger Editor 屬性才能說是準確的,是服務于當前項目的。即使我們把 Swagger Editor 引入到了 SpringBoot 項目中去,但是如果想要運行 Swagger Editor ,我們還是要依賴于 http-server ,因為 Maven 并沒有提供對 Swagger Editor 的支持服務。
下面例子是通過并發(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 組件,可以通過以下命令實現(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
接下來,我們查看下 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>
本小節(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 。瀏覽器中將顯示;
我們使用 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)聽。
在底部最開始的地方,我們可以看到 “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 界面中都有哪些內容。
在 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)