C# -- HttpWebRequest 和 HttpWebResponse 的使用javascript
結合使用HttpWebRequest 和 HttpWebResponse,來判斷一個網頁地址是否能夠正常訪問。css
1.舉例html
class Program { static void Main(string[] args) { string strUrl = "https://www.baidu.com"; HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(strUrl); HttpWebResponse wrsp = (HttpWebResponse)wreq.GetResponse(); if (wrsp.StatusCode == HttpStatusCode.OK) { Console.WriteLine(strUrl+ " --http response正常\r\n"); } else { Console.WriteLine(strUrl + " --http response出現異常!\r\n"); } Console.ReadKey(); } }
2. 運行結果:前端
C#編寫掃雷遊戲
翻看了下之前大學學習的一些小項目,忽然發現有個項目比較有意思,以爲有必要把它分享出來。固然如今看來,裏面有不少的不足之處,但因博主如今已經工做,沒有時間再去優化。這個項目就是利用C#編寫一個Windows系統下的掃雷小遊戲。html5
首先講下掃雷小遊戲的玩法:java
(1)掃雷就是要把全部非地雷的格子揭開即勝利;踩到地雷格子就算失敗。jquery
(2)當點開的格子不是地雷區域的時候,該格子會顯示一個數字,該數字表示的含義就是該格子周邊有多少個地雷。git
(3)同時點開的若是非地雷的格子,周邊連貫的非地雷區域都會自動被掃描打開,直到遇到旁邊有雷區的時候中止。github
(4)當你判斷出格子是地雷的時候,你可使用鼠標右鍵將該塊方格標記爲雷區。當不肯定的時候,你可標記個問號以待肯定。web
下面來講下我大學時候實現這個掃雷小遊戲的思路:
(1)由於雷區是一個個格子聯合組成的,那咱們可使用winform程序自帶的系統按鈕控件Button來實現雷區方格。
(2)表明雷區方格的Button按鈕須要實現下面幾個事件:鼠標左鍵點擊掃雷事件,鼠標右鍵點擊標記雷區事件,鼠標右鍵點擊標記問號區域事件。
(3)爲了更好的實現遊戲的可玩性,增長一個自由設置地雷數量的小功能,可自行設置雷區包含的地雷數量,設置完成後,自動刷新界面,從新部署地雷。
(4)咱們將雷區的方格存儲在一個全局的二維數組中,Form窗體在初始化的時候,自動生成面板區域的Button按鈕列表。
(5)爲了實現每次玩遊戲的時候,地雷分佈不一致,咱們在Button列表生成後。隨機抽取出某些Button按鈕做爲地雷分佈點,並記錄該Button的雷區屬性爲含有地雷。
(6)算法中的關鍵:遞歸算法計算雷區。當點擊某個方格的時候,若是該方格是雷區,則直接Game Over,若是不是的話,則咱們須要一個算法去計算旁邊區域的地雷數量,以及旁邊區域沒有地雷的區域,當沒有地雷的區域連成一片的時候,咱們須要使用遞歸算法,去查找二維數組,找到對應的連片非雷區,將之打開。
(7)如何設置方格的狀態:當鼠標左鍵點擊的方塊區域非雷區的時候,咱們將Button按鈕的屬性設置爲Disabled便可呈現打開的狀態。當鼠標左鍵打開的方格是雷區時候,此時咱們能夠將全部地雷區域塊的Button的背景圖設置爲地雷圖片,並播放相應的爆炸音效,彈出遊戲終止界面便可。當鼠標右鍵標記雷區或者待肯定區域的時候,只須要更改Button的背景圖便可。固然上述全部點擊操做,都得判斷Button方格區域當前的狀態值:初始化狀態、已標記爲待肯定狀態、已標記爲雷區。
遊戲的最終效果圖以下:
咱們下面來剖析下幾個關鍵點,由於代碼量比較多,我就不所有詳細剖析了。
首先咱們定義一個LeiButton類,這個類繼承於系統控件Button,增長x,y,youlei三個字段,x表示二維數組的第一個索引,y表示二維數組的第二個索引值,youlei用於標記Button方塊區域按鈕的狀態(0表示無雷,1表示有雷)。同時咱們使用Button按鈕類自帶的一個Tag屬性標記該方塊區域是否被翻開。具體定義以下:
而後咱們在窗體對象Form類中定義一些經常使用的變量之類,以下圖,都有相應註釋
下面咱們來看下生成Button的二維數組,即生成雷區的Button列表。咱們須要在Form中添加GroupBox組件,而後將動態生成的Button列表添加到這個groupbox組件中。生成Button的二維數組方法體以下:
生成完Button列表後,咱們就開始部署地雷了,地雷隨機部署到Button列表中,部署地雷的方法以下:
到了這一步,咱們就應該將雷區的界面渲染出來了,這時候咱們能夠將上面兩個方法放入窗體的Form_Load事件中便可渲染出遊戲界面。以下所示
咱們繼續,下面寫掃雷的算法,當鼠標左鍵點開某個方格的時候,若是該方格不是雷區,那咱們須要計算該方格周邊的地雷數量,計算方法以下:
下面是個遞歸計算的核心算法,很是關鍵。當咱們點開的方格非雷區的時候,周邊連片的非雷區的方格塊會被打開。這一塊的核心算法參考下列代碼,row表示行,col表示列
下面咱們來添加鼠標的點擊事件,我這邊採用的是bt_MouseUp事件來處理。點擊後,咱們首先判斷遊戲是否結束,若是沒結束,則進行下列操做,獲取到被點擊的按鈕的x,y值以及點擊事件按下的鍵值(判斷按下的是鼠標左鍵仍是右鍵)。x,y值獲取到了,咱們就能夠到Button二維數組中找到對應元素。
假如點擊的是鼠標左鍵,則咱們進行掃雷操做,具體的代碼以下:
若是按下的是鼠標右鍵,則是標記方塊是雷區或者待肯定區域,具體代碼以下:
最後再給出一個判斷是否掃雷完畢的方法。
掃雷遊戲的設計大概到此結束,中間還有不少能夠優化的地方,好比將掃雷的邏輯代碼抽離Form類獨立出來等,這些都靠讀者自行去優化了。
附上博主源碼下載的百度雲連接,須要代碼學習的可自行下載研究:https://pan.baidu.com/s/1T4zVndyypzY9i9HsLiVtGg 提取碼: b2im
最後,附上博主的IT技術學習羣,歡迎各位同行入羣指導交流。
使用IIS調試ASP.NET網站程序
在實際的開發當中,相信不少的開發者在開發調試ASP.NET網站時候都是直接經過Visual Studio工具的編譯運行來調試的。
通常狀況下,這種調試方式也不會有多少問題,但有時候咱們會發現這樣的一個狀況,就是明明在本地調試運行都沒有問題的網站,部署到服務器IIS上,就出現沒法描述的線上問題。這時候,就要排除服務器環境是否跟本地環境同樣,其中一點還要涉及到IIS的配置的排除。
那問題來了,咱們能不能在開發的時候,就部署在IIS上調試,而不是直接使用Visual Studio的運行調試,這樣涉及到IIS配置的相關問題就能及早的暴露出來,好比IIS用戶的權限調用等。舉個很典型的例子,如咱們調用微軟的Office Excel組件導出Excel的時候,就須要設置IIS用戶組對Com組件的調用權限。
在開發階段就經過IIS對網站進行調試,能夠避開不少由於IIS設置問題而致使的問題。
下面來講下配置IIS調試的步驟。
第一步 打開你的IIS管理器,沒有安裝IIS服務器的請上網百度如何安裝,此處再也不闡述。在IIS中新建一個網站,給定一個主機域名爲test.com,並將網站目錄指向你web項目的根目錄,以下圖。
第二步 網站配置的應用程序池很關鍵,上圖中的應用程序池的名字叫testWeb,那這個應用程序池的.NetFrameWork的版本號應該跟你項目中使用的版本一致。咱們點擊上圖左側的菜單【應用程序池】,打開應用程序池列表,選中testWeb名稱的應用程序池,確認編輯好該程序池的版本號。以下圖
第三步 確保你的IIS的狀態爲正在運行而非中止的狀態,而且確認你網站的狀態以及網站對應應用程序池的狀態爲正在運行。
第四步 設置本地電腦的Host文件,配置本地域名test.com。讓瀏覽器識別test.com域名的指向是本地計算機。(此步驟很是關鍵,否則你在瀏覽器中輸入test.com會告知你沒法解析)
咱們在電腦的C盤中查找系統的Host文件,Host文件通常在如下路徑中:C:\Windows\System32\drivers\etc。
找到Host文件後,用文本編輯器打開host文件,推薦使用notepad++文本編輯器。打開後,咱們在最後一行加入127.0.0.1 test.com便可,配置完成後保存(Win10系統可能沒法編輯保存,咱們通常採起復制文件出來,編輯完成後覆蓋回原文件),在瀏覽器中輸入test.com便可指向本地IIS中的網站,即你剛纔配置的網站。
第五步 此時配置完畢後,咱們在本身本地電腦的瀏覽器中輸入test.com的時候,就會指向你本地IIS配置的test.com網站中,即你當前開發的網站。
第六步 若是test.com網站測試中發現相應的問題,咱們就能夠經過附加IIS進程的方式對網站進行調試。固然調試的方式與以前不一樣了,此時就不是直接點擊Visual Studio中的調試按鈕或者F5進行調試了。咱們能夠經過下面的方式進入調試模式,選擇菜單欄上的調試,打開子菜單選中附加到進程,以下圖(固然能夠經過快捷鍵Ctrl+Alt+P直接調取出附加界面)
打開後的附加界面以下圖,咱們找到w3wp.exe進程,而後點擊附加按鈕便可。
附加完成後,你就可在代碼中加入相應的斷點調試了,在前臺瀏覽器中輸入test.com訪問出現的錯誤,你可在Visual Studio中找到對應代碼段加入斷點。
經過上述步驟,便可很好的進行網站的調試,IIS相應的配置問題也能及早發現。
WCF入門教程
這一系列文章的內容是從MSDN中COPY過來的,講述的是最簡單的WCF程序示例:如何在控制檯應用程序實現和承載WCF服務,以及如何建立、配置和使用WCF客戶端。
文章主體可分爲兩部分,分別介紹服務器端和客戶端的編程實現。細分的話,能夠分爲六項任務。
- 服務器端
定義WCF服務協定(任務一)
這是建立基本 Windows Communication Foundation (WCF) 服務和可使用該服務的客戶端所需的六項任務中的第一項任務。
建立基本 WCF 服務時,第一項任務是爲與外界共享的服務建立協定,並在其中描述如何與該服務進行通訊。
具體步驟爲:
一、 建立新的控制檯應用程序項目。 在「新建項目」對話框中,選中「Visual Basic」或「Visual C#」,並選擇「控制檯應用程序」模板,並命名爲Service。 使用默認的位置。
二、將默認的Service 命名空間更改成 Microsoft.ServiceModel.Samples。
三、爲項目提供對 System.ServiceModel 命名空間的引用:右擊「解決方案資源管理器」中的「Service」項目,選擇「添加引用」項,在彈出的對話框中的「.NET」選項卡里的「組件名稱」中選擇「System.ServiceModel」,而後單擊「肯定」。
下面是編程步驟:
一、爲 System.ServiceModel 命名空間添加一個 using 語句。
using System.ServiceModel;
二、建立一個新的ICalculator 接口,並將 ServiceContractAttribute 屬性應用於該接口,並將 Namespace 值設置爲「http://Microsoft.ServiceModel.Samples」。 此命名空間指定該服務在計算機上的路徑,並構成該服務的基址部分。 請注意,在經過採用方括號表示法的屬性來批註接口或類時,該屬性類能夠從其名稱中去掉「Attribute」部分。
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
三、在接口中建立方法聲明,並將 OperationContractAttribute 屬性應用於每一個要做爲公共 WCF 協定的一部分公開的方法。
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
下面是建立服務協定的完整代碼段:
using System;
// Add the using statement for the Sytem.ServiceModel namespace
using System.ServiceModel;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
// Create the method declaration for the contract.
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
}
}
........(整個服務器端的代碼未完,等講完任務三時,再給出完整代碼)
實現WCF服務協定(任務二)
一、建立一個新 CalculatorService 類,該類從用戶定義的 ICalculator 接口繼承而來並實現該接口定義的協定功能。
public class CalculatorService : ICalculator
二、實現每一個算術運算符的功能。
public double Add(double n1, double n2)
{
double result = n1 + n2;
Console.WriteLine("Received Add({0},{1})", n1, n2);
// Code added to write output to the console window.
Console.WriteLine("Return: {0}", result);
return result;
}
public double Subtract(double n1, double n2)
{
double result = n1 - n2;
Console.WriteLine("Received Subtract({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Multiply(double n1, double n2)
{
double result = n1 * n2;
Console.WriteLine("Received Multiply({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Divide(double n1, double n2)
{
double result = n1 / n2;
Console.WriteLine("Received Divide({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
在建立和實現了服務協定後,下一步是運行該服務。 運行服務由三個步驟組成:配置、承載和打開服務。
配置、承載和運行服務(任務三)
- 爲服務配置基址
爲服務的基址建立 Uri 實例。 此 URI 指定 HTTP 方案、本地計算機、端口號 8000,以及服務協定中爲服務命名空間指定的服務路徑ServiceModelSample/Services。
Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");
- 承載服務
- 建立一個新的 ServiceHost 實例以承載服務。 必須指定實現服務協定和基址的類型。 對於此示例,咱們將基址指定爲http://localhost:8000/ServiceModelSamples/Services,並將 CalculatorService 指定爲實現服務協定的類型。
ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress); |
- 添加一個捕獲 CommunicationException 的 try-catch 語句,並在接下來的三個步驟中將該代碼添加到 try 塊中。
- 添加公開服務的終結點。 爲此,必須指定終結點公開的協議、綁定和終結點的地址。 此例中,將 ICalculator 指定爲協定,將 WSHttpBinding 指定爲綁定,並將 CalculatorService 指定爲地址。 在這裏請注意,咱們指定的是相對地址。終結點的完整地址是基址和終結點地址的組合。 在此例中,完整地址是 http://localhost:8000/ServiceModelSamples/Services/CalculatorService。
selfHost.AddServiceEndpoint( typeof(ICalculator), new WSHttpBinding(), "CalculatorService"); |
- 啓用元數據交換。 爲此,必須添加服務元數據行爲。 首先建立一個 ServiceMetadataBehavior 實例,將 HttpGetEnabled 屬性設置爲 true,而後爲服務添加新行爲。
ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); smb.HttpGetEnabled = true; selfHost.Description.Behaviors.Add(smb); |
- 打開 ServiceHost 並等待傳入消息。 用戶按 Enter 鍵時,關閉 ServiceHost。
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
下面是服務器端(即「Service」項目中program.cs文件中)的完整程序代碼:
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
}
// Service class that implements the service contract.
// Added code to write output to the console window.
public class CalculatorService : ICalculator
{
public double Add(double n1, double n2)
{
double result = n1 + n2;
Console.WriteLine("Received Add({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Subtract(double n1, double n2)
{
double result = n1 - n2;
Console.WriteLine("Received Subtract({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Multiply(double n1, double n2)
{
double result = n1 * n2;
Console.WriteLine("Received Multiply({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Divide(double n1, double n2)
{
double result = n1 / n2;
Console.WriteLine("Received Divide({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
}
class Program
{
static void Main(string[] args)
{
// Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");
// Create ServiceHost
ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress);
try
{
// Add a service endpoint.
selfHost.AddServiceEndpoint(
typeof(ICalculator),
new WSHttpBinding(),
"CalculatorService");
// Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Start (and then stop) the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
}
若要運行服務,啓動項目文件夾下bin目錄中的 Service.exe便可。
前面三篇文章是講服務器端的部署和運行,下面講講客戶端如何配置和使用。
建立WCF客戶端(任務四)
- 經過執行如下步驟,在 Visual Studio 2005 中爲客戶端建立新項目:
- 在包含該服務(以前文章所述的服務)的同一解決方案中的「解決方案資源管理器」(位於右上角)中,右擊當前解決方案,而後選擇「添加新項目」。
- 在「添加新項目」對話框中,選擇「Visual Basic」或「Visual C#」,選擇「控制檯應用程序」模板,而後將其命名爲 Client。 使用默認的位置。
- 單擊「肯定」。
- 爲項目提供對 System.ServiceModel 命名空間的引用:在「解決方案資源管理器」中右擊「Service」項目,從「.NET」選項卡上的「組件名稱」列中選擇「System.ServiceModel」,而後單擊「肯定」。
- 爲 System.ServiceModel 命名空間添加 using 語句:using System.ServiceModel;
- 啓動在前面的步驟中建立的服務。(即打開在服務器項目中生成的Service.exe可執行文件)
- 經過執行如下步驟,使用適當的開關運行Service Model Metadata Utility Tool (SvcUtil.exe) 以建立客戶端代碼和配置文件:
- 經過選擇「開始」菜單中的「Microsoft Windows SDK」項下的「CMD Shell」,啓動 Windows SDK 控制檯會話。
- 導航到要放置客戶端代碼的目錄。 若是使用默認設置建立 Client 項目,則目錄爲 C:\Documents and Settings\<用戶名>\Documents\Visual Studio 2008\Projects\Service\Client。
- 將命令行工具Service Model Metadata Utility Tool (SvcUtil.exe) 與適當的開關一塊兒使用以建立客戶端代碼。 下面的示例生成服務的代碼文件和配置文件。
svcutil.exe /language:cs /out:generatedProxy.cs /config:app.config http://localhost:8000/ServiceModelSamples/service
- 在 Visual Studio 中將生成的代理添加到 Client 項目中,方法是在「解決方案資源管理器」中右擊「Client」並選擇「添加現有項」。 而後選擇在上一步中生成的 generatedProxy.cs 文件。
配置WCF客戶端(任務五)
在 Visual Studio 中,將在前一過程當中生成的 App.config 配置文件添加到客戶端項目中。 在「解決方案資源管理器」中右擊該客戶端,選擇「添加現有項」,而後從 C:\Documents and Settings\<用戶名>\Documents\Visual Studio 2008\Projects\Service\Client\bin 目錄中選擇 App.config 配置文件。
將app.config添加到項目中後,就算是完成了wcf客戶端的配置。由於具體的配置信息,咱們在使用svcutil.exe工具時,它就幫咱們配置好並寫入了app.config文件。
使用WCF客戶端(任務六)
一、爲要調用的服務的基址建立 EndpointAddress 實例,而後建立 WCF Client 對象。
//Create an endpoint address and an instance of the WCF Client.
EndpointAddress epAddress = new EndpointAddress("http://localhost:8000/ServiceModelSamples/Service/CalculatorService");
CalculatorClient client = new CalculatorClient(new WSHttpBinding(), epAddress);
二、從 Client 內調用客戶端操做。
// Call the service operations.
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
// Call the Subtract service operation.
value1 = 145.00D;
value2 = 76.54D;
result = client.Subtract(value1, value2);
Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);
// Call the Multiply service operation.
value1 = 9.00D;
value2 = 81.25D;
result = client.Multiply(value1, value2);
Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);
// Call the Divide service operation.
value1 = 22.00D;
value2 = 7.00D;
result = client.Divide(value1, value2);
Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
三、在 WCF 客戶端上調用 Close。
// Closing the client gracefully closes the connection and cleans up resources.
client.Close();
下面是客戶端的完整代碼:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
namespace ServiceModelSamples
{
class Client
{
static void Main()
{
//Step 1: Create an endpoint address and an instance of the WCF Client.
EndpointAddress epAddress = new EndpointAddress("http://localhost:8000/ServiceModelSamples/Service/CalculatorService");
CalculatorClient client = new CalculatorClient(new WSHttpBinding(), epAddress);
// Step 2: Call the service operations.
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
// Call the Subtract service operation.
value1 = 145.00D;
value2 = 76.54D;
result = client.Subtract(value1, value2);
Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);
// Call the Multiply service operation.
value1 = 9.00D;
value2 = 81.25D;
result = client.Multiply(value1, value2);
Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);
// Call the Divide service operation.
value1 = 22.00D;
value2 = 7.00D;
result = client.Divide(value1, value2);
Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
//Step 3: Closing the client gracefully closes the connection and cleans up resources.
client.Close();
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
}
若要啓動客戶端,請在「開始」菜單中的「Microsoft Windows SDK」項下選擇「CMD Shell」,從而啓動 Windows SDK 控制檯會話。 定位至 C:\Documents and Settings\<用戶名>\Documents\Visual Studio 2008\Projects\Service\Client\obj\Debug 目錄,鍵入 client,而後按Enter。 操做請求和響應將出如今客戶端控制檯窗口中,以下所示。
Add(100,15.99) = 115.99
Subtract(145,76.54) = 68.46
Multiply(9,81.25) = 731.25
Divide(22,7) = 3.14285714285714
Press <ENTER> to terminate client.
ASP.Net Core開發(踩坑)指南
ASP.NET與ASP.NET Core很相似,但它們之間存在一些細微區別以及ASP.NET Core中新增特性的使用方法,在此以前也寫過一篇簡單的對比文章ASP.NET MVC應用遷移到ASP.NET Core及其異同簡介,但沒有進行深刻的分析和介紹,在真正使用ASP.NET Core進行開發時,若是忽略這些細節可能會出現奇怪的問題,特此將這些細節進行分享。
本文主要內容有:
- 無處不在的依賴注入(Dependency Injection, DI)
- Configuration&Options
- ASP.NET Core 請求管道創建
- ASP.NET Core Mvc
- Web API
- Signalr
- 小結
注:本文基於ASP.Net Core 2.1版本,.Net Core SDK版本須要2.1.401+。長篇預警( ╯□╰ )
無處不在的依賴注入
ASP.NET與ASP.NET Core之間最大區別之一就是內置了依賴注入機制,雖然ASP.NET中也有DI機制,但沒有內置容器,通常都須要使用第三方的容器來提供服務,另外依賴注入的概念也不像ASP.NET Core中這樣無處不在。
簡單來講依賴注入的目的是爲了讓代碼解耦以提升代碼的可維護性,同時也要求代碼設計符合依賴致使原則使得代碼更加靈活,而其原理實際上就是在應用程序中添加一個對象容器,在應用初始化時將實際的服務「放」到容器中,而後當須要相應服務時從容器中獲取,由容器來組裝服務。
服務的註冊
ASP.NET Core的Startup(注:Startup僅僅只是約定名稱,實際使用是在Program類型中建立 WebHost時使用的),該類型中包含兩個方法分別是ConfigureServices和Configure,其中ConfigureServices的主要做用就是用來將服務「放」置到容器中
代碼來自:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1
替換默認的依賴注入容器
ASP.NET Core的默認容器僅提供了構造注入功能,若是須要使用屬性注入等功能或者在遷移時原有應用依賴於其它容器,那麼能夠經過使用第三方容器實現。
將默認容器替換爲其它容器僅需三步:
1. 將ConfigureServices方法的返回類型改成IServiceProvider。
2. 將ASP.NET Core中的服務註冊到第三方容器中。
3. 使用第三方容器實現IServiceProvider接口並返回。
官方文檔以Autofac爲例,Autofac已經實現了ASP.NET Core服務註冊到Autofac容器中,以及Autofac容器的IServiceProvider接口封裝,僅需安裝Autofac以及Autofac.Extensions.DependencyInjection包便可。
詳情參考:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1#default-service-container-replacement
使用windsor或其它容器能夠參考:
https://stackoverflow.com/questions/47672614/how-to-use-windsor-ioc-in-asp-net-core-2
將Controller註冊爲服務
雖然Controller在激活時是經過容器來獲取Controller的依賴(即構造方法須要的參數),在代碼運行的時候給人一種Controller是從容器中組裝的錯覺,可是實際上默認狀況下Controller的組裝過程不是直接由容器組裝的,若是要讓Controller從容器組裝,那麼在配置MVC服務時須要經過.AddControllerAsServices()方法將Controller註冊到容器中:
注:通常狀況下是否將Controller註冊爲服務對Controller的開發和代碼的運行並無很大區別,可是若是當容器變動爲其它容器,而且使用了容器提供的如屬性注入等功能時,若是沒有將Controller註冊爲服務,那麼相應的屬性注入的過程也不會被觸發,簡單來講就是隻有將Controller註冊爲服務,那麼實例化Controller的工做纔會由容器完成,纔會觸發或者使用到容器提供的其它特性。
服務的獲取
前面介紹了服務的註冊,如今來介紹一下在ASP.NET Core中有哪些方法能夠獲取服務:
1. Controller構造方法參數。
2. 經過Controller注入IServiceProvider類型,經過IServiceProvider來獲取服務:
3. 在Action方法或者Mvc過濾器(過濾器的上下文參數中包含HttpContext)中經過HttpContext的RequestServices對象獲取服務:
4. 在View上經過@inject注入服務:
5. 在Action方法中,經過FormServices特性注入:
注:通常來講儘量顯式的標明類型的依賴(即經過構造參數的方式聲明當前類型所依賴的組件),上面的2和3兩點分別都是經過服務提供器在方法內部來獲取依賴,這樣作依賴對於外界來講是不可知的,可能會對代碼的可維護、可測試性等形成必定影響,這種模式被稱爲Service Locator模式,在開發過程當中儘量避免Service Locator模式的使用。
經常使用的服務
ASP.NET Core相對於ASP.NET來講取消了一些經常使用的靜態類型,好比HttpContext、ConfigurationManager等,取而代之的是經過將相似的組件以服務的形式註冊到容器中,使用時經過容易來獲取相應的服務組件,這些經常使用的服務有:
1. IHostingEnvironment:包含了環境名稱、應用名稱以及當前應用程序所處的根目錄及Web靜態內容的根目錄(默認wwwroot)。
2. IHttpContextAccessor:從名字能夠看出,它用來訪問當前請求的HttpContext。
3. IConfiguration:ASP.NET Core配置信息對象。
4. IServiceProvider: ASP.NET Core服務提供器。
5. DbContext: 這裏的DbContext指的是EFCore的DbContext,在ASP.NET Core中,EFCore的DbContext也是在ConfigureServices方法中進行配置並添加到容器,使用時直接從容器中獲取(但要注意的是對於分層結構的開發風格來講,DbContext不會直接被Controller依賴,而是被Controller中依賴的業務服務類型因此來,就是說編寫Controller代碼的時候不會直接與DbContext發生直接交互)。
Configuration&Options
在ASP.NET的開發中,一般某個變量須要從配置文件讀取,通常都是在相應類型的構造方法中,經過靜態類型ConfigurationManager的AppSettings方法來讀取並初始化變量。雖然ASP.NET Core也能夠在類型中注入IConfiguration實例來直接讀取配置文件,但該方法因爲Options模式的出現已經再也不建議使用,使用組件經過依賴相應的組件Options能夠作到關注點分離,提升程序的靈活性、可拓展性,Options使用方法見文檔:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1
ASP.NET Core 請求管道創建
ASP.NET因爲是基於IIS請求管道的,ASP.NET應用程序僅僅是管道中的一個處理環節,管道中還包含如身份驗證、靜態文件處理等環節,但ASP.NET Core不同,它脫離了IIS處理管道,因此整個管道的創建均須要靠程序自身完成,而ASP.NET Core創建管道的代碼就是Startup類型的Configure方法,該方法經過IApplicationBuilder實例來添加不一樣功能的中間件,經過中間件的串聯造成處理管道,下圖是ASP.NET Mvc模板生成的管道代碼:
圖片來自:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-2.1#the-configure-method
該管道主要包含了錯誤處理(開發環境顯示異常信息,其它環境跳轉錯誤頁面)中間件、靜態文件處理中間件以及Mvc中間件。
更多中間件可參考文檔:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index?view=aspnetcore-2.1
ASP.NET Core Mvc
ASP.NET Core Mvc與ASP.NET Mvc相比總體上區別不大,但仍然有不少細節上的變化,下面就開始一一介紹:
路由
路由的做用是將請求根據Url映射到「對應」的處理器上,在Mvc中請求的終點就是Controller的Action方法,而這裏所謂的「對應」指的是Url與路由模板的匹配,ASP.NET Core Mvc經過如下的方式添加路由模板:
上圖中的路由模板是最經常使用的路由模板,使用花括號內的內容爲路由參數及其默認值,Url中經過路由參數控制器名稱、活動方法名稱來匹配到相應控制器的活動方法。
在註冊路由時能夠爲相應路由添加默認值、路由參數約束以及對應路由的相關附加數據(datatokens):
路由的功能除了處理請求匹配外,還具備連接生成的功能,特別是Mvc程序的View中使用IUrlHelper或TagHelper來生成頁面的超連接:
其生成原理是經過連接參數(如上圖所示的Controller和Action)去路由表中匹配,而後使用匹配結果中的第一個路由(可能會匹配到多個路由對象,具體內容在後續Area章節介紹)來生成連接。
更多路由信息及路由模板定義參考文檔:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1
控制器
ASP.NET Core Mvc的Controller通常繼承Controller類型實現,基類Controller中包含了Mvc中經常使用的返回方法(如Json以及View等)以及用於數據存儲的ViewBag、ViewData、TempData。
Area
Area是Mvc應用中用來進行功能拆分或分組的一種方式,Area通常有本身的命名空間和目錄結構,通常Area的默認目錄結構以下:
ASP.NET Core Mvc和ASP.NET Mvc中的概念和用法基本上是一致的,但也存在一些區別:
1. Area下面的Controller須要使用Area特性標明當前Controller屬於哪個Area:
注:Area的目錄結構不是必須的,只須要經過特性標記的Controller都會被正確識別,但目錄結構的改變會致使沒法找到View,關於View的查找路徑會在後續介紹。
2. Area的路由註冊也是在UseMvc方法中完成:
注:攜帶Area的路由模板須要放在前面,不然在生成經過IUrlHelper或TagHelper生成連接時,因爲Controller以及action會匹配到沒有area的模板並使用該模板生成連接,致使area參數被忽略,而生成相似:/controller/action?area=area的結果(在生成Url時,ASP.NET Core會將多餘的路由參數放置到查詢字符串中)
View
View是基於Razor的HTML模板,Razor的詳細語法參考文檔:
https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-2.1
ASP.NET Core Mvc的View與ASP.NET Mvc中的使用方法基本一致,主要區別以下:
1. 引入了TagHelper,使用TagHelper可讓View的代碼更接近Html。更多TagHelper信息參考文檔:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-2.1
2. Controller將參數傳輸到View的方法添加了ViewData特性,使用方法以下:
View中訪問被ViewData標記的方式:
更多詳情參考文檔:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/overview?view=aspnetcore-2.1#passing-data-to-views
3. 新增View組件:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.1
配置View的查找路徑:
ASP.NET Core能夠在ConfigureServices方法中對RazorViewEngineOptions進行配置,以下圖所示,在默認查找位置基礎上添加了View以及AreaView的查找路徑:
模型綁定
模型綁定指的是ASP.NET Core Mvc將請求攜帶的數據綁定到Action參數的過程,ASP.NET Core Mvc的模型綁定數據源默認使用Form Values、Route Values以及Query Strings,全部值都以Name-Value的形式存在,模型綁定時主要經過參數名稱、參數名稱.屬性名稱、參數名稱[索引]等方式與數據源的Name進行匹配。
除了默認的數據源以外還能夠從Http請求Header、Http請求Body甚至從依賴注入容器中獲取數據,要從這些數據源中獲取數據須要在相應參數上使用[FromHeader]、[FromBody]、[FromServices]特性。
若是須要獲取的數據在不一樣數據源中都存在時(Name存在於多個數據源中),還能夠經過特性指明從哪個數據源中獲取,如[FromForm]、[FromQuery]及[FromRoute]。
須要注意的是[FromBody]默認只支持Json格式的內容,若是須要支持其它格式,如XML須要添加相應的格式化器,添加方法以下圖所示:
更多模型綁定及驗證內容請參考文檔:https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1
https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.1
其中模型驗證的使用方式與ASP.NET Mvc一致,仍然是經過相應的驗證特性對模型或模型屬性進行標記。
Action的返回值與Json序列化
說完Action方法參數的綁定,再來看一下Action方法的返回類型,在ASP.NET Mvc中Controller提供了返回頁面內容的View方法以及返回Json內容的Json方法(固然還有文件、重定向、404等等其它內容返回方法,詳見Controller與ControllerBase類型)。
這裏有一個須要注意的地方是當使用Json方法返回一個對象實例時,默認使用首字母小寫的駝峯命名方式序列化實例的屬性名稱,以下圖所示:
訪問結果:
要使用大寫駝峯形式命名須要在配置Mvc服務時添加如下代碼來修改Json默認的序列化配置:
注:一樣的問題也存在於WebAPI的Ok方法以及Signalr的Json格式協議。
靜態資源
因爲ASP.NET Core已經再也不使用IIS請求管道,因此對於靜態資源的訪問來講須要在請求管道中添加相應的處理中間件來完成:
默認的無參UseStaticFiles方法將wwwroot目錄做爲靜態資源存放目錄,若是要添加其它靜態內容目錄能夠再次使用UseStaticFiles方法,並經過StaticFileOptions對目錄的訪問路徑以及實際路徑進行配置:
注:因爲ASP.NET Core能夠在Linux下運行,因此對於Linux來講路徑是大小寫敏感的,另外因爲Windows和Linux類系統的路徑分隔符也不一致,因此爲了保證路徑的統一,可使用Path.Combine方法,該方法會根據操做系統的不一樣對路徑進行不一樣的處理。
另外對於css及js資源文件的打包、壓縮功能,最新版本(ASP.NET Core 2.1)的應用模板以及不會自動添加相關功能,須要在拓展工具中添加Bunlder& Minifier拓展:
而後經過右鍵js等資源文件來建立bundleconfig.json文件:
WebAPI
API控制器的建立
ASP.NET Core將Mvc和WebAPI進行了合併,它們的實現都直接或間接繼承了ControllerBase類型,只不過Mvc的基類Controller在ControllerBase的基礎上添加了一些用於處理View的功能。
用ASP.NET Core開發WebAPI時,Controller類型直接繼承ControllerBase。而後這個API的Controller就具備了基類的特性,返回一個結果僅須要使用Ok方法便可,以下圖所示:
而後在路由表中添加路由:
便可經過/api/default/index訪問到這個API:
但對於REST風格的API來講,它須要經過ApiController特性對Controller類型進行標記,而且經過Route特性來設置路由:
而後就能夠經過HTTP謂詞來訪問API:
但要注意的是在ASP.NET Core中實現的REST風格的Controller,它不會再根據action方法的名稱來匹配謂詞,因此存在多個方法時會,那怕對方法進行了命名,但仍然會出現如下錯誤:
爲了解決這個問題,須要經過添加謂詞特性解決:
模型綁定
WebAPI中的模型綁定與MVC存在一些區別,首先當使用ApiController標記Controller類型時,若是模型綁定驗證未經過,會直接返回400錯誤,不會執行Action方法(免去了使用!ModelState.IsValid進行判斷):
執行結果:
其次使用ApiController標記的Controller在執行模型綁定時會使用默認的推斷規則,該規則分別從Body、Form、Header、Query、Route、Services(它們分別對應FromBody、FromForm、FromHeader、FromQuery、FromRoute、FromServices特性)中推斷獲取數據並綁定,爲何說推斷?
由於有一些特殊的規則:
1. FromBody用於複雜類型推斷,若是不是複雜類型(如int、string等)以及特殊的內置類型(IFormCollection文檔例子),則不會從Body中獲取數據,除非經過[FromBody]特性指明,例子以下:
請求結果:
當使用[FormBody]指明參數數據源後能夠正常訪問:
注:當請求參數爲簡單類型時,請求體內容類型須要爲application/json,內容不能爲Json字符串,使用參數值做爲內容便可(上圖id沒有提供的異常並非由於Json格式問題,而是沒有指明從body中獲取數據致使的)。
2. 只能存在一個參數從Body中獲取數據,若是出現多個參數時,只能保證一個參數從Body中獲取數據,其它參數須要指明獲取數據的位置:
該API的調用方式以下:
3. FromForm默認只推斷文件(IFormFile)及文件集合類型(IFormFileCollection),其他類型默認均不會從Form中獲取。
4. 使用FromForm特性時會推斷multipart/form-data請求內容類型。
以上推斷行爲能夠經過以下配置禁用:
更多信息參考文檔:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-2.1
SignalR
SignalR是用於客戶端服務器實時通訊的工具庫,從ASP.NET中就具備該功能,ASP.NET Core中的SignalR概念與用法與原來基本一致,但也存在一些區別:
1. 支持更多的客戶端,.Net客戶端、Java客戶端、Js客戶端以及非官方的C++客戶端、Swift客戶端。
2. 當連接SignalR並經過身份驗證後,SignalR會保存當前用戶連接SignalR的ID以及經過驗證後的用戶名,能夠經過用戶名向用戶客戶端推送消息。
3. 在應用程序中能夠經過IHubContext<HubType>方式,對SignalR上下文進行注入,而且能夠直接經過該上下文推送數據給已經連接的客戶端,IHubContext<HubType>其實是GlobalHost.ConnectionManager.GetHubContext<HubType>()的替代方式。
4. ASP.NET Core中經過app.UserSignalR以及route參數來映射一個Hub,每個Hub擁有獨立的上下文,所以若是要使用IHubContext<HubType>來向客戶端推送信息,那麼必須準確註明Hub的類型,以下圖代碼應該使用IHubContext<ChatHub>,不能使用除ChatHub之外的類型(基類也不行)。
5. SignalR默認使用Json協議傳輸數據,默認狀況下使用首字母小寫的駝峯命名方式序列化對象,要更改該默認行爲須要經過一下代碼,替換默認的序列化行爲:
6. ASP.NET Core的客戶端代碼(特指Js客戶端)有變動,須要對應版本使用。
關於更多SignalR內容請參考文檔:https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction?view=aspnetcore-2.1
小結
本文主要介紹了ASP.NET Core中Mvc、WebAPI以及SignalR開發時與原來ASP.NET中的一些細小區別和新特性,總體來講ASP.NET Core與ASP.NET從使用方式上基本上是一致的,這也使得從ASP.NET遷移到ASP.NET Core變得更加容易,但可能由於這些細小的問題每每會向代碼中埋入一些坑,因此特別編寫了本文來解釋這些問題。
總的來講ASP.NET Core的文檔至關齊全,本文中大部份內容實際都是文檔中提到的,因此建議你們在使用ASP.NET Core開發時,首先第一步就是熟讀文檔,避免遺漏細節。但願本篇文章對你們有幫助(*^_^*)
參考:
https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-2.1
本文連接:http://www.javashuo.com/article/p-oaqtejjt-v.html
ASP.Net Core Razor+AdminLTE 小試牛刀
AdminLTE
一個基於 bootstrap 的輕量級後臺模板,這個前端界面我的感受很清爽,對於一個大後端的我來講,能夠減小較多的時間去承擔前端的工做但又必須去獨立去完成一個後臺系統開發的任務,而且,文檔還算比較齊全,對着demo能夠完成一個基本的前端框架搭建了。你們若有更爲好看的又方便後端上手的前端框架,也能夠在留言區分享一下唄。
AdminLTE 文檔
在線中文Demo:http://adminlte.la998.com/
在線中文文檔:http://adminlte.la998.com/documentation/index.html
Github:https://github.com/almasaeed2010/AdminLTE/releases
AdminLTE 佈局
AdminLTE依賴於兩個主要框架:JQ和Bootstrap,其餘插件能夠按需增長。
從文檔能夠知道,使用AdminLTE主要有四個部分:
- 包裝
.wrapper
。包裹整個網站的div。 - 主標題
.main-header
。包含徽標和導航欄。 - 邊欄
.sidebar-wrapper
。包含用戶面板和側邊欄菜單。 - 內容
.content-wrapper
。包含頁眉和內容。
在文檔中,能夠找到下載地址,本文示例是使用最新的版本V2.4.5。
Asp.Net Core Razor
新建項目Asp.net Core Web應用程序,默認就是Razor Pages,而後添加相應的模塊,如圖:本文使用的SDK版本爲:dotNet Core 2.1。
First
在Asp.Net Core項目中,引用AdminLTE,在wwwroot僅添加如圖三個文件夾便可:
- bower_components 基本組件。
- dist adminlte的主要文件。
- plugins 其餘插件。
Second
在_Layout.cshtml文件中添加引入相關文件:
<!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.7 --> <link rel="stylesheet" href="~/adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css"> <!-- Font Awesome --> <link rel="stylesheet" href="~/adminlte/bower_components/font-awesome/css/font-awesome.min.css"> <!-- Ionicons --> <link href="~/adminlte/bower_components/Ionicons/css/ionicons.min.css" rel="stylesheet" /> <!-- Theme style --> <link rel="stylesheet" href="~/adminlte/dist/css/AdminLTE.min.css"> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <link rel="stylesheet" href="~/adminlte/dist/css/skins/_all-skins.min.css"> <!-- Pace style --> <link href="~/adminlte/plugins/pace/pace.min.css" rel="stylesheet" /> <link href="~/css/common.css" rel="stylesheet" /> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> <!-- Google Font --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
在body中,添加js:
<!-- jQuery 3 --> <script src="~/adminlte/bower_components/jquery/dist/jquery.min.js"></script> <!-- jQuery UI 1.11.4 --> <script src="~/adminlte/bower_components/jquery-ui/jquery-ui.min.js"></script> <!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip --> <script> $.widget.bridge('uibutton', $.ui.button); </script> <!-- Bootstrap 3.3.7 --> <script src="~/adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <!-- Slimscroll --> <script src="~/adminlte/bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script> <!-- FastClick --> <script src="~/adminlte/bower_components/fastclick/lib/fastclick.js"></script> <!-- AdminLTE App --> <script src="~/adminlte/dist/js/adminlte.min.js"></script> <!-- Skin --> <script type="text/javascript" src="~/adminlte/dist/js/sidebarskins.js" charset="gbk"></script>
sidebarskins.js是本人漢化的側邊欄皮膚
坑1:通常狀況,發現某些功能運行不起來的都是引用不正確致使的,這個要耐心對照好Demo來檢查,或者直接用Demo來修改吧。
Third
開始使用AdminLTE,這裏直接貼圖吧,圖上有註釋和代碼摺疊比較直觀點,重要的地方在放代碼。
最後就能夠運行項目來預覽一下效果了:
都使用到bootstrap,必須得看看移動端的效果,還不錯吧。
坑2:須要注意的是,點擊這個小圖標能夠實現左側邊欄收縮展開的功能,當只有側邊欄能夠正常收縮展開但Logo無動於衷的時候,你多是少了【sidebar-mini】樣式和【logo-mini】logo小圖的引用
添加一個登錄
登陸界面寫得比較簡約,我比較喜歡這種風格。前端寫得很少,因此還得前端的女票指導一二,否則就是後端的設計的界面了,你懂的。
在Pages文件夾中,添加一個Razor界面,並擼好界面代碼:
@page @model AdminLTE.Net.Web.Pages.LoginModel @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>登陸 - AdminLTE.Net.Web</title> <meta name="developer" content="EminemJK"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" rel="stylesheet" /> <link href="~/adminlte/dist/css/AdminLTE.min.css" rel="stylesheet" /> <link href="~/css/login.css" rel="stylesheet" /> </head> <body> <div> <div class="row"> <div class="loginHeader"> <img class="logo-img" src="~/images/banana_logo.ico" /> <h1 class="logo-name">Banana</h1> <div class="clearfix"></div> </div> </div> <div class="row login-bg"> <div class="loginInBox"> @if (!string.IsNullOrEmpty(Model.Message)) { <p class="login-box-msg" style="color:red">@Model.Message</p> } else { <p class="login-box-msg">Sign in to start your session</p> } <form method="post"> <div class="form-group has-feedback"> <input type="text" class="form-control" asp-for="Login.UserName"> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" class="form-control" asp-for="Login.Password"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <button type="submit" class="btn btn-primary btn-block btn-flat login-btn">Sign In</button> </form> </div> </div> </div> <footer class="footer"> <strong>Copyright © 2018 <a href="http://www.cnblogs.com/EminemJK/">EminemJK</a>.</strong> All rights reserved. </footer> </body> </html>
在Startup中引入Authentication身份驗證:
services.AddAuthentication(CookieService.AuthenticationScheme) .AddCookie(CookieService.AuthenticationScheme, o => { o.LoginPath = new PathString("/Login"); });
Configure方法內調用
app.UseAuthentication();
在Login.cshtml.cs中增長一個OnPostAsync的方法:
[HttpPost] public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { Message = ModelState.Root.Errors[0].ErrorMessage; } else { var user = userService.Login(Login.UserName, Login.Password); if (user != null) { VUserModel model = new VUserModel() { Id = user.Id, UserName = user.UserName, Time = DateTime.Now }; var identity = new ClaimsIdentity(CookieService.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Sid, CookieService.GetDesEncrypt(model))); await HttpContext.SignInAsync(CookieService.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties() { //記住我 IsPersistent = true, //過時時間 ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromMinutes(30)) }); return RedirectToPage("./Index"); } Message = "登陸失敗,用戶名密碼不正確。"; } return Page(); }
userService和CookieService都是在業務層定義的,gayhub會在文章末尾。
在.Net Core Razor中,xx.cshtml.cs中默認觸發的是Get和Post方法,
- OnGet
- OnPost
- OnGetAsync
- OnPostAsync
若是是須要自定義的,舉個栗子,定義爲:OnPostLoginAsync,而後在Form表單提交的【按鈕】增長asp-page-handler="Login",詳細的推薦你們閱讀這篇文章:ASP.NET Core - Razor頁面之Handlers處理方法。
接着,而後再Index和須要身份驗證的地方都加上Authorize特性便可:
namespace AdminLTE.Net.Web.Pages { [Authorize(AuthenticationSchemes = CookieService.AuthenticationScheme)] public class IndexModel : BasePageModel { public void OnGet() { } } }
踩坑
1、Ajax Post請求,HttpCore 400
function uploadfile() { var file = $("#input-userimg")[0].files[0]; var data = new FormData(); data.append('file', file); $.ajax({ url: "/Account/UserList?handler=Upload", type: 'POST', data: data, contentType: false, processData: false, success: function (returndata) { $("#user-img").attr('src', returndata.path); }, error: function (a, b, c) { alert('上傳失敗') } }); };
折騰許久,緣由是Razor被設計爲能夠自動防止跨站請求僞造(CSRF / XSRF)攻擊。你沒必要編寫任何其餘代碼。Razor頁面中自動包含防僞令牌生成和驗證。這裏請求失敗,是由於POST沒有提交AntiForgeryToken。
解決方法:
1.增長"XSRF-TOKEN"標識到框架中
//增長了"XSRF-TOKEN"標識,值爲表單自動生成的防僞標記 services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
2.頁面*.cshtml頭部加上
@Html.AntiForgeryToken()
3.ajax引入
function uploadfile() { var file = $("#input-userimg")[0].files[0]; var data = new FormData(); data.append('file', file); $.ajax({ url: "/Account/UserList?handler=Upload", type: 'POST', data: data, contentType: false, processData: false, beforeSend: function (xhr) { xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val()); }, success: function (returndata) { $("#user-img").attr('src', returndata.path); }, error: function (a, b, c) { alert('上傳失敗') } }); };
而後既能夠正常訪問Handler
2、DataTables參數實例加說明
var table = $('#userListTable').DataTable({ "processing": true, "serverSide": true, "ajax": function (data, callback, settings) { //data的參數請參考: https://segmentfault.com/a/1190000004478726 var param = {}; param.draw = data.draw; param.pageNum = (data.start / data.length) + 1; param.pageSize = data.length; param.sex = $('#select-sex option:selected').val(); param.phone = $('#input-phone').val(); param.name = $('#input-name').val(); $.ajax({ type: "GET", data: param, url: "/Account/UserList?handler=UserPage", dataType: "json", success: function (data) { //成功後回調自動渲染 callback(data); } }); }, 'columns': [ { 'data': 'id' }, { 'data': 'name' }, { 'data': 'userName' }, { 'data': 'sexString' }, { 'data': 'phone' }, { 'data': 'createTime' }, { 'data': 'enableString', 'render': function (data, type, row) { if (row.enable == 1) return '<span style="color:#19be6b" >' + row.enableString + '</span>'; else return '<span style="color:#ed3f14" >' + row.enableString + '</span>'; } }, { 'data': null, 'render': function (data, type, row) { return '<a id="btn-edit" class="btn btn-success btn-xs" title="編輯" onClick=btn_edit(' + row.id + ')><i class="fa fa-edit"></i>編輯</a> ' + '<a id="btn-edit" class="btn btn-danger btn-xs" title="刪除" onClick=btn_edit(' + row.id + ')><i class="fa fa-trash " title="刪除" style="cursor:pointer"></i>刪除</a>'; } }, ], //datatable設置參數 http://www.datatables.club/reference/option/ 'paging': true, //啓用分頁 'lengthChange': true, //設置每頁數量 'searching': false, 'ordering': false, 'info': true, 'autoWidth': false, //設置中文 'language': { "sProcessing": "玩命加載中...", "sLengthMenu": "每頁顯示顯示 _MENU_", "sZeroRecords": "沒有匹配結果", "sInfo": "顯示第 _START_ 至 _END_ 項結果,共 _TOTAL_ 項", "sInfoEmpty": "顯示第 0 至 0 項結果,共 0 項", "sInfoFiltered": "(由 _MAX_ 項結果過濾)", "sInfoPostFix": "", "sSearch": "搜索:", "sUrl": "", "sEmptyTable": "表中數據爲空", "sLoadingRecords": "玩命加載中...", "sInfoThousands": ",", "oPaginate": { "sFirst": "首頁", "sPrevious": "上頁", "sNext": "下頁", "sLast": "末頁" }, "oAria": { "sSortAscending": ": 以升序排列此列", "sSortDescending": ": 以降序排列此列" } } });
Last
附上這些天來的成果,發現,我並不適合寫前端,太醜了,哈哈。
最後,Show me the code。
Github:https://github.com/EminemJK/AdminLTE.Net.Web
Banana
Banana Github:https://github.com/EminemJK/Banana
Demo中會使用到這兩個我的封裝的組件:
Banana.Uow是基於Dapper封裝的工做單元和倉儲;
Banana.Utility是經常使用的工具類,有Redis,加解密,拼音等等;
歡迎你們在Issues中提出意見,你們共同進步。
webservice建立、部署和調用
webservice 能夠用於分佈式應用程序之間的交互,和不一樣程序之間的交互。
下面經過一個簡單的例子來建立一個webservice,用的是vs2010開發工具
首先建立一個web應用程序
接着咱們添加一個web服務
一開始WebService1.asmx這個文件中有一個HelloWord方法
這樣咱們建立webserver服務就算完成了,接着咱們建立一個加減乘除的方法
這樣,咱們的webservice的方法就算編寫完成了,接着咱們將其發佈到咱們iis服務器上
接着咱們建立一個webform程序來調用咱們的webservice
首先咱們須要添加有個服務引用
接着咱們就能夠編寫代碼測試咱們webserver
這樣咱們webservice建立、部署和調用就算完成了。
.net接收post請求並把數據轉爲字典格式
public SortedDictionary<string, string> GetRequestPost()
{
int i = 0;
SortedDictionary<string, string> sArray = new SortedDictionary<string, string>();
NameValueCollection coll = Request.Form;
String[] requestItem = coll.AllKeys;
for (i = 0; i < requestItem.Length; i++)
{
sArray.Add(requestItem[i], Request.Form[requestItem[i]]);
}
coll.Clear();
return sArray;
}
調用:
SortedDictionary<string, string> sPara = GetRequestPost();
.net接收post請求,並轉爲字符串
Stream s = Request.InputStream;int count = 0;byte[] buffer = new byte[1024];StringBuilder reqXml = new StringBuilder();while ((count = s.Read(buffer, 0, 1024)) > 0){reqXml.Append(Encoding.UTF8.GetString(buffer, 0, count));}s.Flush();s.Close();s.Dispose();