Setelah tulisan paling terakhir dari seri ini saya publish pada 16 September 2015, selanjutnya selama hampir 2 bulan saya belum menulis lanjutannya. Alasannya nomor satu pasti karena sibuk dan kemudian disusul menjadi malas meneruskan. Memang suatu kesulitan tersendiri untuk konsisten menuliskan artikel yang berseri.
Alhamdulillah kali ini saya berniat untuk meneruskan seri tulisan ini sampai tamat, sampai total ada 6 bagian.
Tulisan ini merupakan bagian dari tulisan berseri yang terdiri dari beberapa artikel di bawah
1. Memulai
2. Startup
3. MVC 6
4. Dependency Injection
5. Web API (Tulisan ini)
6. Entity Framework 7 (Coming soon)
Dependency dan Startup
Dependency dan konfigurasi startup yang diperlukan untuk membuat Web Api pada ASP.NET 5 sama dengan yang digunakan untuk membuat MVC. Karena sudah sama maka kita tidak perlu melakukannya lagi.
Khusus bagi pembaca yang langsung terdampar ke halaman ini tanpa sebelumnya tahu bagaimana menambahkan dependency dan mengkonfigurasi Startup, saya persilahkan untuk membacanya pada artikel Bagian 3 – MVC 6.
Membuat Model
Model menggambarkan data yang ada pada aplikasi ASP.NET kita. Untuk contoh kali ini kita menggunakan model Book.
Pada folder Models tambahkan file dengan nama Book.cs dan sesuaikan kode menjadi seperti di bawah
1 2 3 4 5 6 7 |
public class Book { public int Id { get; set; } [Required] public string Title { get; set; } public string Description { get; set; } } |
Membuat Repository
Untuk kesederhanaan contoh dan karena akses ke database menggunakan Entity Framework belum saya bahas maka repository yang akan kita gunakan cukuplah menggunakan Dictionary untuk menyimpan objek secara sementara di dalam memory.
Pada folder Repository Buat sebuah file dengan nama IBookRepository.cs dengan kontennya yang seperti di bawah ini.
1 2 3 4 5 6 7 8 |
public interface IBookRepository { Book Save(Book book); List GetAll(); Book Get(string key); Book Delete(string key); Book Update(Book book); } |
Selanjutnya masih di folder yang sama, buat file dengan nama BookRepository yang mengimplementasi interface tadi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class BookRepository : IBookRepository { private static Dictionary<string, Book> _books = new Dictionary<string, Book>(); public Book Delete(string key) { Book book; _books.TryGetValue(key, out book); _books.Remove(book.Id); return book; } public List GetAll() { return _books.Values.ToList(); } public Book Get(string key) { Book book; _books.TryGetValue(key, out book); return book; } public Book Save(Book book) { book.Id = Guid.NewGuid().ToString(); _books.Add(book.Id, book); return book; } public Book Update(Book book) { _books[book.Id] = book; return book; } } |
Mendaftarkan repository untuk di-inject
Dengan membuat interface kita bisa melakukan decouple antara repository dengan controller yang akan menggunakannya nanti.
Kalau dengan menggunakan new maka akan terjadi coupling maka untuk membuat decouple kita menggunakan Dependency Injection.
Masih ingat dengan tulisan sebelumnya pada seri ini?. Ya, cara untuk melakukan DI sudah saya jelaskan di sini. Untuk melakukannya kita peru menambahkan sedikit kode pada file Startup.cs.
Buka file Startup.cs dan tambahkan sebaris kode di bawah ini di dalam method ConfigureServices.
1 2 3 4 5 |
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddTransient<IBookRepository, BookRepository>(); } |
Membuat Controller
Controller ini digunakan untuk menangani request HTTP. Pada folder Controllers buat sebuah file baru dengan nama BookController dan ketikkan kode di bawah ini di dalamnya
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
[Route("api/[controller]")] public class BookController : Controller { private IBookRepository bookRepository; public BukuController(IBookRepository bookRepository) { this.bookRepository = bookRepository; } [HttpGet] public IEnumerable GetAll() { return bookRepository.GetAll(); } [HttpGet("{id}")] public IActionResult Get(string id) { Book book = bookRepository.Get(id); if(book==null) { return HttpNotFound(); } return new ObjectResult(book); } [HttpPost] public IActionResult Create([FromBody] Book book) { if (!ModelState.IsValid) { Context.Response.StatusCode = 400; return new ObjectResult(new { Description = "Book is not valid" }); } else { bookRepository.Save(book); return CreatedAtAction("Get", new { Id = book.Id }, book); } } [HttpPut("{id}")] public IActionResult Update(string id,[FromBody] Book book) { Book bookCheck = bookRepository.Get(id); if(bookCheck==null) { return HttpBadRequest(); } if(bookCheck.Id!=book.Id) { return HttpBadRequest(); } bookRepository.Update(book); return new ObjectResult(book); } [HttpDelete("{id}")] public IActionResult Delete(string id) { Book book = bookRepository.Get(id); if (book == null) { return HttpBadRequest(); } bookRepository.Delete(book.Id); return new ObjectResult(book); } } |
Controller di atas akan di-mapping ke dalam request yang berupa HTTP method dan URL pada tabel di bawah ini
API (HTTP Method+URL) | Deskripsi |
---|---|
GET api/book | Mengambil data semua buku |
GET api/book/{id} | Mengambil data buku berdasarkan id |
POST api/book | Menambah data buku baru |
PUT api/book/{id} | Mengupdate data buku berdasarkan id |
DELETE api/book/{id} | Menghapus data buku berdasarkan id |
Ngomong-ngomong kalau pembaca pernah belajar Web Api pada ASP.NET menggunakan versi sebelum ASP.NET 5 maka akan menemui perbedaan-perbedaan. Yang paling mencolok adalah bahwa MVC dan Web Api sekarang tipe project-nya sama, sama sekali tidak ada bedanya. Hal ini sempat saya singgung pada artikel perkenalan bahwa sekarang ASP.NET memang menjadi 1. Istilah keren yang Microsoft sebutkan adalah One ASP.NET.
Selain masalah tipe project yang sekarang sama dengan MVC, Web Api juga memiliki base controller yang sama lho!. Kalau dulu base controller dari MVC adalah Controller sedangkan untuk Web Api adalah ApiController, sekarang keduanya sama-sama memiliki base class Controller.
Penjelasan Kode Controller
Route
Mungkin yang pertama kali diperhatikan adalah kode ini ya yang ada dibagian awal controller.
1 |
[Route("api/[controller]")] |
Kode di atas digunakan untuk mapping URL ke dalam controller. [controller] maksudnya adalah nama dari kelas controller minus kata Controller di belakangnya. Jadi untuk mengakses controller yang kita buat maka digunakan URL api/book.
Kalau masih asing dengan gaya routing yang menggunakan annotation, coba baca penjelasan saya di artikel Attribute Routing di ASP.NET MVC 5.
HTTP Method
Pada masin-masing method action pada controller di atas terdapat annotation yang menandai method action tersebut bisa di-invoke menggunakan HTTP Method yang mana.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[HttpGet] public IEnumerable GetAll() [HttpGet("{id}")] public IActionResult Get(string id) [HttpPost] public IActionResult Create([FromBody] Book book) [HttpPut("{id}")] public IActionResult Update(string id,[FromBody] Book book) [HttpDelete("{id}")] public IActionResult Delete(string id) |
Khusus untuk yang memiliki {id} maka URL yang digunakan untuk mengakses adalah api/book/{id}. Jika URL yang diakses misalnya adalah api/book/5 berarti id bernilai sama dengan 5.
Kemudian ada lagi [FromBody], digunakan untuk deserialize json yang dikirim melalui request body menjadi objek dari kelas Book. Istilahnya binding, jadi otomatis nilai yang dikirim melalui JSON menjadi nilai property dari objek kelas Book.
Response
Dari kode controller di atas, yok bahas satu persatu yang berkaitan dengan response yang diberikan ke klien.
1 2 3 4 |
public IEnumerable GetAll() { return bookRepository.GetAll(); } |
Web Api ini ketika nilai yang dikembalikan sebagai response adalah sebuah objek atau koleksi dari objek maka yang dikembalikan otomatis berubah menjadi JSON/XML tergantung permintaan klien. Contohnya pada method GetAll, koleksi dari objek Book langsung dijadikan nilai kembalian.
1 2 3 4 5 6 7 8 9 10 11 |
public IActionResult Get(string id) { Book book = bookRepository.Get(id); if(book==null) { return HttpNotFound(); } return new ObjectResult(book); } |
Kali ini berbeda karena nilai yang dikembalikan tidak langsung objek atau koleksi objek tetapi objek yang mengimplementasi interface IActionResult.
Kenapa tidak langsung objek seperti pada method sebelumnya?, jawabannya adalah karena kita perlu mengembalikan HttpNotFound untuk kondisi tertentu yang mana merupakan implementasi dari interface IActionResult. Kalau nilai kembaliannya adalah objek dari Book maka sudah pasti compile error.
Kemudian ketika ingin mengembalikan objek dari kelas Book bagaimana? sedangkan nilai kembalian yang diharuskan adalah IActionResult. Tinggal gunakan saja kelas ObjectResult seperti contoh di atas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[HttpPost] public IActionResult Create([FromBody] Book book) { if (!ModelState.IsValid) { Context.Response.StatusCode = 400; return new ObjectResult(new { Description = "Book is not valid" }); } else { bookRepository.Save(book); return CreatedAtAction("Get", new { Id = book.Id }, book); } } |
Nah yang ini berbeda lagi. Ketika validasi gagal maka Api mengirim kode status 400 dan juga pesan error. Dan kemudian ketika validasi berhasil dan objek baru berhasil dibuat maka digunakan CreatedAtAction yang akan memberikan kode status 200 dan url detail dari objek baru tadi.
Jujur saja untuk Web Api biasanya saya cuma menggunakan 404 kalau error dan 200 untuk sukses haha ? mesti menimba ilmu lagi.
Tes Api
Ngomong-ngomong sudah bisa menggunakan POSTMAN atau tools yang lain seperti Fiddler untuk mengetes Web Api belum?. Kalau belum silahkan belajar ?.
Ini ada tutorial penggunaan POSTMAN, hanya membahas cara untuk mengeksekusi method HTTTP GET.
Sampai jumpa di tulisan selanjutnya yang menjadi tulisan akhir dari seri ASP.NET 5.