立即注册 登录
汉山 返回首页

翰山的个人空间 https://hanshan.info/?2 [收藏] [复制] [分享] [RSS]

日志

Consume Web API By MVC Client In .NET Core (2), Client

已有 362 次阅读2020-12-17 23:31 |个人分类:Tech|系统分类:原创博文 | 宽屏 请点击显示宽屏,再点击恢复窄屏 | 动漫全图 如只见部分动漫,请点击显示全图,再点击恢复窄图


This article will give a way with one line code to consume Web API by a ASP.NET MVC Client in .NET Core.
 

Introduction

 
In the previous article (part I of this article), we created a ASP.NET Core MVC app and associated with a Web API service in it.
  •  MVC is a client/server app, with a web page as a client and SQL server as server, linked by Entity Framework;
  •  Web API is a Server side service, with a RESTful output for consumer, that is linked to database by entity framework.
For our test purpose, MVC and Web API are against two different database, MVC is against the database pubs, while Web API against database DB_Demo_API.
 
In this article, we will make the MVC app as a client to consume Web API. For convinience of analysis and comparison, we will make another MVC module (controller/view) that exactly the same as the previous MVC module but with different name as StoresMVCCallWebAPI controller (see detailes from Part I, A, Step 1, 3, Add controller).
 
The Controller added is like this,
 

 
Visual Studio creates
  • A StroesMVCCallWebAPI controller (Controllers/StoresMVCCallWebAPIController.cs)
  • Razor view files for Create, Delete, Details, Edit, and Index pages (Views/StoresMVCCallWebAPI/*.cshtml)

 
The behavior of the new controller,  StroesMVCCallWebAPI, is exactly the same as the old MVC controller, StroesMVC.

 

Run and Test the app

 
Modify the header of the file: Views/Shared/_layout.cshtml Views, shown below as Red highlighted,
  1. <header>    
  2.     <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">    
  3.         <div class="container">    
  4.             <a class="navbar-brand" asp-area="" asp-controller="StoresMVCCallWebAPI" asp-action="Index"><b>MVC Call Web API</b></a>    
  5.             <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"    
  6.                     aria-expanded="false" aria-label="Toggle navigation">    
  7.                 <span class="navbar-toggler-icon"></span>    
  8.             </button>    
  9.             <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">    
  10.                 <ul class="navbar-nav flex-grow-1">    
  11.                     <li class="nav-item">    
  12.                         <a class="nav-link text-dark" asp-area="" asp-controller="StoresMVC" asp-action="Index">MVC app</a>    
  13.                     </li>    
  14.                     <li class="nav-item">    
  15.                         <a class="nav-link text-dark" asp-area="" asp-controller="Swagger" asp-action="Index">Web API</a>    
  16.                     </li>    
  17.                     <li class="nav-item">    
  18.                         <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>    
  19.                     </li>    
  20.                     <li class="nav-item">    
  21.                         <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>    
  22.                     </li>    
  23.                 </ul>    
  24.             </div>    
  25.         </div>    
  26.     </nav>    
  27. </header>      
Then, we run the app,

 
We can see the two controller endpoints: https://localhost:44350/StoresMVCCallWebAPI (above) and https://localhost:44350/StoresMVC (below) are exactly the same, because the are against the same database --- pubs,
 

 
While the Web API (Swagger),
 

 
 have different data set, that is due to it is against the different database: DB_Demo_API,
 

 
At the end of the article, the controller StoresMVCCallWebAPI will consume the Web API, then these two will share the same database, and get the same result.
 

How to Consume RESTful APIs

 
We can see the most comprehensive list of ways to consume RESTful APIs in your C# projects from this article 《A Few Great Ways to Consume RESTful API in C#》,we borrowed here,
 
"There are several ways to consume a RESTful API in C#,
  •     HttpWebRequest/Response Class
  •     WebClient Class
  •     HttpClient Class
  •     RestSharp NuGet Package
  •     ServiceStack Http Utils
  •     Flurl
  •     DalSoft.RestClient
Every one of these has pros and cons."
 
In this article, we will choose to use HttpClient from Microsoft for our project. In practice or production, you may choose different ones.
 

One Line Code Implementation

 
The database context is used in each of the CRUD methods in the both MVC Controller and Web API ApiController. They have the same methods, same signatures, and implementations.  For each action, we will use one line code to redirect the direct database pubs, to the Web API that is against database DB_Demo_API. 
 
POST
 
We start from Create,  because this is simplest. Get rid of all other actions, we have the controller class with Create method:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Net.Http;  
  5. using System.Threading.Tasks;  
  6. using Microsoft.AspNetCore.Mvc;  
  7. using Microsoft.EntityFrameworkCore;  
  8. using MVCCallWebAPI.Models.DB;  
  9.   
  10. namespace MVCCallWebAPI.Controllers  
  11. {  
  12.     public class StoresMVCCallWebAPIController : Controller  
  13.     {  
  14.         private readonly pubsContext _context;  
  15.   
  16.         public StoresMVCCallWebAPIController(pubsContext context)  
  17.         {  
  18.             _context = context;  
  19.         }  
  20.    
  21.         // POST: StoresMVCCallWebAPI/Create  
  22.         // To protect from overposting attacks, enable the specific properties you want to bind to.  
  23.         // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.  
  24.         [HttpPost]  
  25.         [ValidateAntiForgeryToken]  
  26.         public async Task<IActionResult> Create([Bind("Stor_Id,Stor_Name,Stor_Address,City,State,Zip")] Store store)  
  27.         {  
  28.             if (ModelState.IsValid)  
  29.             {  
  30.                 _context.Add(store);  
  31.                 await _context.SaveChangesAsync();  
  32.                 return RedirectToAction(nameof(Index));  
  33.             }  
  34.             return View(store);  
  35.         }  
  36.     }  
  37. }  
The Create method with a input Object Store, the following two line code save the object into database (pubs) through entity framework.
  1. _context.Add(store);      
  2. await _context.SaveChangesAsync();      
Replace these two line code with class HttpClient, and the method PostAsJsonAsync to call Web API,
  1. HttpClient client = new HttpClient();    
  2. string url = "https://localhost:44350/api/storesAPI/";    
  3. await client.PostAsJsonAsync<Store>(url, store);   
We got the Create method like this,
  1. public async Task<IActionResult> Create([Bind("Stor_Id,Stor_Name,Stor_Address,City,State,Zip")] Store store)  
  2. {  
  3.     if (ModelState.IsValid)  
  4.     {  
  5.         //_context.Add(store);  
  6.         //await _context.SaveChangesAsync();  
  7.   
  8.         // Consume API  
  9.         HttpClient client = new HttpClient();  
  10.         string url = "https://localhost:44350/api/storesWebAPI/";  
  11.   
  12.         await client.PostAsJsonAsync<Store>(url, store);  
  13.   
  14.         return RedirectToAction(nameof(Index));  
  15.     }  
  16.     return View(store);  
  17. }  
We can move the two line shared code (Create HttpClient class and define url address ) into class level, then we only use one line code to complete the job to consume Web API for the Create method (see highlights).
  1. using System;    
  2. using System.Collections.Generic;    
  3. using System.Linq;    
  4. using System.Net.Http;    
  5. using System.Net.Http.Json;    
  6. using System.Threading.Tasks;    
  7. using Microsoft.AspNetCore.Mvc;    
  8. using Microsoft.EntityFrameworkCore;    
  9. using Newtonsoft.Json;    
  10. using WebMVCCore5.Models.DB;    
  11.     
  12. namespace WebMVCCore5.Controllers    
  13. {    
  14.     public class StoresMVCCallAPIController : Controller    
  15.     {    
  16.         private readonly pubsContext _context;    
  17.     
  18.        HttpClient client = new HttpClient();    
  19.        string url = "https://localhost:44330/api/storesWebAPI/";    
  20.     
  21.         public StoresMVCCallAPIController(pubsContext context)    
  22.         {    
  23.             _context = context;    
  24.         }    
  25.     
  26.         // POST: StoresMVCCallAPI/Create    
  27.         // To protect from overposting attacks, enable the specific properties you want to bind to.    
  28.         // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.    
  29.         [HttpPost]    
  30.         [ValidateAntiForgeryToken]    
  31.         public async Task<IActionResult> Create([Bind("Stor_Id,Stor_Name,Stor_Address,City,State,Zip")] Store store)    
  32.         {    
  33.             if (ModelState.IsValid)    
  34.             {    
  35.                 //_context.Add(store);    
  36.                 //await _context.SaveChangesAsync();    
  37.     
  38.                 // Consume API      
  39.                 await client.PostAsJsonAsync<Store>(url, store);    
  40.     
  41.                 return RedirectToAction(nameof(Index));    
  42.             }    
  43.             return View(store);    
  44.         }     
 
DALETE
 
We use DeleteAsync method to do the job,
  1. // POST: StoresMVCCallAPI/Delete/5    
  2. [HttpPost, ActionName("Delete")]    
  3. [ValidateAntiForgeryToken]    
  4. public async Task<IActionResult> DeleteConfirmed(string id)    
  5. {    
  6.     // Original Code:    
  7.     //var store = await _context.Stores.FindAsync(id);    
  8.     //_context.Stores.Remove(store);    
  9.     //await _context.SaveChangesAsync();    
  10.     
  11.     // Consume API    
  12.     await client.DeleteAsync(url + id);    
  13.     
  14.     return RedirectToAction(nameof(Index));    
  15. }     
 
PUT (Edit)
 
We need to bring two parameters, one is id, another is the object, and we use PutAsJsonAsync method
  1. try    
  2. {    
  3.     // Original code:    
  4.     //_context.Update(store);    
  5.     //await _context.SaveChangesAsync();    
  6.     
  7.     // Consume API    
  8.     await client.PutAsJsonAsync<Store>(url + id, store);    
  9. }      
 
GET/id
 
There are three places to use the GET method, but actually, they are the same. We use GetStringAsync. Here, due to getting data, we need to Deserialize the JSON into class, we use JsonConvert.DeserializeObject in Newtonsoft.Json namespace.
  1. // GET: StoresMVCCallAPI/Delete/5    
  2. public async Task<IActionResult> Delete(string id)    
  3. {    
  4.     if (id == null)    
  5.     {    
  6.         return NotFound();    
  7.     }    
  8.     
  9.     // Original code:    
  10.     //var store = await _context.Stores    
  11.     //    .FirstOrDefaultAsync(m => m.Stor_Id == id);    
  12.     
  13.     // Consume API    
  14.     var store = JsonConvert.DeserializeObject<Store>(await client.GetStringAsync(url + id));    
  15.     
  16.     if (store == null)    
  17.     {    
  18.         return NotFound();    
  19.     }    
  20.     
  21.     return View(store);    
  22. }    
 
GET
 
The same as Get/id, but we use List<Store>
  1. // GET: StoresMVCCallAPI    
  2. public async Task<IActionResult> Index()    
  3. {    
  4.     // Original code:    
  5.     //return View(await _context.Stores.ToListAsync());    
  6.     
  7.     // Consume API    
  8.     return View(JsonConvert.DeserializeObject<List<Store>>(await client.GetStringAsync(url)).ToList());    
  9. }     
Finally, we got the full code (highlighted are the changed one line code for each method),
  1. using System.Collections.Generic;    
  2. using System.Linq;    
  3. using System.Net.Http;    
  4. using System.Net.Http.Json;    
  5. using System.Threading.Tasks;    
  6. using Microsoft.AspNetCore.Mvc;    
  7. using Microsoft.EntityFrameworkCore;    
  8. using MVCCallWebAPI.Models.DB;    
  9. using Newtonsoft.Json;    
  10.     
  11. namespace MVCCallWebAPI.Controllers    
  12. {    
  13.     public class StoresMVCCallWebAPIController : Controller    
  14.     {    
  15.         private readonly pubsContext _context;    
  16.     
  17.        HttpClient client = new HttpClient();    
  18.        string url = "https://localhost:44350/api/storesWebAPI/";    
  19.     
  20.         public StoresMVCCallWebAPIController(pubsContext context)    
  21.         {    
  22.             _context = context;    
  23.         }    
  24.     
  25.     
  26.         // GET: StoresMVCCallAPI    
  27.         public async Task<IActionResult> Index()    
  28.         {    
  29.             // Original code:    
  30.             //return View(await _context.Stores.ToListAsync());    
  31.     
  32.             // Consume API    
  33.             return View(JsonConvert.DeserializeObject<List<Store>>(await client.GetStringAsync(url)).ToList());    
  34.         }    
  35.     
  36.         // GET: StoresMVCCallWebAPI/Details/5    
  37.         public async Task<IActionResult> Details(string id)    
  38.         {    
  39.             if (id == null)    
  40.             {    
  41.                 return NotFound();    
  42.             }    
  43.     
  44.             // Original code:    
  45.             //var store = await _context.Stores    
  46.             //    .FirstOrDefaultAsync(m => m.Stor_Id == id);    
  47.     
  48.             // Consume API    
  49.             var store = JsonConvert.DeserializeObject<Store>(await client.GetStringAsync(url + id));    
  50.     
  51.             if (store == null)    
  52.             {    
  53.                 return NotFound();    
  54.             }    
  55.     
  56.             return View(store);    
  57.         }    
  58.     
  59.         // GET: StoresMVCCallWebAPI/Create    
  60.         public IActionResult Create()    
  61.         {    
  62.             return View();    
  63.         }    
  64.     
  65.         // POST: StoresMVCCallWebAPI/Create    
  66.         // To protect from overposting attacks, enable the specific properties you want to bind to.    
  67.         // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.    
  68.         [HttpPost]    
  69.         [ValidateAntiForgeryToken]    
  70.         public async Task<IActionResult> Create([Bind("Stor_Id,Stor_Name,Stor_Address,City,State,Zip")] Store store)    
  71.         {    
  72.             if (ModelState.IsValid)    
  73.             {    
  74.                 // Original code:    
  75.                 //_context.Add(store);    
  76.                 //await _context.SaveChangesAsync();    
  77.     
  78.                 // Consume API    
  79.                 await client.PostAsJsonAsync<Store>(url, store);    
  80.     
  81.                 return RedirectToAction(nameof(Index));    
  82.             }    
  83.             return View(store);    
  84.         }    
  85.     
  86.         // GET: StoresMVCCallWebAPI/Edit/5    
  87.         public async Task<IActionResult> Edit(string id)    
  88.         {    
  89.             if (id == null)    
  90.             {    
  91.                 return NotFound();    
  92.             }    
  93.     
  94.             // Original code:    
  95.             //var store = await _context.Stores.FindAsync(id);    
  96.     
  97.             // Consume API    
  98.             var store = JsonConvert.DeserializeObject<Store>(await client.GetStringAsync(url + id));    
  99.     
  100.             if (store == null)    
  101.             {    
  102.                 return NotFound();    
  103.             }    
  104.             return View(store);    
  105.         }    
  106.     
  107.         // POST: StoresMVCCallWebAPI/Edit/5    
  108.         // To protect from overposting attacks, enable the specific properties you want to bind to.    
  109.         // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.    
  110.         [HttpPost]    
  111.         [ValidateAntiForgeryToken]    
  112.         public async Task<IActionResult> Edit(string id, [Bind("Stor_Id,Stor_Name,Stor_Address,City,State,Zip")] Store store)    
  113.         {    
  114.             if (id != store.Stor_Id)    
  115.             {    
  116.                 return NotFound();    
  117.             }    
  118.     
  119.             if (ModelState.IsValid)    
  120.             {    
  121.                 try    
  122.                 {    
  123.                     // Original code:    
  124.                     //_context.Update(store);    
  125.                     //await _context.SaveChangesAsync();    
  126.     
  127.                     // Consume API    
  128.                     await client.PutAsJsonAsync<Store>(url + id, store);    
  129.                 }    
  130.                 catch (DbUpdateConcurrencyException)    
  131.                 {    
  132.                     if (!StoreExists(store.Stor_Id))    
  133.                     {    
  134.                         return NotFound();    
  135.                     }    
  136.                     else    
  137.                     {    
  138.                         throw;    
  139.                     }    
  140.                 }    
  141.                 return RedirectToAction(nameof(Index));    
  142.             }    
  143.             return View(store);    
  144.         }    
  145.     
  146.         // GET: StoresMVCCallWebAPI/Delete/5    
  147.         public async Task<IActionResult> Delete(string id)    
  148.         {    
  149.             if (id == null)    
  150.             {    
  151.                 return NotFound();    
  152.             }    
  153.     
  154.             // Original code:    
  155.             //var store = await _context.Stores    
  156.             //    .FirstOrDefaultAsync(m => m.Stor_Id == id);    
  157.     
  158.             // Consume API    
  159.             var store = JsonConvert.DeserializeObject<Store>(await client.GetStringAsync(url + id));    
  160.     
  161.             if (store == null)    
  162.             {    
  163.                 return NotFound();    
  164.             }    
  165.     
  166.             return View(store);    
  167.         }    
  168.     
  169.         // POST: StoresMVCCallWebAPI/Delete/5    
  170.         [HttpPost, ActionName("Delete")]    
  171.         [ValidateAntiForgeryToken]    
  172.         public async Task<IActionResult> DeleteConfirmed(string id)    
  173.         {    
  174.             // Original Code:    
  175.             //var store = await _context.Stores.FindAsync(id);    
  176.             //_context.Stores.Remove(store);    
  177.             //await _context.SaveChangesAsync();    
  178.     
  179.             // Consume API    
  180.             await client.DeleteAsync(url + id);    
  181.     
  182.             return RedirectToAction(nameof(Index));    
  183.         }    
  184.     
  185.         private bool StoreExists(string id)    
  186.         {    
  187.             return _context.Stores.Any(e => e.Stor_Id == id);    
  188.         }    
  189.     }    
  190. }      

 

Run and Test the app

 
The MVC module,
 

 
The MVC module Call Web API: the data is different from the MVC module above, but the same as the Web API module.
 

 
 
The Web API module,
 

 

Conclusion

 
In this article, we use MVC, and Web API templates to build out three apps, one is MVC module, one is Web API, then we use HttpClient in MVC module to consume Web API with only one line code manually written.

鲜花

真棒

玩闹

同情

看看

困惑

震惊

bad

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

Archiver|手机版|小黑屋|汉山网    

GMT-5, 2021-5-9 03:07 , Processed in 0.068345 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

返回顶部