目錄
Post到WeatherForecastController
更改WeatherForecastController Post方法
Console.WriteLine的Post()方法無法運行
您是否曾經想過生成一種快速的Web API來運行某些服務器端功能?閱讀本文,您將擁有一個可用的Web API,並瞭解如何使用原始JavaScript通過XMLHttpRequest(AJAX)將JSON發佈到它。
背景
最近,在放鬆使用.NET Core Web API的同時,我發現我不確定通過XMLHttpRequest(XHR)對象將數據發佈到Web API的正確方法。
我不確定:
- 如何將數據添加到XHR對象以使其顯示在帖子主體中。
- 如何確保Web API將發佈的JSON自動轉換爲我的目標域對象。
我遇到了各種各樣的錯誤,我無法分辨是客戶端錯誤還是Web API錯誤,或者兩者都不是。我發現兩者都是錯誤的,但是即使您認爲這是常見的配對(將純JavaScript XHR發佈到Web API),也很難找到一個明確的答案。
本文涵蓋的內容
- 創建一個.NET Core Web API項目(通過命令行)——我使用在Ubuntu Linux上運行的Visual Studio Code
- 添加可以發佈的Web API方法
- 更改Web API應用程序,以便它將提供index.htm文件(用於測試我們的Web API)
- 設置一個簡單的index.htm和main.js文件,我們用它來發布到Web API
- 配置XMLHttpRequest(XHR)以將JSON發佈到Web API
介紹
我假設您已經在計算機上安裝了Visual Studio或Visual Studio Code的最新副本。
當然,您還需要確保也安裝了最新版本的.NET Core。如果需要,可以訪問https://dotnet.microsoft.com/download^。
我在Ubuntu Linux上運行Visual Studio Code,因此我可以從命令行執行所有操作,但是如果您在Windows上運行並使用Visual Studio,則可以使用嚮導創建.NET Core Web API。.NET Core的命令行實際上並不難,您可能會發現它非常令人滿意。
創建新項目
首先,請執行以下步驟:
- 打開一個控制檯(或終端)。
- 導航到要在其中創建新項目的目錄。
- 輸入以下命令: dotnet new webapi --name MainWebAPI
將創建一個新文件夾
當您鍵入該命令並按<ENTER>時,它將創建一個名爲MainWebAPI的新子文件夾,並將所有項目文件添加到該目錄中。
如果成功,您將看到以下內容:
如果您收到某種無法識別該命令的錯誤,則可能您沒有安裝.NET Core SDK。要確定已安裝的.NET版本,可以嘗試以下命令:
dotnet --version
我的回答是3.1.202。
如果一切順利,那麼您就可以在.NET Core上運行一個非常基本的Web API。
運行Web API項目
讓我們確保項目可以編譯並運行。很簡單
首先,只需將目錄更改爲新創建的項目目錄: cd MainWebAPI
接下來,鍵入: dotnet run
.NET將構建並啓動Web API。
它看起來像以下內容:
只是生成項目
順便說一句,如果您只是想生成項目,則可以輸入: dotnet build
實際使用中的Web API
要查看Web API的功能,可以按住CTRL鍵並單擊控制檯窗口中顯示的鏈接(https:// localhost:5001)。完成後,默認瀏覽器將打開該鏈接。
但這並不十分令人驚奇,因爲您尚未調用任何Web API方法。
您只會看到一個空白網頁。
一個通過Get的Web API方法
但是,您可以通過GET調用一個方法。嘗試以下URL:https://localhost:5001/WeatherForecast/^。
這將在WeatherForecastController*類上調用默認的Get方法。該方法只是生成一些隨機的WeatherForecast *領域對象,然後將它們作爲JSON發送給客戶端。
*當我們看一下代碼時,我將向您展示這兩個類。
您將在瀏覽器中看到類似以下兩個圖像之一的圖像(取決於瀏覽器呈現JSON的方式)。
現在您已經看到了代碼的運行,讓我們看一下代碼並開始進行修改WeatherForecastController,以便我們可以向其發送JSON,並使它自動將JSON轉換爲領域對象。
Visual Studio Code
在本文中,我將一直使用Visual Studio Code(VSC),但如果願意,您仍然可以使用Visual Studio。
繼續並啓動Visual Studio Code(VSC),然後打開項目。
開啓文件夾
要在VSC中加載項目,請執行以下操作:
- 進入主菜單
- 選擇[文件...]菜單項
- 選擇[打開文件夾...]
- 導航到創建項目時創建的文件夾,然後選擇它
當您按照這些步驟,您將看到類似以下內容:
重點地區
我在VSC中強調了一些領域,因此我們可以討論它們。
工具欄
第一部分是工具欄,其中包含一些圖標。
文件視圖
當前選定的圖標以白色突出顯示,並指示已顯示“文件視圖”。您可以看到在第二部分中有一個文件列表。但是,如果您選擇其他圖標之一,則此部分將更改。
調試視圖
在本文中,我們還將使用“調試”項向您展示如何使用VSC逐步遍歷Web API代碼。調試項由指示。
但是,要在VSC中使用調試器,必須確保已安裝並正在運行C#Extensions插件。
插件視圖
插件視圖由指示。
您可以看到我在上面提供的VSC概述圖像的第4部分實際上是VSC,警告我需要將C#Extension插件添加到項目中,以便我們可以調試代碼。如果看到該內容,然後單擊[是]按鈕,它將把它添加到項目中。但是,如果未將擴展名添加到系統中,則可能看不到。
同樣,該彈出窗口往往會迅速消失。沒關係,因爲您可以單擊“插件”圖標,然後隨時將插件添加到系統和項目中。
安裝和激活後的外觀如下:
主編輯區
最後,您可以看到主編輯區域(上面的VSC圖像上的區域3)當前正在顯示有關VSC當前版本的註釋。從“文件查看器”區域中選擇每個文件時,這裏是將顯示每個文件的區域。
現在您對VSC更加熟悉了,讓我們開始看一下WeatherForecastController。
檢查WeatherForecastController
在子目錄中打開文件
確保您在“文件視圖”上。接下來,在文件視圖中,找到Controllers目錄。它將有一個向下的右箭頭(大於號)指向Controllers名稱。這表示當前已摺疊的文件夾。單擊Controllers文件夾,然後選擇出現的WeatherForecaseController.cs,它將顯示在編輯器區域中。
控制器:應用邏輯層
我不會解釋Controller代碼的每個方面。您通常可以將控制器視爲應用程序邏輯層。這是發生的事情的基礎。
用戶通過將HTTP命令(GET,POST,PUT等^)發送到服務器上的命名控制器(由URL描述)來請求控制器上的操作(功能或方法)。
注意:在本文中,我們將僅處理POST命令。
作爲開發人員,我們可以在Controller中使用運行提供特定功能的應用程序邏輯的方法編寫代碼。真的就是這麼簡單。
項目模板包含在Controller的Get方法如下所示:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
Console.WriteLine("in get...");
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
這可能很明顯,但是[HttpGet]方法裝飾器告訴編譯器通過HTTP Get使此方法可用。
此方法僅生成五個WeatherForecast對象(領域對象)的數組並返回它們。它們將被自動序列化爲JSON,這就是我們先前在瀏覽器中看到的。
我們的主要挑戰
這裏的挑戰是如何將數據發佈到WeatherForecast Controller,以便將其自動反序列化爲WeatherForecast對象。
首先,讓我們向控制器添加最基本的Post方法。
[HttpPost]
public String Post(){
Console.WriteLine("in post...");
return "It worked!";
}
首先,請注意,我們將裝飾器更改爲 [HttpPost] ,以便在用戶發佈到URL http://localhost:5001/WeatherForecast時觸發此方法。
默認Post方法
此內部方法名稱是Post(),但在外部沒有顯示任何操作(方法名稱),因此,這意味着當用戶直接發佈到Controller時,這是將發生的默認操作(將運行的方法)。
有一種方法可以提供一個外部名稱,然後可以在WeatherForecast控制器URL 上提供該名稱,稍後我將向您展示。
次要挑戰:客戶端Post創建
現在,我們面臨着在WeatherForecast控制器上測試Post()方法的挑戰。由於我們不再只能使用瀏覽器導航到URL(簡單的HTTP Get),因此我們需要其他方法從客戶端調用HTTP Post。
第一次最簡單的Post測試
Post對控制器執行操作的第一種,可能是最簡單的方法是下載PostMan並設置它。還有其他工具,但PostMan可能是最簡單的工具,並且是免費的。
轉到https://www.postman.com/downloads/^,獲得免費的實用程序併爲您的系統安裝它。
完成並啓動後,我們可以嘗試將其發佈到我們的控制器。
Walkthru:PostMan請求
PostMan用戶界面有點混亂,但是創建一個新請求很容易,我將逐步引導您。
創建一個新請求
您要做的第一件事是創建一個新請求。爲此,您可以選擇[新建(New)]按鈕,然後在菜單出現時選擇[請求(Request)]。或者,您可以單擊[創建請求(Create a Request)]選項。在下一個圖像中,這兩個都以紅色突出顯示。
選擇這些選項之一後,您將看到一個新表格。在圖像中,您可以看到我還刪除了HTTP操作列表,以向您顯示可以在其中選擇提交請求時使用的HTTP操作。首先,我們將嘗試簡單的Get。
PostMan:配置更改
但是,在我們發出第一個請求之前,您需要對PostMan默認設置進行一次更改。如果不這樣做,則所有請求都將失敗,因爲PostMan不支持自簽名證書。
如果不更改設置,則將看到類似以下的錯誤(請注意,藍色突出顯示的錯誤文本)。
HTTPS:不支持自簽名證書
要解決此問題,只需在PostMan中轉到“文件 ... 設置 ...”,然後關閉以紅色突出顯示的選項(此處顯示爲打開狀態)-SSL證書驗證。
完成此操作並關閉“設置”窗口後,即可繼續進行,它將起作用。
繼續並從下拉列表中選擇Get命令。
接下來,添加以下URL,然後按<ENTER>或單擊[發送]按鈕。
完成此操作後,您將看到與以前(在瀏覽器中)看到的結果類似的結果,但只是以PostMan格式化內容的方式進行了格式化。
請記住,這是在WeatherForecast控制器上調用默認Get()操作的結果。我提到這一點是爲了提醒您,我們沒有在URL上添加任何操作名稱(特定)方法。
Post到WeatherForecastController
現在,讓我們簡單地在PostMan中將HTTP Verb更改爲Post,然後重試。
這是新的非常基本的結果:
一切都很好,但是我們要做的是將一些JSON發佈到控制器,並使其自動將JSON轉換爲我們的領域對象(WeatherForecast)。
發佈JSON:嘗試1
我們需要了解的第一件事是目標領域對象的外觀。它有什麼特性?
我們可以回顧一下VSC 中的WeatherForecast類。
WeatherForecast類分析
這是一個非常簡單的類。這是該類的完整代碼清單。
using System;
namespace MainWebAPI
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}
它實際上僅由四個屬性組成(儘管其中一個確實運行函數來獲取其值)。
- Date
- TemperatureC //(攝氏溫度)
- TemperatureF //(以華氏溫度計算)
- Summary //基於文本的天氣摘要
JSON:名稱/值對
由於我們知道JSON(JavaScript對象表示法)只是用於定義對象的名稱值對,因此我們可以非常輕鬆地創建自己的WeatherForecast(帶有數據)。這是我們將用於測試的一個。
{"Date":"2015-05-03", "TemperatureC":37,"Summary":"Our weather data"}
請注意,由於TemperatureF是從TemperatureC中計算得出的,因此我甚至沒有在JSON中包含該屬性。
方括號:Think Object——快速JSON摘要
這是考慮您在JSON中看到的內容的快速方法。大括號表示對象的開始和結束。冒號前面的每個值表示屬性名稱,冒號後面的每個值表示屬性值。每個名稱/值對用逗號分隔。每個屬性名稱必須與目標類中的一個屬性匹配。
就是這樣,所以讓我們使用PostMan嘗試一下。
更改WeatherForecastController Post方法
我們必須在WeatherForecastController中的默認Post()方法中進行一項更改。我們必須確保它期望接收一個WeatherForecast對象。
讓我們在VSC中打開WeatherForecastController並進行更改。
我們還將更改方法以返回Int32,然後返回TemperatureF,以便您可以看到生成的值。這也爲我們提供了某種可用的Web API方法(儘管有些許作弊),因爲您可以發送一個知道攝氏溫度的WeatherForecast對象,並且該方法將以華氏溫度返回溫度。
這是更改後的默認Post()方法。
[HttpPost]
public Int32 Post(WeatherForecast wf){
Console.WriteLine("in post...");
return wf.TemperatureF;
}
進行更改並運行
進行了這些更改之後,請返回控制檯並執行以下操作:
- 如果該程序已經在運行,則將其關閉(按CTRL-C結束會話)
- 再次構建並運行該應用程序——dotnet run將再次構建並運行該應用程序
- 複製我們在上面創建的JSON
- 回到PostMain
- 單擊[body]選項卡(見下圖)
- 選擇[raw](下一個圖像)單選按鈕
- 將JSON粘貼到編輯器區域。
- 點擊[發送]按鈕。
完成所有這些步驟後,您將看到以下結果(錯誤)。
注意:我使用一張圖像來顯示多個步驟和結果。
錯誤說明
這有點令人困惑,但是最下面的部分是將我們的JSON發佈到Controller之後的結果。我們沒有得到預期的響應(TemperatureF),但是得到了描述問題的JSON。很難說一個錯誤甚至發生在哪裏。
Console.WriteLine的Post()方法無法運行
請記住,我們的默認WeatherForecastController有一個Console.WriteLine()調用來嘗試輸出消息。這應該在運行應用程序的控制檯窗口中輸出一些文本,但是如果您查看的話,將沒有輸出。這爲我們提供了一個線索,即我們的方法從未運行過。
不支持的媒體類型
發生錯誤的真正線索是錯誤415和消息“不支持的媒體類型”。但是,它仍然是一個神祕的事物,如果您剛剛開始,那麼您真的會想知道這意味着什麼。經過大量搜索和閱讀之後,您會發現這意味着服務器不知道我們要向其發佈什麼類型的數據,並且也不喜歡這樣。
使用HTTP標頭解決問題
通過在Post操作中添加HTTP標頭,我們可以告訴服務器正在發送的數據類型。
服務器想知道您要發送的數據類型,以便可以正確使用數據。讓我們將適當的HTTP標頭添加到PostMan Post操作中,然後重試。
HTTP標頭:名稱/值對
HTTP標頭是與數據一起發送的元數據(與實際數據一起發送的數據,用於描述數據)。要將名稱/值對添加到PostMan標頭中,我們只需要:
- 選擇“標題”標籤(請參見下圖)
- 添加一個名爲:Content-Type的新標題(在Key(與Name相同)下的列表中鍵入)
- 爲添加的鍵添加一個新值: application/json
該Key和value都是服務器將理解的HTTP規範描述的預定義值。
現在,當您再次發佈時,服務器將理解傳入的內容是JSON,應將其作爲JSON處理。
完成這些更改後,繼續並再次單擊PostMan中的[發送]按鈕,這一次,您將獲得有效的結果。98華氏溫度與37攝氏度(我們在JSON中發送WeatherForecastController的值)相同。
您還可以說WeatherForecastController Post()方法確實運行了,因爲回到控制檯窗口,對Console.WriteLine()的調用有一些輸出:
自動轉換爲領域對象:太神奇了!
另外,請考慮一下底層的.NET Core Web API將我們的JSON自動轉換爲我們的域對象(WeatherForecast)的驚人程度。
您可以說它是自動完成的,因爲我們的默認Post()方法只接受類型WeatherForecast參數,並且我們在return語句中引用了對象的屬性:
return wf.TemperatureF;
總結第1部分
我希望您能從本系列文章的第1部分中獲得指導,並以一種清晰的方式揭示了一些棘手的元素(HTTP標頭,HTTP Post和使用PostMan)。
但是,由於本文已經很長了,因此我決定將本文的其餘部分放在第2部分中。
現在我們知道我們需要...
- JSON數據已添加到Post正文
- 特殊Content-Type Header添加到Post操作中
...這將使我們更容易理解如何通過JavaScript 使用AJAX(XMLHttpRequest對象)將數據發佈到我們的Web API控制器。
使用代碼
- 下載壓縮文件。
- 解壓縮到Dev目錄(有一個名爲MainWebAPI的外部文件夾,其中包含所有文件和子文件夾)。
- 打開控制檯並將目錄更改爲\MainWebAPI。
- 運行命令/> dotnet run。
- 該應用程序將構建並啓動Web服務器。