本文力求用最简的描述,演示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、配置启动文档" PropertieslaunchSettings.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),感谢宝贵意见!