• 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),感谢宝贵意见!


  • 相关阅读:
    scrapy中selenium的应用
    Django的锁和事务
    redis
    【leetcode】187. Repeated DNA Sequences
    【leetcode】688. Knight Probability in Chessboard
    【leetcode】576. Out of Boundary Paths
    【leetcode】947. Most Stones Removed with Same Row or Column
    【leetcode】948. Bag of Tokens
    【leetcode】946. Validate Stack Sequences
    【leetcode】945. Minimum Increment to Make Array Unique
  • 原文地址:https://www.cnblogs.com/wanjinliu/p/14672559.html
Copyright © 2020-2023  润新知