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


  • 相关阅读:
    Event notifications
    twobin博客样式
    Http协议
    ASP.NET Web API自身对CORS的支持: CORS授权检验的实施
    理解计算机系统3
    游标-Oracle游标汇总
    Oracle10g 回收站及彻底删除table : drop table xx purge
    ora-01031:insufficient privileges
    &&运算符和||运算符的优先级问题 专题
    oracle connect by 和start with
  • 原文地址:https://www.cnblogs.com/wanjinliu/p/14672559.html
Copyright © 2020-2023  润新知