本文力求用最簡的描述,演示C#開發和調用webapi。
所用的例子在.net5以及vs2019 v16.9.3中調試通過。
mvc框架實現的webapi和webapi框架的對比:
學過.net MVC的同學都知道,MVC中,c是可以返回字符串(多數是json字符串)的。因此,在不計體量的情況下,完全可以用mvc來開發webapi。
webapi相對於mvc,最大的好處就是輕量。因為它不用考慮視圖等等內容。當然,配置也略微麻煩一點。
webapi實現步驟:
1、新建項目。如下圖:

之后的項目配置界面,選擇最簡內容:

如需勾選什么,自己負責研究清楚。
2、創建項目之后,添加自己的控制器“HomeController”(原有的天氣系統可以刪除)

此處添加兩個方法:index和index1
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1.Controllers
{
[ApiController]
[Route("[controller]/[action]")]
public class HomeController :ControllerBase
{
public string Index()
{
return "Hello Katty.";
}
[HttpGet("{x}")]
public string Index1(string x)
{
return x+ ",Hello Katty.";
}
}
}
要點:
(1)、“[ApiController]”必須有,否則方法不會被識別為webapi方法。加了它以后,“[Route("[controller]/[action]")]”也就必須有了。它表示使用什么樣的格式訪問對應方法。
想要在多個控制器上使用ApiController屬性,微軟的建議是:
[ApiController] public class MyControllerBase : ControllerBase { }
然后
[Produces(MediaTypeNames.Application.Json)] [Route("[controller]")] public class PetsController : MyControllerBase
(見:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-5.0)
(2)、參數可以用示例代碼的方式添加。
(3)、控制器繼承自ControllerBase而不是Controller。前者是后者的父類,更輕便,沒有處理視圖的代碼。
(4)、要返回json,也可以返回類型是 ActionResult<T> 類型。 ASP.NET Core 自動將對象序列化為 JSON,並將 JSON 寫入響應消息的正文中。 此返回類型的響應代碼為 200 OK(假設沒有未處理的異常)。
(見:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-5.0)這一點和mvc相同。
3、配置啟動文檔" Properties\launchSettings.json" 。(可選)
1 { 2 "$schema": "http://json.schemastore.org/launchsettings.json", 3 "iisSettings": { 4 "windowsAuthentication": false, 5 "anonymousAuthentication": true, 6 "iisExpress": { 7 "applicationUrl": "http://localhost:39574", 8 "sslPort": 0 9 } 10 }, 11 "profiles": { 12 "IIS Express": { 13 "commandName": "IISExpress", 14 "launchBrowser": true, 15 "launchUrl": "api/home/index", 16 "environmentVariables": { 17 "ASPNETCORE_ENVIRONMENT": "Development" 18 } 19 } 20 } 21 }
可以在原文件的基礎上,修改第15行。
至此,webapi完成。
1、httpwebrequest方式調用webapi:
簡單起見,采用控制台方式調用。
****
考慮到日后使用方便,參考了CSDN博主「大海中一粒沙子」的原創文章(原文鏈接:https://blog.csdn.net/u013730110/article/details/98941934)
新建了restClient類,代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Net; using System.Web; namespace ConsoleApp1 { public class RestClient { /// <summary> /// 請求服務器地址 /// </summary> private string BaseUri; public RestClient(string baseUri) { this.BaseUri = baseUri; } #region Get請求 public string Get(string uri) { //先根據用戶請求的uri構造請求地址 string serviceUrl = string.Format("{0}/{1}", this.BaseUri, uri); //創建Web訪問對 象 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl); //通過Web訪問對象獲取響應內容 HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse(); //通過響應內容流創建StreamReader對象,因為StreamReader更高級更快 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8); //string returnXml = HttpUtility.UrlDecode(reader.ReadToEnd());//解決編碼問題 string returnXml = reader.ReadToEnd();//利用StreamReader就可以從響應內容從頭讀到尾 reader.Close(); myResponse.Close(); return returnXml; } #endregion #region Post請求 public string Post(string data, string uri) { //先根據用戶請求的uri構造請求地址 string serviceUrl = string.Format("{0}/{1}", this.BaseUri, uri); //創建Web訪問對象 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl); //數據轉成“UTF-8”的字節流 byte[] buf = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(data); myRequest.Method = "POST"; myRequest.ContentLength = buf.Length; myRequest.ContentType = "application/json"; myRequest.MaximumAutomaticRedirections = 1; myRequest.AllowAutoRedirect = true; //發送請求 Stream stream = myRequest.GetRequestStream(); stream.Write(buf, 0, buf.Length); stream.Close(); //獲取接口返回值 //通過Web訪問對象獲取響應內容 HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse(); //通過響應內容流創建StreamReader對象,因為StreamReader更高級更快 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8); //string returnXml = HttpUtility.UrlDecode(reader.ReadToEnd());//解決編碼問題 string returnXml = reader.ReadToEnd();//利用StreamReader就可以從響應內容從頭讀到尾 reader.Close(); myResponse.Close(); return returnXml; } #endregion #region Put請求 public string Put(string data, string uri) { //先根據用戶請求的uri構造請求地址 string serviceUrl = string.Format("{0}/{1}", this.BaseUri, uri); //創建Web訪問對象 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl); //把用戶傳過來的數據轉成“UTF-8”的字節流 byte[] buf = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(data); myRequest.Method = "PUT"; myRequest.ContentLength = buf.Length; myRequest.ContentType = "application/json"; myRequest.MaximumAutomaticRedirections = 1; myRequest.AllowAutoRedirect = true; //發送請求 Stream stream = myRequest.GetRequestStream(); stream.Write(buf, 0, buf.Length); stream.Close(); //獲取接口返回值 //通過Web訪問對象獲取響應內容 HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse(); //通過響應內容流創建StreamReader對象,因為StreamReader更高級更快 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8); //string returnXml = HttpUtility.UrlDecode(reader.ReadToEnd());//解決編碼問題 string returnXml = reader.ReadToEnd();//利用StreamReader就可以從響應內容從頭讀到尾 reader.Close(); myResponse.Close(); return returnXml; } #endregion #region Delete請求 public string Delete(string data, string uri) { //先根據用戶請求的uri構造請求地址 string serviceUrl = string.Format("{0}/{1}", this.BaseUri, uri); //創建Web訪問對象 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl); //把用戶傳過來的數據轉成“UTF-8”的字節流 byte[] buf = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(data); myRequest.Method = "DELETE"; myRequest.ContentLength = buf.Length; myRequest.ContentType = "application/json"; myRequest.MaximumAutomaticRedirections = 1; myRequest.AllowAutoRedirect = true; //發送請求 Stream stream = myRequest.GetRequestStream(); stream.Write(buf, 0, buf.Length); stream.Close(); //獲取接口返回值 //通過Web訪問對象獲取響應內容 HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse(); //通過響應內容流創建StreamReader對象,因為StreamReader更高級更快 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8); //string returnXml = HttpUtility.UrlDecode(reader.ReadToEnd());//解決編碼問題 string returnXml = reader.ReadToEnd();//利用StreamReader就可以從響應內容從頭讀到尾 reader.Close(); myResponse.Close(); return returnXml; } #endregion } }
實質:利用HttpWebRequest、HttpWebResponse發送和返回內容,由流來讀寫,自行研究,不再贅述。
****
代碼為:
string s; RestClient restClient = new("http://localhost:28916"); s=restClient.Get("home/index"); Console.WriteLine(s); s = restClient.Get("home/index1/zs"); Console.WriteLine(s);
運行效果:
webapi:

控制台:

對於post等方式,由於封裝類是用json發送數據的,所以思路:傳遞json過去,webapi反序列化為類。方便起見,可以匿名。(httpclient應該一樣,未驗證)
webapi:
public int post_Sum(dynamic x) { dynamic y = JsonConvert.DeserializeObject<dynamic>(x.ToString()); return y.x * 2; }
其中兩個dynamic換成object一樣可以工作,感覺效率還高些。
調用端控制器:
RestClient rc = new RestClient("http://localhost:2674"); ViewData["data"]= rc.Post(JsonConvert.SerializeObject(new { x=15}), "api/values/post_Sum"); return View();
結果:頁面顯示30。
2、補充調用方式:httpclient
微軟推薦用httpclient替代httpwebrequest。網上有人做過測試,后者的效率比前者高。所以,請讀者根據實際情況進行取舍。
以下講述get調用、使用json的post調用、鍵/值對post調用。
webapi主要代碼:
public string Index(int x,int y) { return (x+y).ToString(); } [HttpPost] public string Index1(m a) { return (a.x+a.y).ToString(); }
很簡單,m是一個簡單類:
public class m { public int x { get; set; } public int y { get; set; } }
調用端為普通mvc,主要代碼如下:
1 using Microsoft.AspNetCore.Mvc; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using Newtonsoft.Json; 6 using System.Net.Http; 7 8 namespace WebApplication2.Controllers 9 { 10 public class HomeController : Controller 11 { 12 public IActionResult Index() 13 { 14 ViewData["data"]=get("http://localhost:39574/api/home/index?x=1&y=2"); 15 ViewData["data1"] = post(new { x = 10, y = 15 }); 16 return View(); 17 } 18 public string get(string s) 19 { 20 var hc = new HttpClient(); 21 HttpResponseMessage response = hc.GetAsync(s).Result; 22 response.EnsureSuccessStatusCode();//拋異常 23 string responseBody = response.Content.ReadAsStringAsync().Result; 24 return responseBody; 25 } 26 public string post(object x) 27 { 28 var hc = new HttpClient(); 29 string t = JsonConvert.SerializeObject(x); 30 HttpContent content = new StringContent(t); 31 content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); 32 HttpResponseMessage response = hc.PostAsync("http://localhost:39574/api/home/index1", content).Result; 33 response.EnsureSuccessStatusCode();//拋異常 34 string responseBody = response.Content.ReadAsStringAsync().Result; 35 return responseBody; 36 } 37 } 38 }
頁面上主要顯示兩個viewdata,很簡單。
運行效果:

使用鍵/值對訪問post方法
webapi:
[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { [Route("[action]")] [HttpPost] public int add([FromForm] int x = 1, [FromForm] int y = 2) { return x+y; } }
注意:由於是仿form提交方式post數據過去,所以形參前面必須有“[FromForm]”。否則拿不到數據。
調用端-類:
public class MyHttpClient { static HttpClient _httpClient; public static void init() { _httpClient ??= new HttpClient(); } public static string post(string url) { //application/x-www-form-urlencoded編碼傳送 //Dictionary<string, string> data = new(); //data.Add("x", "200"); //data.Add("y", "300"); //var formdata = new FormUrlEncodedContent(data); //form-data編碼傳送 var formdata = new MultipartFormDataContent(); formdata.Add(new StringContent("500"), "y"); formdata.Add(new StringContent("400"), "x"); HttpResponseMessage response = _httpClient.PostAsync(url,formdata).Result; response.EnsureSuccessStatusCode();//拋異常 string responseBody = response.Content.ReadAsStringAsync().Result; return responseBody; } }
調用端-mvc:
public IActionResult Index() { //ViewData["data"] = ":)"; MyHttpClient.init(); ViewData["data"] = MyHttpClient.post("http://localhost:58397/api/values/add"); return View(); }
結果正確。
mvc控制器里還用到了異步知識(沒辦法,httpclient的方法都是異步方法),大家自行學習或忽略。
關於跨域:
無論是webapi還是mvc仿webapi,默認都不許跨網站get訪問(即跨域)(實測使用post方式調用webapi,可以跨域,不知道是不是bug)。
如果要實現跨域,請參閱微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/security/cors?view=aspnetcore-5.0
在官方說明的基礎上,快速實現跨域,操作如下:
1、在startup.cs文件里,配置方法中,路由之后,節點之前,添加跨域
1 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 2 { 3 if (env.IsDevelopment()) 4 { 5 app.UseDeveloperExceptionPage(); 6 } 7 8 app.UseRouting(); 9 app.UseCors(); 10 app.UseAuthorization(); 11 12 app.UseEndpoints(endpoints => 13 { 14 endpoints.MapControllers(); 15 }); 16 }
如上第九行代碼
2、在webapi控制器里引用跨域名稱空間
using Microsoft.AspNetCore.Cors;
3、在要跨域的類或方法前面,添加跨域特性
1 [Route("api/[controller]/[action]")] 2 [ApiController] 3 [EnableCors] 4 public class HomeController : ControllerBase 5 {
如上第三行
(完)
1. 應該先介紹一下webapi是基於restful規范來實現的,然后解釋一下restful是什么規范 2. 可以把OpenAPI給選中,在項目運行起來后,能看到swagger調用頁面 3. 在第一點中的restful會提到get,post,put,delete,然后介紹什么交冪等性,這四個方法所用的場景是什么。 4. 不建議用httpwebrequest這種類,用httpclient。5. 認證知識也要提及,這一塊內容可以先用jwt作為樣例簡單的介紹,這部分內容可以后續專門章節展開。
以上建議來自技術群友:2003-夜空*星星(qq58926957),感謝寶貴意見!
