則表達式是一個十分強大的工具,可以完成對字符串的模式匹配以及提取工作,在很多工程中都被廣泛使用,但是我之前對于Python正則表達式模塊re的使用都沒有進行一些深入學習,對原始字符串之類的了解也比較淺顯,之前有個同學問到我關于反斜線\在正則中的一些表現的時候我也迷惑了,跟周圍人討論后上StackOverflow問了一番,對這方面又多了一些了解,今天跟大家分享。
我們知道,反斜線\在字符串以及正則表達式中都是完成轉義的工作,比如我有一個字符串s1:
現在這個字符串s1內容為123,如果我想在字符串中保留一個單引號',那么我可能的一種做法是使用雙引號來包含內容,字符串s2:
Python中的單引號'和雙引號'其實都可以用來指示字符串,我們在此處使用雙引號而不是單引號,原因是因為如果使用單引號Python解釋器將會無法對這條語句進行正常解釋,因為緊跟2之后的'被用來指示字符串結束,然而后面還剩下3這個字符,所以會引發Syntax Error:
但是,只是使用語句中不包含的那種引號來指示字符串有時候是行不通的,因為有時候一個字符串可能同時包含單引號和雙引號,比如字符串1"2'3,所以這時候我們需要使用反斜線\來對內容進行轉義,因此對于字符串1"2'3,我們可以采用如下表示:
注意我們并沒有對字符串內容中的雙引號"進行轉義,原因同上,字符串使用單引號指示的,因此雙引號只會被認為是一個普通的字符。對于字符串內部的單引號',為了讓解釋器得知它是一個普通字符,我們在其前面增加了一個反斜線\,\'組合起來的意思是對反斜線后面的字符'進行轉義,可以這樣理解,通過添加一個反斜線\,對于\',你告訴了解釋器:不要按照普通的方式去解釋這個字符',這樣解釋器就能正確地完成對該字符串的解釋。
有編程基礎的讀者應該會知道轉義這一概念廣泛存在于很多編程語言中,對\n、\r等字符應該有一定了解,類比前面的解釋,\n表示:不要按照普通的方式去解釋這個字符n。
進一步理解,假設我們從鍵盤輸入了字符串:abc\nde,一共輸入了7個字符,這7個字符經過Python解釋器解釋,兩個字符\和n將會被轉義成一個字符\n,請特別注意這一點,雖然從表面上看是兩個字符,但其實\n是一個特殊的字符,這一點我們可以從以下代碼看出:
有了這些基礎,下面我們進入到正則的部分。
因為有了Python解釋器首先對字符串的轉義,所以正則表達式模塊re看到的和你輸入的可能有所不同,比如,\n在字符串層被轉義成一個換行字符,然后re看到的是字符\n而不是\和n兩個字符,看以下代碼:
以上代碼首先定義了一個前文提到的字符串s,然后使用re模塊的search方法搜索,然后要搜索的字符串是\n,注意我們輸入的是字符\和字符n,然后經過Python解釋器的解釋實際在內存中的是一個\n字符,因此re模塊看到的是只有一個字符的字符串\n,而不是兩個字符,最后完成搜索。
如果我們想匹配字符串\\n,應該怎么輸入模式呢?,一種方法是:
對于字符串\\n,它的內容其實是一個普通的\字符和一個換行字符\n,對于我們輸入的匹配模式\\n,首先經過Python解釋器的字符串解釋,變成了兩個普通的\字符和一個換行字符\n,然后這些內容通過re模塊的compile方法,compile方法發現有兩個連續的\符號,因此將在接下來的匹配中匹配一個普通的\字符(請注意理解這一點),而\n字符將會匹配一個\n字符,因此該模式的匹配內容是字符串中的一個普通\字符和一個\n字符。當然,寫這么多反斜線顯得十分麻煩,所以Python也提供了一種方便的方式,即原始字符串,其格式為在字符串前添加一個r字符:r'content',通過這樣的書寫方式,Python解釋器將使用不同的規則來解釋轉義(原始字符串不能以\符號結尾)。因此上述模式可以簡化為:
讓我們再回到普通字符串,考慮以下代碼:
對于模式\\\n,經過Python解釋器的解釋將變成一個普通’`字符和一個換行字符\n,re模塊的compile方法將會看到以上解釋結果,然后該模式組合起來就是:匹配一個換行字符\n的轉義字符,也就是對換行字符\n進行轉義!結果是:
相關連接:
https://docs.python.org/3.6/library/re.html
http://stackoverflow.com/questions/33582162/backslashes-in-python-regular-expressions
本文原文地址: http://youchen.me/2017/04/24/Backslashes-in-Python-Regular-Expressions/
NET 在 System.Net.* 命名空間中提供了各種類,用于通過標準網絡協議(如 HTTP 和 TCP/IP)進行通信。以下是關鍵組件的摘要:
本章中的 .NET 類型位于 System.Net.* 和 System.IO 命名空間中。
.NET 還提供對 FTP 的客戶端支持,但只能通過已從 .NET 6 標記為過時的類。如果需要使用 FTP,最好的選擇是使用 NuGet 庫,例如 FluentFTP。
說明了 .NET 網絡類型及其所在的通信層。大多數類型駐留在層或中。傳輸層定義了發送和接收字節(TCP 和 UDP)的基本協議;應用層定義了為特定應用設計的更高級別的協議,例如檢索網頁 (HTTP)、發送郵件 (SMTP) 以及在域名和 IP 地址 (DNS) 之間進行轉換。
在應用程序層編程通常是最方便的;但是,您可能希望直接在傳輸層工作的原因有幾個。一種是是否需要 .NET 中未提供的應用程序協議(如 POP3)來檢索郵件。另一個是如果您想為特殊應用程序(如點對點客戶端)發明自定義協議。
在應用程序協議中,HTTP在對通用通信的適用性方面是特殊的。它的基本操作模式(“給我包含此 URL 的網頁”)很好地適應了“讓我了解使用這些參數調用此終結點的結果”。(除了“get”動詞之外,還有“put”,“post”和“delete”,允許基于REST的服務。
HTTP 還具有一組豐富的功能,這些功能在多層業務應用程序和面向服務的體系結構中非常有用,例如用于身份驗證和加密、消息分塊、可擴展標頭和 Cookie 的協議,以及讓許多服務器應用程序共享單個端口和 IP 地址的能力。由于這些原因,HTTP 在 .NET 中得到了很好的支持,既可以直接支持(如本章所述),也可以通過 Web API 和 ASP.NET Core 等技術在更高級別得到支持。
正如前面的討論所表明的那樣,網絡是一個充斥著首字母縮略詞的領域。我們在 中列出了最常見的。
網絡縮略語 | ||
縮寫 | 擴張 | 筆記 |
域名解析 | 域名服務 | 在域名(例如 )和 IP 地址(例如 199.54.213.2)之間進行轉換 |
郵票 | 文件傳輸協議 | 用于發送和接收文件的基于互聯網的協議 |
HTTP | 超文本傳輸協議 | 檢索網頁并運行 Web 服務 |
二世 | 互聯網信息服務 | Microsoft的網絡服務器軟件 |
知識產權 | 網際協議 | 低于 TCP 和 UDP 的網絡層協議 |
局域網 | 局域網 | 大多數局域網使用基于互聯網的協議,如TCP/IP |
流行 | 郵局協議 | 檢索互聯網郵件 |
休息 | 再現狀態轉移 | 一種流行的 Web 服務體系結構,在響應中使用機器可遵循的鏈接,并且可以在基本 HTTP 上運行 |
短信通信 | 簡單郵件傳輸協議 | 發送互聯網郵件 |
技術合作計劃(TCP | 傳輸和控制協議 | 傳輸層互聯網協議,大多數更高層服務都在其上構建 |
UDP | 通用數據報協議 | 用于低開銷服務(如 VoIP)的傳輸層互聯網協議 |
北卡羅來納大學 | 通用命名約定 | \計算機\共享名\文件名 |
烏里 | 統一資源標識符 | 無處不在的資源命名系統(例如, 或mailto:) |
網址 | 統一資源定位器 | 技術含義(從使用中淡出):URI的子集;通俗含義:URI 的同義詞 |
要使通信正常工作,計算機或設備需要一個地址。互聯網使用兩種尋址系統:
IPv4
目前占主導地位的尋址系統;IPv4 地址的寬度為 32 位。當字符串格式時,IPv4 地址被寫入為四個點分隔的小數(例如,101.102.103.104)。地址在世界上可以是唯一的,也可以在特定中是唯一的(例如在公司網絡上)。
IPv6
較新的 128 位尋址系統。地址采用十六進制格式的字符串格式,帶有冒號分隔符(例如,[3EA0:FFFF:198A:E4A3:4FF2:54fA:41BC:8D31])。.NET 要求在地址兩邊添加方括號。
System.Net 命名空間中的 IPAddress 類表示任一協議中的地址。它有一個接受字節數組的構造函數和一個接受正確格式字符串的靜態 Parse 方法:
IPAddress a1 = new IPAddress (new byte[] { 101, 102, 103, 104 });
IPAddress a2 = IPAddress.Parse ("101.102.103.104");
Console.WriteLine (a1.Equals (a2)); // True
Console.WriteLine (a1.AddressFamily); // InterNetwork
IPAddress a3 = IPAddress.Parse
("[3EA0:FFFF:198A:E4A3:4FF2:54fA:41BC:8D31]");
Console.WriteLine (a3.AddressFamily); // InterNetworkV6
TCP 和 UDP 協議將每個 IP 地址分成 65,535 個端口,允許單個地址上的計算機運行多個應用程序,每個應用程序都在自己的端口上。許多應用程序具有標準的默認端口分配;例如,HTTP 使用端口 80;SMTP 使用端口 25。
從 49152 到 65535 的 TCP 和 UDP 端口是正式未分配的,因此它們非常適合測試和小規模部署。
IP 地址和端口組合在 .NET 中由 IPEndPoint 類表示:
IPAddress a = IPAddress.Parse ("101.102.103.104");
IPEndPoint ep = new IPEndPoint (a, 222); // Port 222
Console.WriteLine (ep.ToString()); // 101.102.103.104:222
防火墻阻止端口。在許多企業環境中,只有少數端口處于打開狀態,通常是端口 80(用于未加密的 HTTP)和端口 443(用于安全 HTTP)。
URI 是一個特殊格式的字符串,用于描述互聯網或 LAN 上的資源,例如網頁、文件或電子郵件地址。示例包括 、ftp://myisp/doc.txt 和 mailto:。確切的格式由 (IETF) 定義。
URI 可以分解為一系列元素,通常是、和。System 命名空間中的 Uri 類僅執行此劃分,為每個元素公開一個屬性,如圖 所示。
當您需要驗證 URI 字符串的格式或將 URI 拆分為其組成部分時,Uri 類非常有用。否則,可以將 URI 簡單地視為字符串 - 大多數網絡方法都會重載以接受 Uri 對象或字符串。
可以通過將以下任何字符串傳遞到其構造函數中來構造 Uri 對象:
文件和 UNC 路徑會自動轉換為 URI:添加“file:”協議,并將反斜杠轉換為正斜杠。Uri 構造函數還會在創建 Uri 之前對字符串執行一些基本的清理,包括將方案和主機名轉換為小寫以及刪除默認和空白端口號。如果提供不帶方案的 URI 字符串(如“”),則會引發 UriFormatException。
Uri 具有 IsLoopback 屬性,該屬性指示 Uri 是否引用本地主機(IP 地址 127.0.0.1)和一個 IsFile 屬性,該屬性指示 Uri 引用本地路徑還是 UNC (IsUnc) 路徑(對于掛載在 文件系統中的 共享,IsUnc 報告 false)。如果 IsFile 返回 true ,則 LocalPath 屬性返回對本地操作系統友好的 AbsolutePath 版本(根據操作系統使用斜杠或反斜杠),您可以在該版本上調用 File.Open 。
Uri 的實例具有只讀屬性。要修改現有的 Uri,請實例化 UriBuilder 對象 - 該對象具有可寫屬性,可以通過其 Uri 屬性轉換回來。
Uri 還提供了比較和減去路徑的方法:
Uri info = new Uri ("http://www.domain.com:80/info/");
Uri page = new Uri ("http://www.domain.com/info/page.html");
Console.WriteLine (info.Host); // www.domain.com
Console.WriteLine (info.Port); // 80
Console.WriteLine (page.Port); // 80 (Uri knows the default HTTP port)
Console.WriteLine (info.IsBaseOf (page)); // True
Uri relative = info.MakeRelativeUri (page);
Console.WriteLine (relative.IsAbsoluteUri); // False
Console.WriteLine (relative.ToString()); // page.html
相對 Uri,例如本例中的 ,如果您調用除 IsAbsoluteUri 和 ToString() 之外的幾乎任何屬性或方法,則會引發異常。你可以直接實例化一個相對的 Uri,如下所示:
Uri u = new Uri ("page.html", UriKind.Relative);
尾部斜杠在 URI 中很重要,如果存在路徑組件,則服務器如何處理請求會有所不同。
例如,在傳統的Web服務器中,給定URI ,您可以期望HTTP Web服務器在站點Web文件夾中的子目錄中查找并返回默認文檔(通常是)。
如果沒有尾部斜杠,Web 服務器將直接在站點的根文件夾中查找一個名為 的文件(不帶擴展名),這通常不是您想要的。如果不存在此類文件,大多數 Web 服務器將假定用戶鍵入錯誤,并將返回 301 錯誤,建議客戶端使用尾部斜杠重試。默認情況下,.NET HTTP 客戶端將以與 Web 瀏覽器相同的方式透明地響應 301,方法是使用建議的 URI 重試。這意味著,如果在應該包含尾部斜杠時省略了尾部斜杠,您的請求仍然有效,但會遭受不必要的額外往返。
Uri 類還提供了靜態輔助方法,例如 EscapeUriString() ,它通過將 ASCII 值大于 127 的所有字符轉換為十六進制表示形式,將字符串轉換為有效的 URL。CheckHostName() 和 CheckSchemeName() 方法接受字符串并檢查它對于給定屬性在語法上是否有效(盡管它們不嘗試確定主機或 URI 是否存在)。
HttpClient 類公開了一個用于 HTTP 客戶端操作的現代 API,取代了舊的 WebClient 和 WebRequest / WebResponse 類型(這些類型已被標記為過時)。
HttpClient 是為了響應基于 HTTP 的 Web API 和 REST 服務的增長而編寫的,并且在處理比簡單地獲取網頁更復雜的協議時提供了良好的體驗。特別:
HttpClient 不支持進度報告。有關解決方案,請參閱 ,或通過 LINQPad 的交互式示例庫。
使用 HttpClient 的最簡單方法是實例化它,然后調用其 Get* 方法之一,傳入一個 URI:
string html = await new HttpClient().GetStringAsync ("http://linqpad.net");
(還有GetByteArrayAsync和GetStreamAsync。HttpClient 中的所有 I/O 綁定方法都是異步的。
與其WebRequest / WebResponse的前身不同,要獲得最佳性能 HttpClient ,重用相同的實例(否則DNS解析之類的事情可能會不必要地重復,并且套接字保持打開的時間超過必要的時間)。HttpClient 允許并發操作,因此以下內容是合法的,并且可以一次下載兩個網頁:
var client = new HttpClient();
var task1 = client.GetStringAsync ("http://www.linqpad.net");
var task2 = client.GetStringAsync ("http://www.albahari.com");
Console.WriteLine (await task1);
Console.WriteLine (await task2);
HttpClient 具有超時屬性和 BaseAddress 屬性,該屬性為每個請求添加前綴 URI。HttpClient有點像一個薄殼:您可能希望在此處找到的大多數其他屬性都是在另一個名為HttpClientHandler的類中定義的。要訪問這個類,你實例化它,然后將實例傳遞到 HttpClient 的構造函數中:
var handler = new HttpClientHandler { UseProxy = false };
var client = new HttpClient (handler);
...
在此示例中,我們告訴處理程序禁用代理支持,這有時可以通過避免自動代理檢測的成本來提高性能。還有一些屬性可以控制 Cookie、自動重定向、身份驗證等(我們將在以下各節以及“使用 HTTP”中介紹這些屬性)。
GetStringAsync 、GetByteArrayAsync 和 GetStreamAsync 方法是調用更通用的 GetAsync 方法的便捷快捷方式,該方法返回:
var client = new HttpClient();
// The GetAsync method also accepts a CancellationToken.
HttpResponseMessage response = await client.GetAsync ("http://...");
response.EnsureSuccessStatusCode();
string html = await response.Content.ReadAsStringAsync();
HttpResponseMessage 公開了用于訪問標頭(請參閱和 HTTP 狀態代碼的屬性。不成功的狀態代碼(如 404(未找到))不會導致引發異常,除非您顯式調用 確保成功狀態代碼 。但是,通信或 DNS 錯誤確實會引發異常。
HttpContent 有一個用于寫入另一個流的 CopyToAsync 方法,該方法在將輸出寫入文件時很有用:
using (var fileStream = File.Create ("linqpad.html"))
await response.Content.CopyToAsync (fileStream);
GetAsync 是對應于 HTTP 的四個動詞的四種方法之一(其他方法是 PostAsync、PutAsync 和 DeleteAsync)。稍后我們將在“上傳表單數據”中演示 PostAsync。
GetAsync 、PostAsync、PutAsync 和 DeleteAsync 都是調用 SendAsync 的快捷方式,SendAsync 是其他所有內容都饋送到的單一低級方法。要使用它,您首先構造一個 HttpRequestMessage :
var client = new HttpClient();
var request = new HttpRequestMessage (HttpMethod.Get, "http://...");
HttpResponseMessage response = await client.SendAsync (request);
response.EnsureSuccessStatusCode();
...
實例化 HttpRequestMessage 對象意味著您可以自定義請求的屬性,例如標頭(請參閱和內容本身,從而允許您上傳數據。
實例化 HttpRequestMessage 對象后,可以通過分配其 Content 屬性來上載內容。此屬性的類型是一個名為 HttpContent 的抽象類。.NET 包含以下用于不同類型內容的具體子類(您也可以編寫自己的子類):
例如:
var client = new HttpClient (new HttpClientHandler { UseProxy = false });
var request = new HttpRequestMessage (
HttpMethod.Post, "http://www.albahari.com/EchoPost.aspx");
request.Content = new StringContent ("This is a test");
HttpResponseMessage response = await client.SendAsync (request);
response.EnsureSuccessStatusCode();
Console.WriteLine (await response.Content.ReadAsStringAsync());
我們之前說過,大多數用于自定義請求的屬性不是在 HttpClient 中定義的,而是在 HttpClientHandler 中定義的。后者實際上是抽象 HttpMessageHandler 類的子類,定義如下:
public abstract class HttpMessageHandler : IDisposable
{
protected internal abstract Task<HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken);
public void Dispose();
protected virtual void Dispose (bool disposing);
}
SendAsync方法是從HttpClient的SendAsync方法調用的。
HttpMessageHandler非常簡單,可以輕松進行子類化,并為HttpClient提供了一個擴展點。
我們可以對 HttpMessageHandler 進行子類化,創建一個處理程序來幫助進行單元測試:
class MockHandler : HttpMessageHandler
{
Func <HttpRequestMessage, HttpResponseMessage> _responseGenerator;
public MockHandler
(Func <HttpRequestMessage, HttpResponseMessage> responseGenerator)
{
_responseGenerator = responseGenerator;
}
protected override Task <HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var response = _responseGenerator (request);
response.RequestMessage = request;
return Task.FromResult (response);
}
}
它的構造函數接受一個函數,該函數告訴模擬者如何從請求生成響應。這是最通用的方法,因為同一個處理程序可以測試多個請求。
SendAsync 是同步的,憑借 Task.FromResult 。我們本可以通過讓我們的響應生成器返回一個 Task<HttpResponseMessage> 來保持異步性,但這是沒有意義的,因為我們可以預期模擬函數運行時間很短。以下是使用我們的模擬處理程序的方法:
var mocker = new MockHandler (request =>
new HttpResponseMessage (HttpStatusCode.OK)
{
Content = new StringContent ("You asked for " + request.RequestUri)
});
var client = new HttpClient (mocker);
var response = await client.GetAsync ("http://www.linqpad.net");
string result = await response.Content.ReadAsStringAsync();
Assert.AreEqual ("You asked for http://www.linqpad.net/", result);
(Assert.AreEqual是您希望在單元測試框架(如NUnit)中找到的方法。
您可以通過子類化 De委派處理程序 來創建調用另一個消息處理程序(生成處理程序鏈)。您可以使用它來實現自定義身份驗證、壓縮和加密協議。下面演示了一個簡單的日志記錄處理程序:
class LoggingHandler : DelegatingHandler
{
public LoggingHandler (HttpMessageHandler nextHandler)
{
InnerHandler = nextHandler;
}
protected async override Task <HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine ("Requesting: " + request.RequestUri);
var response = await base.SendAsync (request, cancellationToken);
Console.WriteLine ("Got response: " + response.StatusCode);
return response;
}
}
請注意,我們在覆蓋 SendAsync 時保持了異步。在重寫任務返回方法時引入異步修飾符是完全合法的,在這種情況下是可取的。
比寫入控制臺更好的解決方案是讓構造函數接受某種日志記錄對象。更好的辦法是接受幾個 Action<T> 委托,告訴它如何記錄請求和響應對象。
是可以路由 HTTP 請求的中介。組織有時會將代理服務器設置為員工訪問互聯網的唯一方式,主要是因為它簡化了安全性。代理有自己的地址,可以要求身份驗證,以便只有 LAN 上的選定用戶才能訪問互聯網。
要將代理與 HttpClient 一起使用,首先創建一個 HttpClientHandler 并分配其 Proxy 屬性,然后將其饋送到 HttpClient 的構造函數中:
WebProxy p = new WebProxy ("192.178.10.49", 808);
p.Credentials = new NetworkCredential ("username", "password", "domain");
var handler = new HttpClientHandler { Proxy = p };
var client = new HttpClient (handler);
...
HttpClientHandler 還有一個 UseProxy 屬性,您可以將其分配給 false,而不是清空 Proxy 屬性以阻止自動檢測。
如果在構造 NetworkCredential 時提供域,則使用基于 Windows 的身份驗證協議。若要使用當前經過身份驗證的 Windows 用戶,請將靜態 CredentialCache.DefaultNetworkCredentials 值分配給代理的 Credentials 屬性。
作為重復設置代理的替代方法,您可以按如下方式設置全局默認值:
HttpClient.DefaultWebProxy = myWebProxy;
您可以向 HttpClient 提供用戶名和密碼,如下所示:
string username = "myuser";
string password = "mypassword";
var handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential (username, password);
var client = new HttpClient (handler);
...
這適用于基于對話框的身份驗證協議(如基本和摘要),并且可通過 AuthenticationManager 類進行擴展。它還支持 Windows NTLM 和 Kerberos(如果在構造 NetworkCredential 對象時包含域名)。如果要使用當前經過身份驗證的 Windows 用戶,可以將“憑據”屬性保留為空,而是將“使用默認憑據”設置為 true 。
當您提供憑據時,HttpClient 會自動協商兼容的協議。在某些情況下,可以選擇:例如,如果檢查來自Microsoft Exchange 服務器 Web 郵件頁面的初始響應,則它可能包含以下標頭:
HTTP/1.1 401 Unauthorized
Content-Length: 83
Content-Type: text/html
Server: Microsoft-IIS/6.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
WWW-Authenticate: Basic realm="exchange.somedomain.com"
X-Powered-By: ASP.NET
Date: Sat, 05 Aug 2006 12:37:23 GMT
401 代碼表示需要授權;“WWW 身份驗證”標頭指示理解的身份驗證協議。但是,如果使用正確的用戶名和密碼配置 HttpClientHandler,則此消息將對你隱藏,因為運行時通過選擇兼容的身份驗證協議,然后使用額外的標頭重新提交原始請求來自動響應。下面是一個示例:
Authorization: Negotiate TlRMTVNTUAAABAAAt5II2gjACDArAAACAwACACgAAAAQ
ATmKAAAAD0lVDRdPUksHUq9VUA==
此機制提供透明度,但會為每個請求生成額外的往返行程。通過將 HttpClientHandler 上的 PreAuthenticate 屬性設置為 true 來避免對同一 URI 的后續請求進行額外的往返。
您可以使用憑據緩存對象強制使用特定的身份驗證協議。憑據緩存包含一個或多個 NetworkCredential 對象,每個對象都以特定協議和 URI 前綴為密鑰。例如,您可能希望在登錄 Exchange Server 時避免使用基本協議,因為它以純文本形式傳輸密碼:
CredentialCache cache = new CredentialCache();
Uri prefix = new Uri ("http://exchange.somedomain.com");
cache.Add (prefix, "Digest", new NetworkCredential ("joe", "passwd"));
cache.Add (prefix, "Negotiate", new NetworkCredential ("joe", "passwd"));
var handler = new HttpClientHandler();
handler.Credentials = cache;
...
身份驗證協議指定為字符串。有效值包括:
Basic, Digest, NTLM, Kerberos, Negotiate
在這種特殊情況下,它將選擇協商,因為服務器在其身份驗證標頭中未指示它支持 Digest。協商是一種 Windows 協議,目前歸結為 Kerberos 或 NTLM,具體取決于服務器的功能,但在部署未來安全標準時可確保應用程序的向前兼容性。
靜態 CredentialCache.DefaultNetworkCredentials 屬性允許您將當前經過身份驗證的 Windows 用戶添加到憑據緩存中,而無需指定密碼:
cache.Add (prefix, "Negotiate", CredentialCache.DefaultNetworkCredentials);
另一種身份驗證方法是直接設置身份驗證標頭:
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue ("Basic",
Convert.ToBase64String (Encoding.UTF8.GetBytes ("username:password")));
...
此策略也適用于自定義身份驗證系統,如 OAuth。
HttpClient 允許您向請求添加自定義 HTTP 標頭,以及在響應中枚舉標頭。標頭只是包含元數據(如消息內容類型或服務器軟件)的鍵/值對。HttpClient 公開具有標準 HTTP 標頭屬性的強類型集合。屬性適用于應用于每個請求的標頭:
var client = new HttpClient (handler);
client.DefaultRequestHeaders.UserAgent.Add (
new ProductInfoHeaderValue ("VisualStudio", "2022"));
client.DefaultRequestHeaders.Add ("CustomHeader", "VisualStudio/2022");
但是,類上的 Headers 屬性用于特定于請求的標頭。
查詢字符串只是附加到帶有問號的 URI 的字符串,用于將簡單數據發送到服務器。可以使用以下語法在查詢字符串中指定多個鍵/值對:
?key1=value1&key2=value2&key3=value3...
下面是一個帶有查詢字符串的 URI:
string requestURI = "http://www.google.com/search?q=HttpClient&hl=fr";
如果您的查詢可能包含符號或空格,則可以使用 Uri 的 EscapeDataString 方法創建一個合法的 URI:
string search = Uri.EscapeDataString ("(HttpClient or HttpRequestMessage)");
string language = Uri.EscapeDataString ("fr");
string requestURI = "http://www.google.com/search?q=" + search +
"&hl=" + language;
此生成的 URI 為:
http://www.google.com/search?q=(HttpClient%20OR%20HttpRequestMessage)&hl=fr
(EscapeDataString 與 EscapeUriString 類似,不同之處在于它還對 & 和 = 等字符進行編碼,否則會弄亂查詢字符串。
若要上載 HTML 表單數據,請創建并填充 FormUrlEncodedContent 對象。然后,可以將其傳遞到 PostAsync 方法中,也可以將其分配給請求的 Content 屬性:
string uri = "http://www.albahari.com/EchoPost.aspx";
var client = new HttpClient();
var dict = new Dictionary<string,string>
{
{ "Name", "Joe Albahari" },
{ "Company", "O'Reilly" }
};
var values = new FormUrlEncodedContent (dict);
var response = await client.PostAsync (uri, values);
response.EnsureSuccessStatusCode();
Console.WriteLine (await response.Content.ReadAsStringAsync());
Cookie 是 HTTP 服務器在響應標頭中發送到客戶端的名稱/值字符串對。Web 瀏覽器客戶端通常會記住 Cookie,并在每次后續請求(到同一地址)中將它們重播到服務器,直到它們到期。Cookie 允許服務器知道它是在與一分鐘前還是昨天的同一客戶端通信,而無需在 URI 中提供混亂的查詢字符串。
默認情況下,HttpClient 會忽略從服務器接收的任何 Cookie。要接受 cookie,請創建一個 CookieContainer 對象并為其分配一個 HttpClientHandler:
var cc = new CookieContainer();
var handler = new HttpClientHandler();
handler.CookieContainer = cc;
var client = new HttpClient (handler);
...
要在將來的請求中重播收到的 Cookie,只需再次使用相同的 CookieContainer 對象即可。或者,您可以從新的 CookieContainer 開始,然后手動添加 cookie,如下所示:
Cookie c = new Cookie ("PREF",
"ID=6b10df1da493a9c4:TM=1179...",
"/",
".google.com");
freshCookieContainer.Add (c);
第三個和第四個參數指示發起方的路徑和域。客戶端上的 CookieContainer 可以容納來自許多不同位置的 Cookie;HttpClient 僅發送路徑和域與服務器路徑和域匹配的 cookie。
如果需要在 .NET 6 中編寫 HTTP 服務器,另一種更高級別的方法是使用最小 API ASP.NET。以下是入門所需的全部內容:
var app = WebApplication.CreateBuilder().Build();
app.MapGet ("/", () => "Hello, world!");
app.Run();
您可以使用 HttpListener 類編寫自己的 .NET HTTP 服務器。下面是一個簡單的服務器,它偵聽端口 51111,等待單個客戶端請求,然后返回一行回復:
using var server = new SimpleHttpServer();
// Make a client request:
Console.WriteLine (await new HttpClient().GetStringAsync
("http://localhost:51111/MyApp/Request.txt"));
class SimpleHttpServer : IDisposable
{
readonly HttpListener listener = new HttpListener();
public SimpleHttpServer() => ListenAsync();
async void ListenAsync()
{
listener.Prefixes.Add ("http://localhost:51111/MyApp/"); // Listen on
listener.Start(); // port 51111
// Await a client request:
HttpListenerContext context = await listener.GetContextAsync();
// Respond to the request:
string msg = "You asked for: " + context.Request.RawUrl;
context.Response.ContentLength64 = Encoding.UTF8.GetByteCount (msg);
context.Response.StatusCode = (int)HttpStatusCode.OK;
using (Stream s = context.Response.OutputStream)
using (StreamWriter writer = new StreamWriter (s))
await writer.WriteAsync (msg);
}
public void Dispose() => listener.Close();
}
OUTPUT: You asked for: /MyApp/Request.txt
在Windows上,HttpListener在內部不使用.NET Socket對象;相反,它調用Windows HTTP Server API。這允許計算機上的許多應用程序偵聽相同的 IP 地址和端口,只要每個應用程序注冊不同的地址前綴即可。在我們的示例中,我們注冊了前綴 http://localhost/myapp,因此另一個應用程序可以自由偵聽另一個前綴(如 http://localhost/anotherapp)上的同一 IP 和端口。這是有價值的,因為在公司防火墻上打開新端口在政治上可能很困難。
當您調用 GetContext 時,HttpListener 會等待下一個客戶端請求,返回具有請求和響應屬性的對象。每個都類似于客戶端請求或響應,但從服務器的角度來看。例如,您可以讀取和寫入標頭和 Cookie 到請求和響應對象,就像在客戶端一樣。
您可以根據預期的客戶端受眾選擇完全支持 HTTP 協議功能的程度。至少應設置每個請求的內容長度和狀態代碼。
這是一個非常簡單的網頁服務器,
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
class WebServer
{
HttpListener _listener;
string _baseFolder; // Your web page folder.
public WebServer (string uriPrefix, string baseFolder)
{
_listener = new HttpListener();
_listener.Prefixes.Add (uriPrefix);
_baseFolder = baseFolder;
}
public async void Start()
{
_listener.Start();
while (true)
try
{
var context = await _listener.GetContextAsync();
Task.Run (() => ProcessRequestAsync (context));
}
catch (HttpListenerException) { break; } // Listener stopped.
catch (InvalidOperationException) { break; } // Listener stopped.
}
public void Stop() => _listener.Stop();
async void ProcessRequestAsync (HttpListenerContext context)
{
try
{
string filename = Path.GetFileName (context.Request.RawUrl);
string path = Path.Combine (_baseFolder, filename);
byte[] msg;
if (!File.Exists (path))
{
Console.WriteLine ("Resource not found: " + path);
context.Response.StatusCode = (int) HttpStatusCode.NotFound;
msg = Encoding.UTF8.GetBytes ("Sorry, that page does not exist");
}
else
{
context.Response.StatusCode = (int) HttpStatusCode.OK;
msg = File.ReadAllBytes (path);
}
context.Response.ContentLength64 = msg.Length;
using (Stream s = context.Response.OutputStream)
await s.WriteAsync (msg, 0, msg.Length);
}
catch (Exception ex) { Console.WriteLine ("Request error: " + ex); }
}
}
以下代碼啟動了操作:
// Listen on port 51111, serving files in d:\webroot:
var server = new WebServer ("http://localhost:51111/", @"d:\webroot");
try
{
server.Start();
Console.WriteLine ("Server running... press Enter to stop");
Console.ReadLine();
}
finally { server.Stop(); }
您可以使用任何 Web 瀏覽器在客戶端對此進行測試;在這種情況下,URI 將 http://localhost:51111/ 加上網頁的名稱。
如果其他軟件競爭同一端口,HttpListener 將不會啟動(除非該軟件也使用 Windows HTTP Server API)。可能偵聽默認端口 80 的應用程序示例包括 Web 服務器或對等程序(如 Skype)。
我們對異步函數的使用使該服務器具有可擴展性和效率。但是,從用戶界面 (UI) 線程開始會阻礙可伸縮性,因為對于每個,執行會在每次等待后反彈回 UI 線程。鑒于我們沒有共享狀態,產生這樣的開銷特別沒有意義,因此在 UI 場景中,我們會像這樣離開 UI 線程。
Task.Run (Start);
或者在調用 GetContextAsync 后調用 ConfigureAwait(false)。
請注意,我們使用 Task.Run 來調用 ProcessRequestAsync,即使該方法已經是異步的。這允許調用方處理另一個請求,而不必首先等待方法的同步階段(直到第一個等待)。
靜態 Dns 類封裝 DNS,該 DNS 在原始 IP 地址(如 66.135.192.87)和人類友好域名(如 )之間進行轉換。
方法從域名轉換為 IP 地址(或多個地址):
foreach (IPAddress a in Dns.GetHostAddresses ("albahari.com"))
Console.WriteLine (a.ToString()); // 205.210.42.167
GetHostEntry 方法則相反,從地址轉換為域名:
IPHostEntry entry = Dns.GetHostEntry ("205.210.42.167");
Console.WriteLine (entry.HostName); // albahari.com
GetHostEntry 還接受 IPAddress 對象,因此您可以將 IP 地址指定為字節數組:
IPAddress address = new IPAddress (new byte[] { 205, 210, 42, 167 });
IPHostEntry entry = Dns.GetHostEntry (address);
Console.WriteLine (entry.HostName); // albahari.com
當您使用 WebRequest 或 TcpClient 等類時,域名會自動解析為 IP 地址。但是,如果您計劃在應用程序的生命周期內向同一地址發出許多網絡請求,則有時可以通過首先使用 Dns 將域名顯式轉換為 IP 地址,然后從該點開始直接與 IP 地址通信來提高性能。這避免了重復往返解析相同的域名,并且在傳輸層(通過 TcpClient 、UdpClient 或 Socket )處理時可能會有所幫助。
DNS 類還提供可等待的基于任務的異步方法:
foreach (IPAddress a in await Dns.GetHostAddressesAsync ("albahari.com"))
Console.WriteLine (a.ToString());
命名空間中的 SmtpClient 類允許您通過無處不在的簡單郵件傳輸協議 (SMTP) 發送郵件。要發送簡單的文本消息,請實例化 SmtpClient ,將其 Host 屬性設置為 SMTP 服務器地址,然后調用 發送 :
SmtpClient client = new SmtpClient();
client.Host = "mail.myserver.com";
client.Send ("from@adomain.com", "to@adomain.com", "subject", "body");
構造 MailMessage 對象會公開更多選項,包括添加附件的功能:
SmtpClient client = new SmtpClient();
client.Host = "mail.myisp.net";
MailMessage mm = new MailMessage();
mm.Sender = new MailAddress ("kay@domain.com", "Kay");
mm.From = new MailAddress ("kay@domain.com", "Kay");
mm.To.Add (new MailAddress ("bob@domain.com", "Bob"));
mm.CC.Add (new MailAddress ("dan@domain.com", "Dan"));
mm.Subject = "Hello!";
mm.Body = "Hi there. Here's the photo!";
mm.IsBodyHtml = false;
mm.Priority = MailPriority.High;
Attachment a = new Attachment ("photo.jpg",
System.Net.Mime.MediaTypeNames.Image.Jpeg);
mm.Attachments.Add (a);
client.Send (mm);
為了阻止垃圾郵件發送者,互聯網上的大多數SMTP服務器僅接受來自經過身份驗證的連接的連接,并要求通過SSL進行通信。
var client = new SmtpClient ("smtp.myisp.com", 587)
{
Credentials = new NetworkCredential ("me@myisp.com", "MySecurePass"),
EnableSsl = true
};
client.Send ("me@myisp.com", "someone@somewhere.com", "Subject", "Body");
Console.WriteLine ("Sent");
通過更改 DeliveryMethod 屬性,可以指示 SmtpClient 改用 IIS 發送郵件,或者只是將每封郵件寫入指定目錄中的 文件。這在開發過程中可能很有用。
SmtpClient client = new SmtpClient();
client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = @"c:\mail";
TCP 和 UDP 構成了傳輸層協議,大多數互聯網和 LAN 服務都在其上構建。HTTP(版本 2 及更低版本)、FTP 和 SMTP 使用 TCP;DNS 和 HTTP 版本 3 使用 UDP。TCP 是面向連接的,包括可靠性機制;UDP 是無連接的,開銷較低,并支持廣播。使用UDP,IP語音(VoIP)也是如此。
與較高層相比,傳輸層提供了更大的靈活性,并可能提高性能,但它要求您自己處理身份驗證和加密等任務。
使用 .NET 中的 TCP,您可以選擇更易于使用的 TcpClient 和 TcpListener 外觀類,也可以選擇功能豐富的 Socket 類。 (實際上,您可以混合搭配,因為 TcpClient 通過 Client 屬性公開基礎套接字對象。Socket 類公開了更多的配置選項,并允許直接訪問網絡層 (IP) 和非基于 Internet 的協議,例如 Novell 的 SPX/IPX。
與其他協議一樣,TCP 區分客戶端和服務器:客戶端發起請求,而服務器等待請求。下面是同步 TCP 客戶端請求的基本結構:
using (TcpClient client = new TcpClient())
{
client.Connect ("address", port);
using (NetworkStream n = client.GetStream())
{
// Read and write to the network stream...
}
}
TcpClient 的連接方法會阻塞,直到建立連接(ConnectAsync 是異步等價物)。然后,NetworkStream提供了一種雙向通信方式,用于從服務器發送和接收字節的數據。
一個簡單的TCP服務器如下所示:
TcpListener listener = new TcpListener (<ip address>, port);
listener.Start();
while (keepProcessingRequests)
using (TcpClient c = listener.AcceptTcpClient())
using (NetworkStream n = c.GetStream())
{
// Read and write to the network stream...
}
listener.Stop();
TcpListener 需要偵聽的本地 IP 地址(例如,具有兩個網卡的計算機可以有兩個地址)。您可以使用 IPAddress.Any 指示它偵聽所有(或唯一)本地 IP 地址。AcceptTcpClient 阻塞,直到收到客戶端請求(同樣,還有一個異步版本),此時我們調用 GetStream ,就像在客戶端一樣。
在傳輸層工作時,您需要決定誰何時通話以及通話多長時間的協議,就像使用對講機一樣。如果雙方同時交談或傾聽,溝通就會中斷!
讓我們發明一個協議,在這個協議中,客戶端首先說“你好”,然后服務器通過說“你好馬上回來!代碼如下:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
new Thread (Server).Start(); // Run server method concurrently.
Thread.Sleep (500); // Give server time to start.
Client();
void Client()
{
using (TcpClient client = new TcpClient ("localhost", 51111))
using (NetworkStream n = client.GetStream())
{
BinaryWriter w = new BinaryWriter (n);
w.Write ("Hello");
w.Flush();
Console.WriteLine (new BinaryReader (n).ReadString());
}
}
void Server() // Handles a single client request, then exits.
{
TcpListener listener = new TcpListener (IPAddress.Any, 51111);
listener.Start();
using (TcpClient c = listener.AcceptTcpClient())
using (NetworkStream n = c.GetStream())
{
string msg = new BinaryReader (n).ReadString();
BinaryWriter w = new BinaryWriter (n);
w.Write (msg + " right back!");
w.Flush(); // Must call Flush because we're not
} // disposing the writer.
listener.Stop();
}
// OUTPUT: Hello right back!
在此示例中,我們使用 localhost 環回在同一臺計算機上運行客戶端和服務器。我們任意選擇了未分配范圍內的端口(高于 49152),并使用 BinaryWriter 和 BinaryReader 對文本消息進行編碼。我們避免關閉或處置讀取器和編寫器,以便在我們的對話完成之前保持底層 NetworkStream 打開。
BinaryReader 和 BinaryWriter 似乎是讀取和寫入字符串的奇怪選擇。但是,它們比StreamReader和StreamWriter有一個主要優勢:它們在字符串前面加上一個指示長度的整數,因此BinaryReader總是確切地知道要讀取多少字節。如果你調用StreamReader.ReadToEnd,你可能會無限期地阻止,因為NetworkStream沒有終點!只要連接處于打開狀態,網絡流就永遠無法確定客戶端不會發送更多數據。
StreamReader實際上完全超出了NetworkStream的界限,即使你只打算調用ReadLine。這是因為 StreamReader 具有預讀緩沖區,這可能導致它讀取的字節數超過當前可用的字節數,從而無限期阻塞(或直到套接字超時)。其他流(如 FileStream)不會遭受與 StreamReader 的這種不兼容,因為它們有一個明確的 — 此時 Read 立即返回值 0 。
TcpClient 和 TcpListener 提供基于任務的異步方法,以實現可擴展的并發性。使用這些只是將阻止方法調用替換為其 *Async 版本并等待返回的任務的問題。
在下面的示例中,我們編寫了一個異步 TCP 服務器,該服務器接受長度為 5,000 字節的請求,反轉字節,然后將其發送回客戶端:
async void RunServerAsync ()
{
var listener = new TcpListener (IPAddress.Any, 51111);
listener.Start ();
try
{
while (true)
Accept (await listener.AcceptTcpClientAsync ());
}
finally { listener.Stop(); }
}
async Task Accept (TcpClient client)
{
await Task.Yield ();
try
{
using (client)
using (NetworkStream n = client.GetStream ())
{
byte[] data = new byte [5000];
int bytesRead = 0; int chunkSize = 1;
while (bytesRead < data.Length && chunkSize > 0)
bytesRead += chunkSize =
await n.ReadAsync (data, bytesRead, data.Length - bytesRead);
Array.Reverse (data); // Reverse the byte sequence
await n.WriteAsync (data, 0, data.Length);
}
}
catch (Exception ex) { Console.WriteLine (ex.Message); }
}
這樣的程序是可擴展的,因為它不會在請求期間阻塞線程。因此,如果 1,000 個客戶端通過慢速網絡連接同時連接(例如,每個請求從開始到結束需要幾秒鐘),則該程序在這段時間內不需要 1,000 個線程(與同步解決方案不同)。相反,它僅在 await 表達式之前和之后執行代碼所需的短時間內租用線程。
.NET 不提供對 POP3 的應用程序層支持,因此您需要在 TCP 層寫入才能從 POP3 服務器接收郵件。幸運的是,這是一個簡單的協議;POP3 對話是這樣的:
客戶 | 郵件服務器 | 筆記 |
客戶端連接... | +好的 你好。 | 歡迎辭 |
用戶喬 | +確定 需要密碼。 | |
通票密碼 | +確定已登錄。 | |
列表 | +OK 1 1876 2 5412 3 845 . | 列出服務器上每封郵件的 ID 和文件大小 |
RETR 1 | +OK 1876 八位字節 . | 檢索具有指定 ID 的郵件 |
德勒 1 | +確定已刪除。 | 從服務器中刪除郵件 |
退出 | +好的再見。 |
每個命令和響應都由換行符 (CR + LF) 終止,但多行 LIST 和 RETR 命令除外,它們由單獨行上的單個點終止。因為我們不能將 StreamReader 與 網絡流 ,我們可以從編寫一個輔助方法開始,以非緩沖方式讀取一行文本:
string ReadLine (Stream s)
{
List<byte> lineBuffer = new List<byte>();
while (true)
{
int b = s.ReadByte();
if (b == 10 || b < 0) break;
if (b != 13) lineBuffer.Add ((byte)b);
}
return Encoding.UTF8.GetString (lineBuffer.ToArray());
}
我們還需要一個幫助程序方法來發送命令。因為我們總是期望收到以 +OK 開頭的響應,所以我們可以同時讀取和驗證響應:
void SendCommand (Stream stream, string line)
{
byte[] data = Encoding.UTF8.GetBytes (line + "\r\n");
stream.Write (data, 0, data.Length);
string response = ReadLine (stream);
if (!response.StartsWith ("+OK"))
throw new Exception ("POP Error: " + response);
}
編寫這些方法后,檢索郵件的工作很容易。我們在端口 110(默認 POP3 端口)上建立 TCP 連接,然后開始與服務器通信。在此示例中,我們將每封郵件寫入擴展名為 的隨機命名文件,然后再從服務器中刪除郵件:
using (TcpClient client = new TcpClient ("mail.isp.com", 110))
using (NetworkStream n = client.GetStream())
{
ReadLine (n); // Read the welcome message.
SendCommand (n, "USER username");
SendCommand (n, "PASS password");
SendCommand (n, "LIST"); // Retrieve message IDs
List<int> messageIDs = new List<int>();
while (true)
{
string line = ReadLine (n); // e.g., "1 1876"
if (line == ".") break;
messageIDs.Add (int.Parse (line.Split (' ')[0] )); // Message ID
}
foreach (int id in messageIDs) // Retrieve each message.
{
SendCommand (n, "RETR " + id);
string randomFile = Guid.NewGuid().ToString() + ".eml";
using (StreamWriter writer = File.CreateText (randomFile))
while (true)
{
string line = ReadLine (n); // Read next line of message.
if (line == ".") break; // Single dot = end of message.
if (line == "..") line = "."; // "Escape out" double dot.
writer.WriteLine (line); // Write to output file.
}
SendCommand (n, "DELE " + id); // Delete message off server.
}
SendCommand (n, "QUIT");
}
可以在 NuGet 上找到開源 POP3 庫,這些庫為協議方面提供支持,例如身份驗證 TLS/SSL 連接、MIME 分析等。
者:hetu666
來源:CSDN
HTML(HyperText Markup Language) 不是一門編程語言,而是一種用來告知瀏覽器如何組織頁面的標記語言。
HTML 可復雜、可簡單,一切取決于開發者。它由一系列的元素組成,這些元素可以用來包圍不同部分的內容,使其以某種方式呈現或者工作。 一對標簽可以為一段文字或者一張圖片添加超鏈接,將文字設置為斜體,改變字號,等等
元素的主要部分有:
1開始標簽(Opening tag):包含元素的名稱(本例為 p),被左、右角括號所包圍。表示元素從這里開始或者開始起作用 —— 在本例中即段落由此開始。
2結束標簽(Closing tag):與開始標簽相似,只是其在元素名之前包含了一個斜杠。這表示著元素的結尾 —— 在本例中即段落在此結束。初學者常常會犯忘記包含結束標簽的錯誤,這可能會產生一些奇怪的結果。
3內容(Content):元素的內容,本例中就是所輸入的文本本身。
4元素(Element):開始標簽、結束標簽與內容相結合,便是一個完整的元素。
塊級元素在頁面中以塊的形式展現 —— 相對于其前面的內容它會出現在新的一行,其后的內容也會被擠到下一行展現。塊級元素通常用于展示頁面上結構化的內容,例如段落、列表、導航菜單、頁腳等等。一個以block形式展現的塊級元素不會被嵌套進內聯元素中,但可以嵌套在其它塊級元素中。
<p>第四</p><p>第五</p><p>第六</p>
效果:
一個屬性必須包含如下內容:
1,在元素和屬性之間有個空格space (如果已經有一個或多個屬性,就與前一個屬性之間有一個空格.)
2屬性后面緊跟著一個“=”符號.
3,有一個屬性值,由一對引號“ ”引起來.
有時你會看到沒有值的屬性,它是合法的。這些屬性被稱為布爾屬性,他們只能有跟它的屬性名一樣的屬性值。
<!-- 使用disabled屬性來防止終端用戶輸入文本到輸入框中 --> <input type="text" disabled> <!-- 下面這個輸入框沒有disabled屬性,所以用戶可以向其中輸入 --> <input type="text">
在目前為止,本章內容所有的屬性都是由雙引號來包裹。也許在一些HTML中,你以前也見過單引號。這只是風格的問題,你可以從中選擇一個你喜歡的。以下兩種情況都可以:
<a >示例站點鏈接</a> <a >示例站點鏈接</a>
但你應該注意單引號和雙引號不能在一個屬性值里面混用。下面的語法是錯誤的:
<a >示例站點鏈接</a>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>我的測試站點</title> </head> <body> <p>這是我的頁面</p> </body> </html>
1,<!DOCTYPE html>: 聲明文檔類型. 很久以前,早期的HTML(大約1991年2月),文檔類型聲明類似于鏈接,規定了HTML頁面必須遵從的良好規則,能自動檢測錯誤和其他有用的東西。使用如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
然而現在沒有人再這樣寫,需要保證每一個東西都正常工作已成為歷史。你只需要知道<!DOCTYPE html>是最短的有效的文檔聲明。
2,<html></html>: <html>元素。這個元素包裹了整個完整的頁面,是一個根元素。
3,<head></head>: <head>元素. 這個元素是一個容器,它包含了所有你想包含在HTML頁面中但不想在HTML頁面中顯示的內容。這些內容包括你想在搜索結果中出現的關鍵字和頁面描述,CSS樣式,字符集聲明等等。以后的章節能學到更多關于<head>元素的內容。
4.<meta charset="utf-8">: 這個元素設置文檔使用utf-8字符集編碼,utf-8字符集包含了人類大部分的文字。基本上他能識別你放上去的所有文本內容。毫無疑問要使用它,并且它能在以后避免很多其他問題。
5.<title></title>: 設置頁面標題,出現在瀏覽器標簽上,當你標記/收藏頁面時它可用來描述頁面。
6.<body></body>: <body>元素。 包含了你訪問頁面時所有顯示在頁面上的內容,文本,圖片,音頻,游戲等等。
代碼中包含的空格是沒有必要的;下面的兩個代碼片段是等價的:
<p>狗 狗 很 呆 萌。</p> <p>狗 狗 很 呆 萌。</p>
渲染這些代碼的時候,HTML解釋器會將連續出現的空白字符減少為一個單獨的空格符。
那么為什么我們會使用那么多的空白呢? 可讀性 —— 如果你的代碼被很好地進行格式化,那么就很容易理解你的代碼。
為了將一段HTML中的內容置為注釋,你需要將其用特殊的記號<!--和-->包括起來, 比如:
<p>我在注釋外!</p> <!-- <p>我在注釋內!</p> -->
HTML 頭部是包含在<head> 元素里面的內容。不像 <body>元素的內容會顯示在瀏覽器中,head 里面的內容不會在瀏覽器中顯示,它的作用是包含一些頁面的元數據。
元數據就是描述數據的數據,而HTML有一個“官方的”方式來為一個文檔添加元數據—— <meta> 。有很多不同種類的 <meta> 可以被包含進你的頁面的<head>元素,比如。
指定字符的編碼
<meta charset="utf-8">
這個元素簡單的指定了文檔的字符編碼 —— 在這個文檔中被允許使用的字符集。 utf-8 是一個通用的字符集,它包含了任何人類語言中的大部分的字符。
添加作者和描述
CSS和JavaScript
如今,幾乎你使用的所有網站都會使用css讓網頁更加炫酷,使用js讓網頁有交互功能,比如視頻播放器,地圖,游戲以及更多功能。這些應用在網頁中很常見,它們分別使用<link>元素以及 <script> 元素。
<link>元素經常位于文檔的頭部。這個link元素有2個屬性,rel="stylesheet"表明這是文檔的樣式表,而 href包含了樣式表文件的路徑:
<link rel="stylesheet" href="my-css-file.css">
<script>部分沒必要非要放在文檔頭部;實際上,把它放在文檔的尾部(在 </body>標簽之前)是一個更好的選擇,這樣可以確保在加載腳本之前瀏覽器已經解析了HTML內容(如果腳本加載某個不存在的元素,瀏覽器會報錯)。
<script src="my-js-file.js"></script>
在HTML中,每個段落是通過<p>元素標簽進行定義的, 比如下面這樣:
<p>我是一個段落,千真萬確。</p>
每個標題(Heading)是通過“標題標簽”進行定義的:
<h1>我是文章的標題</h1>
這里有六個標題元素標簽 —— <h1>、<h2>、<h3>、<h4>、<h5>、<h6>。每個元素代表文檔中不同級別的內容; <h1> 表示主標題(the main heading),<h2> 表示二級子標題(subheadings),<h3> 表示三級子標題(sub-subheadings),等等。
<ol> <li>先用蛋白一個、鹽半茶匙及淀粉兩大匙攪拌均勻,調成“腌料”,雞胸肉切成約一厘米見方的碎丁并用“腌料”攪拌均勻,腌漬半小時。</li> <li>用醬油一大匙、淀粉水一大匙、糖半茶匙、鹽四分之一茶匙、白醋一茶匙、蒜末半茶匙調拌均勻,調成“綜合調味料”。</li> <li>雞丁腌好以后,色拉油下鍋燒熱,先將雞丁倒入鍋內,用大火快炸半分鐘,炸到變色之后,撈出來瀝干油汁備用。</li> <li>在鍋里留下約兩大匙油,燒熱后將切好的干辣椒下鍋,用小火炒香后,再放入花椒粒和蔥段一起爆香。隨后雞丁重新下鍋,用大火快炒片刻后,再倒入“綜合調味料”繼續快炒。 <ul> <li>如果你采用正宗川菜做法,最后只需加入花生米,炒拌幾下就可以起鍋了。</li> <li>如果你在北方,可加入黃瓜丁、胡蘿卜丁和花生米,翻炒后起鍋。</li> </ul> </li> </ol>
<i> 被用來傳達傳統上用斜體表達的意義:外國文字,分類名稱,技術術語,一種思想……
<b> 被用來傳達傳統上用粗體表達的意義:關鍵字,產品名稱,引導句……
<u> 被用來傳達傳統上用下劃線表達的意義:專有名詞,拼寫錯誤……
<strong>強調重要的詞
通過將文本轉換為<a>元素內的鏈接來創建基本鏈接, 給它一個href屬性(也稱為目標),它將包含您希望鏈接指向的網址。
<p>I'm creating a link to <a href="https://www.***.com/">***</a>. </p>
使用title屬性添加支持信息
<p>I'm creating a link to <a title="這是百度">百度</a>. </p>
I'm creating a link to 百度.
塊級鏈接
可以將一些內容轉換為鏈接,甚至是塊級元素。例如你想要將一個圖像轉換為鏈接,你只需把圖像元素放到<a></a>標簽中間。
文檔片段
超鏈接除了可以鏈接到文檔外,也可以鏈接到HTML文檔的特定部分(被稱為文檔片段)。要做到這一點,你必須首先給要鏈接到的元素分配一個id屬性。例如,如果你想鏈接到一個特定的標題,可以這樣做:
<h2 id="Mailing_address">Mailing address</h2>
然后鏈接到那個特定的id,可以在URL的結尾使用一個哈希符號(#)指向它,例如:
<p>Want to write us a letter? Use our <a href="contacts.html#Mailing_address">mailing address</a>.</p>
你甚至可以在同一份文檔下,通過鏈接文檔片段,來鏈接到同一份文檔的另一部分:
<p>The <a href="#Mailing_address">company mailing address</a> can be found at the bottom of this page.</p>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>增加學生</title> </head> <body> <form method="post" action="/community/alpha/student"> <p> 姓名:<input type="text" name="name"> </p> <p> 年齡:<input type="text" name="age"> </p> <p> <input type="submit" value="保存"> </p> </form> </body> </html>
//post @RequestMapping(path = "/student",method = RequestMethod.POST) @ResponseBody public String saveStudent(String name,int age){ System.out.println(name); System.out.println(age); return "success"; }
大部分用來定義表單小部件的元素都有一些他們自己的屬性。然而,在所有表單元素中都有一組通用屬性,它們可以對這些小部件進行控制。下面是這些通用屬性的列表:
密碼框
通過設置type屬性值為password來設置該類型框:
<input type="password" id="pwd" name="pwd">
搜索框
通過設置 type屬性值為 search 來設置該類型框:
<input type="search" id="search" name="search">
電話號碼欄:
通過 type屬性的 tel 值設置該類型框:
<input type="tel" id="tel" name="tel">
URL 欄
通過type屬性的url 值設置該類型框:
<input type="url" id="url" name="url">
多行文本框
多行文本框專指使用<textarea>元素,而不是使用<input>元素。
<textarea cols="30" rows="10"></textarea>
在HTML表單中,有三種按鈕:
Submit
將表單數據發送到服務器。對于<button>元素, 省略 type 屬性 (或是一個無效的 type 值) 的結果就是一個提交按鈕.
Reset
將所有表單小部件重新設置為它們的默認值。
Anonymous
沒有自動生效的按鈕,但是可以使用JavaScript代碼進行定制。
每次向服務器發送數據時,都需要考慮安全性。到目前為止,HTML表單是最常見的攻擊路徑(可能發生攻擊的地方)。這些問題從來都不是來自HTML表單本身,它們來自于服務器如何處理數據。
根據你所做的事情,你會遇到一些非常有名的安全問題:
跨站腳本(XSS)和跨站點請求偽造(CSRF)是常見的攻擊類型,它們發生在當您將用戶發送的數據顯示給這個用戶或另一個用戶時。
XSS允許攻擊者將客戶端腳本注入到其他用戶查看的Web頁面中。攻擊者可以使用跨站點腳本攻擊的漏洞來繞過諸如同源策略之類的訪問控制。這些攻擊的影響可能從一個小麻煩到一個重大的安全風險。
CSRF攻擊類似于XSS攻擊,因為它們以相同的方式開始攻擊——向Web頁面中注入客戶端腳本——但它們的目標是不同的。CSRF攻擊者試圖將權限升級到特權用戶(比如站點管理員)的級別,以執行他們不應該執行的操作(例如,將數據發送給一個不受信任的用戶)。
XSS攻擊利用用戶對web站點的信任,而CSRF攻擊則利用網站對其用戶的信任。
為了防止這些攻擊,您應該始終檢查用戶發送給服務器的數據(如果需要顯示),盡量不要顯示用戶提供的HTML內容。相反,您應該對用戶提供的數據進行處理,這樣您就不會逐字地顯示它。當今市場上幾乎所有的框架都實現了一個最小的過濾器,它可以從任何用戶發送的數據中刪除HTML<script>、<iframe> 和<object>元素。這有助于降低風險,但并不一定會消除風險。
SQL 注入是一種試圖在目標web站點使用的數據庫上執行操作的攻擊類型。這通常包括發送一個SQL請求,希望服務器能夠執行它(通常發生在應用服務器試圖存儲由用戶發送的數據時)。這實際上是攻擊網站的主要途徑之一。
其后果可能是可怕的,從數據丟失到通過使用特權升級控制整個網站基礎設施的攻擊。這是一個非常嚴重的威脅,您永遠不應該存儲用戶發送的數據,而不執行一些清理工作(例如,在php/mysql基礎設施上使用mysql_real_escape_string()
這種類型的攻擊出現在當您的應用程序基于表單上用戶的數據輸入構建HTTP頭部或電子郵件時。這些不會直接損害您的服務器或影響您的用戶,但它們會引發一個更深入的問題,例如會話劫持或網絡釣魚攻擊。
這些攻擊大多是無聲的,并且可以將您的服務器變成僵尸。
偏執:永遠不要相信你的用戶
那么,你如何應對這些威脅呢?這是一個遠遠超出本指南的主題,不過有一些規則需要牢記。最重要的原則是:永遠不要相信你的用戶,包括你自己;即使是一個值得信賴的用戶也可能被劫持。
所有到達服務器的數據都必須經過檢查和消毒。總是這樣。沒有例外。
遠離有潛在危險的字符轉義。應該如何謹慎使用的特定字符取決于所使用的數據的上下文和所使用的服務器平臺,但是所有的服務器端語言都有相應的功能。
限制輸入的數據量,只允許有必要的數據。
沙箱上傳文件(將它們存儲在不同的服務器上,只允許通過不同的子域訪問文件,或者通過完全不同的域名訪問文件更好)。
最后,我自己是一名從事了多年開發的JAVA老程序員,辭職目前在做自己的java私人定制課程,今年年初我花了一個月整理了一份最適合2019年學習的java學習干貨,可以送給每一位喜歡java的小伙伴,想要獲取的可以關注我的頭條號并在后臺私信我:交流,即可免費獲取。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。