VS2019下开发和调用webapi

本文力求用最简的描述,演示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),感谢宝贵意见!


原文地址:https://www.cnblogs.com/wanjinliu/p/14672559.html