.NET Core (Part 1)
Neil Haddley • April 10, 2021
Creating a REST API Service using Microsoft cross-platform framework.
$ dotnet new webapi
$ dotnet new gitignore
$ dotnet run

Example Controller
Creating a Book class
$ code .
Add Book class
Books REST Controller
Add BooksController class
dotnet run
$ dotnet run

https://localhost:5001/books

https://localhost:5001/books/1788395549

https://localhost:5001/books/1234567890
Swagger
Notice that by default Swagger is available.

https://localhost:5001/swagger/index.html
Startup
Support for Swagger is added in the project's Startup.cs file
Static files
Static files can be added to a wwwroot folder in a Dotnet Core webapi project.
The static files can be image files, html files and javascript files.
To enable the static files add "app.UseStaticFiles();" to Startup.cs as shown below.

app.UseStaticFiles()
React client
The static files can be a React app that calls the REST API exposed by the (same) Dotnet Core project.

Running React/Dotnet Core app
haddley_power_app_api
The BooksController code can be extended to support POST, PUT and DELETE actions

haddley_power_app_api
Book.cs
TEXT
1using System; 2 3namespace dotnet_api 4{ 5 public class Book 6 { 7 public Book(long id, String title) 8 { 9 this.id = id; 10 this.title = title; 11 } 12 13 public long id { get; set; } 14 15 public string title { get; set; } 16 } 17}
BooksController.cs
TEXT
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Threading.Tasks; 5using Microsoft.AspNetCore.Mvc; 6using Microsoft.Extensions.Logging; 7 8namespace dotnet_api.Controllers 9{ 10 [ApiController] 11 [Route("[controller]")] 12 public class BooksController : ControllerBase 13 { 14 private static readonly List<Book> books = new List<Book>() 15 { 16 (new Book(1788395549, "Learning Node.js Development")), 17 (new Book(1788620216, "Hands-On Microservices with Node.js")) 18 }; 19 20 private readonly ILogger<BooksController> _logger; 21 22 public BooksController(ILogger<BooksController> logger) 23 { 24 _logger = logger; 25 } 26 27 [HttpGet] 28 public IEnumerable<Book> GetBooks() 29 { 30 return books.ToArray(); 31 } 32 33 [HttpGet("{id}")] 34 public ActionResult<Book> GetBooks(long id) 35 { 36 Book book = books.Find(x => (x.id == id)); 37 if (book == null) 38 { 39 return NotFound("Book Not Found"); 40 } 41 return book; 42 } 43 44 45 } 46}
Startup.cs
TEXT
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Threading.Tasks; 5using Microsoft.AspNetCore.Builder; 6using Microsoft.AspNetCore.Hosting; 7using Microsoft.AspNetCore.HttpsPolicy; 8using Microsoft.AspNetCore.Mvc; 9using Microsoft.Extensions.Configuration; 10using Microsoft.Extensions.DependencyInjection; 11using Microsoft.Extensions.Hosting; 12using Microsoft.Extensions.Logging; 13using Microsoft.OpenApi.Models; 14 15namespace dotnet_api 16{ 17 public class Startup 18 { 19 public Startup(IConfiguration configuration) 20 { 21 Configuration = configuration; 22 } 23 24 public IConfiguration Configuration { get; } 25 26 // This method gets called by the runtime. Use this method to add services to the container. 27 public void ConfigureServices(IServiceCollection services) 28 { 29 30 services.AddControllers(); 31 services.AddSwaggerGen(c => 32 { 33 c.SwaggerDoc("v1", new OpenApiInfo { Title = "dotnet_api", Version = "v1" }); 34 }); 35 } 36 37 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 38 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 39 { 40 if (env.IsDevelopment()) 41 { 42 app.UseDeveloperExceptionPage(); 43 app.UseSwagger(); 44 app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "dotnet_api v1")); 45 } 46 47 app.UseHttpsRedirection(); 48 49 app.UseRouting(); 50 51 app.UseAuthorization(); 52 53 app.UseEndpoints(endpoints => 54 { 55 endpoints.MapControllers(); 56 }); 57 } 58 } 59}
App.js
TEXT
1import React, { useEffect, useState } from 'react'; 2 3function App() { 4 5 const [books, setBooks] = useState([]); 6 7 useEffect(() => { 8 9 console.log("use effect"); 10 fetch('/books') 11 .then( 12 (d) => { 13 d.json().then((b) => { 14 setBooks(() => b) 15 }); 16 } 17 ) 18 19 } 20 , []) 21 22 return ( 23 24 <> 25 {books.map(book => 26 <h4>{book.title}</h4>)} 27 </> 28 29 ); 30} 31 32export default App;
index.html
TEXT
1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <title>React App</title> 6</head> 7 8<body> 9 10 <noscript>You need to enable JavaScript to run this app.</noscript> 11 <div id="root"></div> 12 13 <script>!function (e) {function r(r) {for (var n, i, l = r[0], p = r[1], f = r[2], c = 0, s = []; c < l.length; c++)i = l[c], Object.prototype.hasOwnProperty.call(o, i) && o[i] && s.push(o[i][0]), o[i] = 0; for (n in p) Object.prototype.hasOwnProperty.call(p, n) && (e[n] = p[n]); for (a && a(r); s.length;)s.shift()(); return u.push.apply(u, f || []), t()} function t() {for (var e, r = 0; r < u.length; r++) {for (var t = u[r], n = !0, l = 1; l < t.length; l++) {var p = t[l]; 0 !== o[p] && (n = !1)} n && (u.splice(r--, 1), e = i(i.s = t[0]))} return e} var n = {}, o = {1: 0}, u = []; function i(r) {if (n[r]) return n[r].exports; var t = n[r] = {i: r, l: !1, exports: {}}; return e[r].call(t.exports, t, t.exports, i), t.l = !0, t.exports} i.m = e, i.c = n, i.d = function (e, r, t) {i.o(e, r) || Object.defineProperty(e, r, {enumerable: !0, get: t})}, i.r = function (e) {"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {value: "Module"}), Object.defineProperty(e, "__esModule", {value: !0})}, i.t = function (e, r) {if (1 & r && (e = i(e)), 8 & r) return e; if (4 & r && "object" == typeof e && e && e.__esModule) return e; var t = Object.create(null); if (i.r(t), Object.defineProperty(t, "default", {enumerable: !0, value: e}), 2 & r && "string" != typeof e) for (var n in e) i.d(t, n, function (r) {return e[r]}.bind(null, n)); return t}, i.n = function (e) {var r = e && e.__esModule ? function () {return e.default} : function () {return e}; return i.d(r, "a", r), r}, i.o = function (e, r) {return Object.prototype.hasOwnProperty.call(e, r)}, i.p = "/"; var l = this["webpackJsonpnode-spring-client"] = this["webpackJsonpnode-spring-client"] || [], p = l.push.bind(l); l.push = r, l = l.slice(); for (var f = 0; f < l.length; f++)r(l[f]); var a = p; t()}([])</script> 14 <script src="/js/2.bd91be3c.chunk.js"></script> 15 <script src="/js/main.d36b6e09.chunk.js"></script> 16 17</body> 18 19</html>
BookController.cs
TEXT
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Threading.Tasks; 5using Microsoft.AspNetCore.Mvc; 6using Microsoft.Extensions.Logging; 7 8namespace haddley_power_app_api.Controllers 9{ 10 [ApiController] 11 [Route("[controller]")] 12 public class BookController : ControllerBase 13 { 14 15 private static readonly List<Book> books = new List<Book>() 16 { 17 (new Book(1788395549, "Learning Node.js Development")), 18 (new Book(1788620216, "Hands-On Microservices with Node.js")) 19 }; 20 21 private readonly ILogger<BookController> _logger; 22 23 public BookController(ILogger<BookController> logger) 24 { 25 _logger = logger; 26 } 27 28 [HttpGet] 29 public IEnumerable<Book> GetBooks() 30 { 31 return books.ToArray(); 32 } 33 34 [HttpGet("{id}")] 35 public ActionResult<Book> GetBooks(long id) 36 { 37 var itemFound = books.SingleOrDefault(book => book.id == id); 38 if (itemFound == null) 39 { 40 return NotFound("Book Not Found"); 41 } 42 43 return itemFound; 44 } 45 46 47 [HttpPost] 48 public IActionResult Post([FromBody] Book book) 49 { 50 books.Add(book); 51 52 System.Console.WriteLine(book.title); 53 54 return StatusCode(200); 55 } 56 57 58 [HttpPut("{id}")] 59 public IActionResult Put(long id, [FromBody] Book book) 60 { 61 var itemToUpdate = books.SingleOrDefault(book => book.id == id); 62 if (itemToUpdate != null) 63 itemToUpdate.title = book.title; 64 65 return StatusCode(200); 66 } 67 68 [HttpDelete("{id}")] 69 public IActionResult Delete(long id) 70 { 71 var itemToRemove = books.SingleOrDefault(book => book.id == id); 72 if (itemToRemove != null) 73 books.Remove(itemToRemove); 74 return StatusCode(200); 75 } 76 77 } 78 79}