- สร้างโปรเจ็กส์แบบ Web API
- Add a data store
- Add a controller
- Implement CRUD operations
- Test web API actions
1.สร้างโปรเจ็กส์แบบ Web API
สร้างโปรเจ็กส์แบบ Web API ชื่อ ContosoPets
2.Add a data store
สร้างโฟลเดอร์ Models และไฟล์ Models/Product.cs
ไฟล์ Models/Product.cs
using System.ComponentModel.DataAnnotations;
namespace ContosoPets.Models
{
public class Product
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[Range(minimum: 0.01, maximum: (double)decimal.MaxValue)]
public decimal Price { get; set; }
}
}
สร้างโฟลเดอร์ Data และไฟล์ Data/ContosoPetsContext.cs ไฟล์ Data/SeedData.cs
ไฟล์ Data/ContosoPetsContext.cs
using ContosoPets.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoPets.Data
{
public class ContosoPetsContext : DbContext
{
public ContosoPetsContext(DbContextOptions<ContosoPetsContext> options)
: base(options)
{
}
public DbSet<Product> Products { get; set; }
}
}
แก้ไขไฟล์ Startup.cs
using ContosoPets.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ContosoPets
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ContosoPetsContext>(options =>
options.UseInMemoryDatabase("ContosoPets"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
ไฟล์ Data/SeedData.cs
using ContosoPets.Models;
using System.Linq;
namespace ContosoPets.Data
{
public static class SeedData
{
public static void Initialize(ContosoPetsContext context)
{
if (!context.Products.Any())
{
context.Products.AddRange(
new Product
{
Name = "Squeaky Bone",
Price = 20.99m
},
new Product
{
Name = "Knotted Rope",
Price = 12.99m
}
);
context.SaveChanges();
}
}
}
}
แก้ไขไฟล์ Program.cs
using ContosoPets.Data;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace ContosoPets
{
public class Program
{
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
SeedDatabase(host);
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
private static void SeedDatabase(IWebHost host)
{
var scopeFactory = host.Services.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ContosoPetsContext>();
if (context.Database.EnsureCreated())
{
try
{
SeedData.Initialize(context);
}
catch (Exception ex)
{
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "A database seeding error occurred.");
}
}
}
}
}
}
3.Add a controller
สร้างไฟล์ Controllers/ProductsController.cs
ถ้าสร้างแบบ API Controller – Empty

ไฟล์ Controllers/ProductsController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace ContosoPets.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
}
}
ถ้าสร้างแบบ API Controller with actions, using Entity Framework


ไฟล์ Controllers/ProductsController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ContosoPets.Data;
using ContosoPets.Models;
namespace ContosoPets.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly ContosoPetsContext _context;
public ProductsController(ContosoPetsContext context)
{
_context = context;
}
// GET: api/Products
[HttpGet]
public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
{
return await _context.Products.ToListAsync();
}
// GET: api/Products/5
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetProduct(long id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
return product;
}
// PUT: api/Products/5
[HttpPut("{id}")]
public async Task<IActionResult> PutProduct(long id, Product product)
{
if (id != product.Id)
{
return BadRequest();
}
_context.Entry(product).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Products
[HttpPost]
public async Task<ActionResult<Product>> PostProduct(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return CreatedAtAction("GetProduct", new { id = product.Id }, product);
}
// DELETE: api/Products/5
[HttpDelete("{id}")]
public async Task<ActionResult<Product>> DeleteProduct(long id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return product;
}
private bool ProductExists(long id)
{
return _context.Products.Any(e => e.Id == id);
}
}
}
แก้ไขไฟล์ Controllers/ProductsController.cs
using ContosoPets.Data;
using ContosoPets.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
namespace ContosoPets.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly ContosoPetsContext _context;
public ProductsController(ContosoPetsContext context)
{
_context = context;
}
[HttpGet]
public ActionResult<List<Product>> GetAll() =>
_context.Products.ToList();
// GET by ID action
// POST action
// PUT action
// DELETE action
}
}
ลองรันดู

> curl -k -s https://localhost:44361/api/values ["value1","value2"]
4.Implement CRUD operations
| HTTP action verb | CRUD operation | ASP.NET Core attribute |
| POST | Create | [HttpPost] |
| GET | Read | [HttpGet] |
| PUT | Update | [HttpPut] |
| DELETE | Delete | [HttpDelete] |
Retrieve a product
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetById(long id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
return product;
}
ถ้าไม่เจอโปรดัก (product == null) จะ return 404 ด้วย NotFound()
แต่ถ้าเจอโปรดักจะ return 200
Add a product
[HttpPost]
public async Task<ActionResult<Product>> Create(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
// CreatedAtAction uses the action name to generate
// a Location HTTP response header with a URL to the newly created product.
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
ถ้าเพิ่มโปรดักสำเร็จจะ return 201
แต่ถ้าไม่สำเร็จจะ return 400
Modify a product
[HttpPut("{id}")]
public async Task<IActionResult> Update(long id, Product product)
{
if (id != product.Id)
{
return BadRequest();
}
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
return NoContent();
}
ถ้าไม่เจอโปรดักจะ return 400 ด้วย BadRequest()
ถ้าเจอ แต่อัพเดทโปรดักไม่สำเร็จจะ return 400
แต่ถ้าอัพเดทโปรดักสำเร็จจะ return 204 ด้วย NoContent()
Remove a product
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(long id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return NoContent();
}
ถ้าไม่เจอโปรดัก (product == null) จะ return 404 ด้วย NotFound()
แต่ถ้าลบโปรดักสำเร็จจะ return 204 ด้วย NoContent()
5.Test web API actions
1.ส่ง invalid HTTP POST request
curl -i -k \
-H "Content-Type: application/json" \
-d "{\"name\":\"Plush Squirrel\",\"price\":0.00}" \
https://localhost:5001/api/Products
-idisplays the HTTP response headers.-dimplies an HTTP POST operation and defines the request body.-Hindicates that the request body is in JSON format. The header’s value overrides the default content type ofapplication/x-www-form-urlencoded.
พอส่ง request นี้ออกไปจะ Error 400 เพราะ Price ต่ำกว่า minimum (0.01)
> curl -i -k -H "Content-Type: application/json" -d "{\"name\":\"Plush Squirrel\",\"price\":0.00}" https://localhost:44361/api/Products
HTTP/1.1 400 Bad Request
Transfer-Encoding: chunked
Content-Type: application/problem+json; charset=utf-8
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNccGhhaXNhcm5zXHNvdXJjZVxyZXBvM1xDb250b3NvUGV0c1xDb250b3NvUGV0c1xhcGlcUHJvZHVjdHM=?=
X-Powered-By: ASP.NET
Date: Tue, 18 Jun 2019 04:51:38 GMT
{"errors":{"Price":["The field Price must be between 0.01 and 7.92281625142643E+28."]},"title":"One or more validation errors occurred.","status":400,"traceId":"80000002-0005-ff00-b63f-84710c7967bb"}
format JSON ให้ดูง่าย
{
"errors":{
"Price":["The field Price must be between 0.01 and 7.92281625142643E+28."]
},
"title":"One or more validation errors occurred.",
"status":400,
"traceId":"80000002-0005-ff00-b63f-84710c7967bb"
}
2.ส่ง valid HTTP POST request
curl -i -k \
-H "Content-Type: application/json" \
-d "{\"name\":\"Plush Squirrel\",\"price\":12.99}" \
https://localhost:5001/api/Products
ส่ง request ออกไปจะได้ 201
> curl -i -k -H "Content-Type: application/json" -d "{\"name\":\"Plush Squirrel\",\"price\":12.99}" https://localhost:44361/api/Products
HTTP/1.1 201 Created
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Location: https://localhost:44361/api/Products/3
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNccGhhaXNhcm5zXHNvdXJjZVxyZXBvM1xDb250b3NvUGV0c1xDb250b3NvUGV0c1xhcGlcUHJvZHVjdHM=?=
X-Powered-By: ASP.NET
Date: Tue, 18 Jun 2019 04:59:24 GMT
{"id":3,"name":"Plush Squirrel","price":12.99}
บรรทัดที่ 5 Location แสดง url ในการเรียกดู item ที่เราสร้างขึ้นมา https://localhost:44361/api/Products/3
3.ส่ง HTTP GET request
curl -k -s https://localhost:5001/api/Products/3 | jq
จะได้
> curl -k -s https://localhost:44361/api/Products/3
{"id":3,"name":"Plush Squirrel","price":12.99}
4.ส่ง HTTP PUT request
curl -i -k \
-X PUT \
-H "Content-Type: application/json" \
-d "{\"id\":2,\"name\":\"Knotted Rope\",\"price\":14.99}" \
https://localhost:5001/api/Products/2
> curl -i -k -X PUT -H "Content-Type: application/json" -d "{\"id\":2,\"name\":\"Knotted Rope\",\"price\":14.99}" https://localhost:44361/api/Products/2
HTTP/1.1 204 No Content
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNccGhhaXNhcm5zXHNvdXJjZVxyZXBvM1xDb250b3NvUGV0c1xDb250b3NvUGV0c1xhcGlcUHJvZHVjdHNcMg==?=
X-Powered-By: ASP.NET
Date: Tue, 18 Jun 2019 05:05:48 GMT
ได้ 204 คืออัพเดทโปรดักสำเร็จ
5.ส่ง HTTP DELETE request
curl -i -k -X DELETE https://localhost:5001/api/Products/1
>curl -i -k -X DELETE https://localhost:44361/api/Products/1 HTTP/1.1 204 No Content Server: Microsoft-IIS/10.0 X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNccGhhaXNhcm5zXHNvdXJjZVxyZXBvM1xDb250b3NvUGV0c1xDb250b3NvUGV0c1xhcGlcUHJvZHVjdHNcMQ==?= X-Powered-By: ASP.NET Date: Tue, 18 Jun 2019 05:11:17 GMT
ได้ 204 คือลบโปรดักสำเร็จ
ตอนนี้ลบ Product id 1 แล้ว ถ้าเรียก id 1 จะได้ 404
> curl -k -s https://localhost:44361/api/Products/1
{
"type":"https://tools.ietf.org/html/rfc7231#section-6.5.4",
"title":"Not Found",
"status":404,
"traceId":"80000002-0004-ff00-b63f-84710c7967bb"
}
6.ส่ง HTTP GET request
curl -k -s https://localhost:44361/api/Products | jq
> curl -k -s https://localhost:44361/api/Products
[
{
"id":2,
"name":"Knotted Rope",
"price":14.99
},
{
"id":3,
"name":"Plush Squirrel",
"price":12.99
}
]
ตอนนี้เหลือแค่ Product 2 และ 3