Bagikan melalui


Penerapan API Web

RESTful web API yang dirancang dengan cermat mendefinisikan skema sumber daya, hubungan, dan navigasi yang dapat diakses oleh aplikasi klien. Saat menerapkan dan menyebarkan web API, Anda harus mempertimbangkan persyaratan fisik lingkungan yang menghosting web API dan cara pembuatan web API daripada mempertimbangkan struktur logis data. Panduan ini berfokus pada praktik terbaik untuk mengimplementasikan web API dan memublikasikannya agar tersedia untuk aplikasi klien. Untuk informasi mendetail tentang desain web API, lihat Desain Web API.

Memproses permintaan

Pertimbangkan poin-poin berikut saat Anda menerapkan kode untuk menangani permintaan.

Tindakan GET, PUT, DELETE, HEAD, dan PATCH harus idempoten

Kode yang mengimplementasikan permintaan ini tidak boleh menimbulkan efek samping apa pun. Permintaan yang sama yang diulang pada sumber daya yang sama akan menghasilkan status yang sama. Misalnya, mengirim beberapa permintaan DELETE ke URI yang sama harus memiliki efek yang sama, meskipun kode status HTTP dalam pesan respons mungkin berbeda. Permintaan DELETE pertama mungkin mengembalikan kode status 204 (Tidak Ada Konten), sedangkan permintaan DELETE berikutnya mungkin mengembalikan kode status 404 (Tidak Ditemukan).

Catatan

Artikel Pola Idempotensi di blog Jonathan Oliver memberikan gambaran umum tentang idempotensi dan bagaimana kaitannya dengan operasi pengelolaan data.

Tindakan POST yang membuat sumber daya baru tidak boleh memiliki efek samping yang tidak terkait

Jika permintaan POST dimaksudkan untuk membuat sumber daya baru, efek permintaan harus dibatasi pada sumber daya baru (dan mungkin sumber daya terkait langsung jika ada semacam tautan yang terlibat). Misalnya, dalam sistem e-niaga, permintaan POST yang membuat pesanan baru untuk pelanggan mungkin juga mengubah tingkat inventori dan menghasilkan informasi penagihan, tetapi tidak boleh memodifikasi informasi yang tidak terkait langsung dengan pesanan atau memiliki efek samping lainnya pada status keseluruhan sistem.

Hindari menerapkan operasi POST, PUT, dan DELETE yang rumit

Mendukung permintaan POST, PUT, dan DELETE melalui koleksi sumber daya. Permintaan POST dapat berisi detail untuk beberapa sumber daya baru dan menambahkan semuanya ke koleksi yang sama, permintaan PUT dapat menggantikan seluruh rangkaian sumber daya dalam koleksi, dan permintaan DELETE dapat menghapus seluruh koleksi.

Dukungan Open Data Protocol (OData) yang disertakan dalam ASP.NET Web API 2 menyediakan kemampuan untuk membuat batch permintaan. Aplikasi klien dapat memaketkan beberapa permintaan web API dan mengirimkannya ke server dalam satu permintaan HTTP, dan menerima satu respons HTTP yang berisi balasan untuk setiap permintaan. Untuk informasi selengkapnya, lihat Memperkenalkan dukungan batch di Web API dan Web API OData.

Ikuti spesifikasi HTTP saat mengirim respons

Web API harus mengembalikan pesan yang berisi kode status HTTP yang benar untuk memungkinkan klien menentukan cara menangani hasilnya, header HTTP yang sesuai sehingga klien memahami sifat hasil, dan badan yang diformat dengan tepat untuk memungkinkan klien mengurai hasilnya.

Misalnya, operasi POST harus mengembalikan kode status 201 (Dibuat) dan pesan respons harus menyertakan URI sumber daya yang baru dibuat di header Lokasi dari pesan respons.

Mendukung negosiasi konten

Isi pesan respons mungkin berisi data dalam berbagai format. Misalnya, permintaan HTTP GET dapat mengembalikan data dalam format JSON atau XML. Saat klien mengirimkan permintaan, itu bisa menyertakan header Terima yang menentukan format data yang bisa ditanganinya. Format ini ditentukan sebagai jenis media. Misalnya, klien yang mengeluarkan permintaan GET yang mengambil gambar dapat menentukan header Terima yang mencantumkan jenis media yang dapat ditangani klien, seperti image/jpeg, image/gif, image/png. Ketika web API mengembalikan hasilnya, itu harus memformat data dengan menggunakan salah satu tipe media ini dan menentukan format di header Jenis-Konten dari respons.

Jika klien tidak menentukan header Terima, gunakan format default yang masuk akal untuk badan respons. Sebagai contoh, kerangka kerja ASP.NET Web API default ke JSON untuk data berbasis teks.

Pendekatan HATEOAS memungkinkan klien untuk menavigasi dan menemukan sumber daya dari titik awal yang pertama. Ini dicapai dengan menggunakan tautan yang berisi URI; ketika klien mengeluarkan permintaan HTTP GET untuk mendapatkan sumber daya, respons harus berisi URI yang memungkinkan aplikasi klien untuk dengan cepat menemukan sumber daya yang terkait langsung. Misalnya, dalam web API yang mendukung solusi e-niaga, pelanggan mungkin telah melakukan banyak pesanan. Saat aplikasi klien mengambil detail untuk pelanggan, respons harus menyertakan tautan yang memungkinkan aplikasi klien mengirim permintaan HTTP GET yang dapat mengambil pesanan ini. Selain itu, tautan gaya HATEOAS harus menjelaskan operasi lain (POST, PUT, DELETE, dan seterusnya) yang didukung oleh setiap sumber daya yang ditautkan bersama dengan URI yang sesuai untuk melakukan setiap permintaan. Pendekatan ini dijelaskan secara lebih rinci dalam desain API.

Saat ini tidak ada standar yang mengatur implementasi HATEOAS, tetapi contoh berikut menggambarkan satu pendekatan yang mungkin. Dalam contoh ini, permintaan HTTP GET yang menemukan detail untuk pelanggan mengembalikan respons yang menyertakan tautan HATEOAS yang mereferensikan pesanan untuk pelanggan tersebut:

GET https://adventure-works.com/customers/2 HTTP/1.1
Accept: text/json
...
HTTP/1.1 200 OK
...
Content-Type: application/json; charset=utf-8
...
Content-Length: ...
{"CustomerID":2,"CustomerName":"Bert","Links":[
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"GET",
    "types":["text/xml","application/json"]},
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"PUT",
    "types":["application/x-www-form-urlencoded"]},
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"DELETE",
    "types":[]},
    {"rel":"orders",
    "href":"https://adventure-works.com/customers/2/orders",
    "action":"GET",
    "types":["text/xml","application/json"]},
    {"rel":"orders",
    "href":"https://adventure-works.com/customers/2/orders",
    "action":"POST",
    "types":["application/x-www-form-urlencoded"]}
]}

Dalam contoh ini, data pelanggan diwakili oleh kelas Customer yang ditampilkan dalam cuplikan kode berikut. Tautan HATEOAS disimpan di properti koleksi Links:

public class Customer
{
    public int CustomerID { get; set; }
    public string CustomerName { get; set; }
    public List<Link> Links { get; set; }
    ...
}

public class Link
{
    public string Rel { get; set; }
    public string Href { get; set; }
    public string Action { get; set; }
    public string [] Types { get; set; }
}

Operasi GET HTTP mengambil data pelanggan dari penyimpanan dan membuat objek Customer, lalu mengisi koleksi Links. Hasilnya diformat sebagai pesan respons JSON. Setiap tautan menyertakan bidang berikut:

  • Hubungan (Rel) antara objek yang dikembalikan dan objek yang dijelaskan oleh tautan. Dalam hal ini self menunjukkan bahwa tautan adalah referensi kembali ke objek itu sendiri (mirip dengan pointer this dalam banyak bahasa yang berorientasi objek), dan orders adalah nama kumpulan yang berisi informasi urutan terkait.
  • Hyperlink (Href) untuk objek yang dijelaskan oleh tautan dalam bentuk URI.
  • Jenis permintaan HTTP (Action) yang dapat dikirim ke URI ini.
  • Format data apa pun (Types) yang harus disediakan dalam permintaan HTTP atau yang dapat dikembalikan dalam respons, bergantung pada jenis permintaan.

Tautan HATEOAS yang ditunjukkan dalam contoh respons HTTP menunjukkan bahwa aplikasi klien dapat melakukan operasi berikut:

  • Permintaan HTTP GET ke URI https://adventure-works.com/customers/2 untuk mengambil detail pelanggan (lagi). Data dapat dikembalikan sebagai XML atau JSON.
  • Permintaan PUT HTTP ke URI https://adventure-works.com/customers/2 untuk mengubah detail pelanggan. Data baru harus disediakan dalam pesan permintaan dalam format x-www-form-urlencoded.
  • Permintaan HTTP DELETE ke URI https://adventure-works.com/customers/2 untuk menghapus pelanggan. Permintaan tidak mengharapkan informasi tambahan atau mengembalikan data di isi pesan respons.
  • Permintaan HTTP GET ke URI https://adventure-works.com/customers/2/orders untuk menemukan semua pesanan untuk pelanggan. Data dapat dikembalikan sebagai XML atau JSON.
  • Permintaan HTTP POST ke URI https://adventure-works.com/customers/2/orders untuk membuat pesanan baru untuk pelanggan ini. Data harus disediakan dalam pesan permintaan dalam format x-www-form-urlencoded.

Menangani pengecualian

Pertimbangkan poin-poin berikut jika sebuah operasi melempar pengecualian yang tidak tertangkap.

Tangkap pengecualian dan kembalikan respons yang berarti kepada klien

Kode yang mengimplementasikan operasi HTTP harus menyediakan penanganan pengecualian yang komprehensif daripada membiarkan pengecualian yang tidak tertangkap menyebar ke kerangka kerja. Jika pengecualian membuatnya tidak mungkin untuk menyelesaikan operasi dengan sukses, pengecualian dapat diteruskan kembali dalam pesan respons, tetapi harus menyertakan deskripsi yang berarti dari kesalahan yang menyebabkan pengecualian. Pengecualian juga harus menyertakan kode status HTTP yang sesuai daripada hanya mengembalikan kode status 500 untuk setiap situasi. Misalnya, jika permintaan pengguna menyebabkan pembaruan database yang melanggar batasan (seperti mencoba menghapus pelanggan yang memiliki pesanan yang belum diselesaikan), Anda harus mengembalikan kode status 409 (Konflik) dan isi pesan yang menunjukkan alasan konflik. Jika beberapa kondisi lain membuat permintaan tidak dapat dicapai, Anda dapat mengembalikan kode status 400 (Permintaan Buruk). Anda dapat menemukan daftar lengkap kode status HTTP di halaman Definisi kode status di situs web W3C.

Contoh kode menjebak kondisi yang berbeda dan mengembalikan respons yang sesuai.

[HttpDelete]
[Route("customers/{id:int}")]
public IHttpActionResult DeleteCustomer(int id)
{
    try
    {
        // Find the customer to be deleted in the repository
        var customerToDelete = repository.GetCustomer(id);

        // If there is no such customer, return an error response
        // with status code 404 (Not Found)
        if (customerToDelete == null)
        {
            return NotFound();
        }

        // Remove the customer from the repository
        // The DeleteCustomer method returns true if the customer
        // was successfully deleted
        if (repository.DeleteCustomer(id))
        {
            // Return a response message with status code 204 (No Content)
            // To indicate that the operation was successful
            return StatusCode(HttpStatusCode.NoContent);
        }
        else
        {
            // Otherwise return a 400 (Bad Request) error response
            return BadRequest(Strings.CustomerNotDeleted);
        }
    }
    catch
    {
        // If an uncaught exception occurs, return an error response
        // with status code 500 (Internal Server Error)
        return InternalServerError();
    }
}

Tip

Jangan sertakan informasi yang dapat berguna bagi penyerang yang mencoba menembus API Anda.

Banyak server web menjebak kondisi kesalahan sendiri sebelum mencapai web API. Misalnya, jika Anda mengonfigurasi autentikasi untuk situs web dan pengguna gagal memberikan informasi autentikasi yang benar, server web harus merespons dengan kode status 401 (Tidak Sah). Setelah klien diautentikasi, kode Anda dapat melakukan pemeriksaannya sendiri untuk memverifikasi bahwa klien harus dapat mengakses sumber daya yang diminta. Jika otorisasi ini gagal, Anda harus mengembalikan kode status 403 (Terlarang).

Tangani pengecualian secara konsisten dan catat informasi tentang kesalahan

Untuk menangani pengecualian secara konsisten, pertimbangkan untuk menerapkan strategi penanganan kesalahan global di seluruh web API. Anda juga harus memasukkan log galat yang menangkap detail lengkap dari setiap pengecualian; log galat ini dapat berisi informasi terperinci selama tidak dapat diakses melalui web oleh klien.

Bedakan antara kesalahan sisi klien dan kesalahan sisi server

Protokol HTTP membedakan antara kesalahan yang terjadi karena aplikasi klien (kode status HTTP 4xx), dan kesalahan yang disebabkan oleh kesalahan pada server (kode status HTTP 5xx). Pastikan Anda menghormati konvensi ini dalam pesan respons kesalahan apa pun.

Mengoptimalkan akses data sisi klien

Dalam lingkungan terdistribusi seperti yang melibatkan server web dan aplikasi klien, salah satu sumber perhatian utama adalah jaringan. Ini dapat bertindak sebagai penyempitan yang cukup besar, terutama jika aplikasi klien sering mengirim permintaan atau menerima data. Oleh karena itu Anda harus bertujuan untuk mengecilkan jumlah lalu lintas yang mengalir di seluruh jaringan. Pertimbangkan poin-poin berikut saat Anda menerapkan kode untuk mengambil dan memelihara data:

Mendukung cache sisi klien

Protokol HTTP 1.1 mendukung cache di klien dan server perantara di mana permintaan dirutekan dengan menggunakan header Cache-Control. Saat aplikasi klien mengirimkan permintaan HTTP GET ke web API, respons dapat menyertakan header Kontrol-Cache yang menunjukkan apakah data di badan respons dapat di-cache dengan aman oleh klien atau server perantara yang digunakan oleh permintaan tersebut. telah dialihkan, dan untuk berapa lama sebelum itu harus kedaluwarsa dan dianggap ketinggalan zaman.

Contoh berikut menunjukkan permintaan HTTP GET dan respons terkait yang menyertakan header Cache-Control:

GET https://adventure-works.com/orders/2 HTTP/1.1
HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: text/json; charset=utf-8
Content-Length: ...
{"orderID":2,"productID":4,"quantity":2,"orderValue":10.00}

Dalam contoh ini, header Cache-Control menetapkan bahwa data yang dikembalikan harus kedaluwarsa setelah 600 detik, dan hanya cocok untuk satu klien dan tidak boleh disimpan dalam cache bersama yang digunakan oleh klien lain (ini bersifat pribadi). Header Cache-Control dapat menentukan public daripada private dalam hal ini data dapat disimpan dalam cache bersama, atau dapat menentukan no-store di dalam hal ini data harus tidak di-cache oleh klien. Contoh kode berikut menunjukkan cara membuat header Cache-Control dalam pesan respons:

public class OrdersController : ApiController
{
    ...
    [Route("api/orders/{id:int:min(0)}")]
    [HttpGet]
    public IHttpActionResult FindOrderByID(int id)
    {
        // Find the matching order
        Order order = ...;
        ...
        // Create a Cache-Control header for the response
        var cacheControlHeader = new CacheControlHeaderValue();
        cacheControlHeader.Private = true;
        cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);
        ...

        // Return a response message containing the order and the cache control header
        OkResultWithCaching<Order> response = new OkResultWithCaching<Order>(order, this)
        {
            CacheControlHeader = cacheControlHeader
        };
        return response;
    }
    ...
}

Kode ini menggunakan kelas IHttpActionResult kustom bernama OkResultWithCaching. Kelas ini memungkinkan pengontrol untuk mengatur konten header cache:

public class OkResultWithCaching<T> : OkNegotiatedContentResult<T>
{
    public OkResultWithCaching(T content, ApiController controller)
        : base(content, controller) { }

    public OkResultWithCaching(T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
        : base(content, contentNegotiator, request, formatters) { }

    public CacheControlHeaderValue CacheControlHeader { get; set; }
    public EntityTagHeaderValue ETag { get; set; }

    public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response;
        try
        {
            response = await base.ExecuteAsync(cancellationToken);
            response.Headers.CacheControl = this.CacheControlHeader;
            response.Headers.ETag = ETag;
        }
        catch (OperationCanceledException)
        {
            response = new HttpResponseMessage(HttpStatusCode.Conflict) {ReasonPhrase = "Operation was cancelled"};
        }
        return response;
    }
}

Catatan

Protokol HTTP juga mendefinisikan arahan no-cache untuk header Cache-Control. Agak membingungkan, arahan ini tidak berarti "jangan meng-cache" melainkan "validasi ulang informasi cache dengan server sebelum mengembalikannya"; data masih dapat di-cache, tetapi diperiksa setiap kali digunakan untuk memastikan bahwa data tersebut masih terkini.

Manajemen cache adalah tanggung jawab aplikasi klien atau server perantara, tetapi jika diterapkan dengan benar, ini dapat menghemat bandwidth dan meningkatkan performa dengan menghilangkan kebutuhan untuk mengambil data yang telah diambil baru-baru ini.

Nilai max-age di header Cache-Control hanyalah panduan dan bukan jaminan bahwa data terkait tidak akan berubah selama waktu yang ditentukan. Web API harus menyetel usia maksimal ke nilai yang sesuai, bergantung pada volatilitas data yang diharapkan. Ketika periode ini berakhir, klien harus membuang objek dari cache.

Catatan

Sebagian besar browser web modern mendukung cache sisi klien dengan menambahkan header kontrol cache yang sesuai ke permintaan dan memeriksa header hasil, seperti yang dijelaskan. Namun, beberapa browser lama tidak akan menyimpan nilai yang dikembalikan dari URL yang menyertakan string kueri dalam cache. Ini biasanya tidak menjadi masalah untuk aplikasi klien kustom yang menerapkan strategi manajemen cache mereka sendiri berdasarkan protokol yang dibahas di sini.

Beberapa proxy lama menunjukkan perilaku yang sama dan mungkin tidak menyimpan permintaan cache berdasarkan URL dengan string kueri. Ini bisa menjadi masalah untuk aplikasi klien kustom yang terhubung ke server web melalui proxy tersebut.

Sediakan ETag untuk mengoptimalkan pemrosesan kueri

Saat aplikasi klien mengambil objek, pesan respons juga dapat menyertakan tag entitas (ETag). ETag adalah string buram yang menunjukkan versi sumber daya; setiap kali sumber daya berubah, ETag juga dimodifikasi. ETag ini harus di-cache sebagai bagian dari data oleh aplikasi klien. Contoh kode berikut menunjukkan cara menambahkan ETag sebagai bagian dari respons terhadap permintaan HTTP GET. Kode ini menggunakan metode GetHashCode dari suatu objek untuk menghasilkan nilai numerik yang mengidentifikasi objek (Anda dapat mengganti metode ini jika perlu dan menghasilkan hash Anda sendiri menggunakan algoritme seperti MD5) :

public class OrdersController : ApiController
{
    ...
    public IHttpActionResult FindOrderByID(int id)
    {
        // Find the matching order
        Order order = ...;
        ...

        var hashedOrder = order.GetHashCode();
        string hashedOrderEtag = $"\"{hashedOrder}\"";
        var eTag = new EntityTagHeaderValue(hashedOrderEtag);

        // Return a response message containing the order and the cache control header
        OkResultWithCaching<Order> response = new OkResultWithCaching<Order>(order, this)
        {
            ...,
            ETag = eTag
        };
        return response;
    }
    ...
}

Pesan respons yang diposting oleh web API terlihat seperti ini:

HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: text/json; charset=utf-8
ETag: "2147483648"
Content-Length: ...
{"orderID":2,"productID":4,"quantity":2,"orderValue":10.00}

Tip

Untuk alasan keamanan, jangan izinkan data atau data sensitif yang dikembalikan melalui koneksi terautentikasi (HTTPS) untuk di-cache.

Aplikasi klien dapat mengeluarkan permintaan GET berikutnya untuk mengambil sumber daya yang sama setiap saat, dan jika sumber daya telah berubah (memiliki ETag yang berbeda), versi cache harus dibuang dan versi baru ditambahkan ke cache. Jika sumber daya besar dan membutuhkan bandwidth yang signifikan untuk mengirimkan kembali ke klien, permintaan berulang untuk mengambil data yang sama dapat menjadi tidak efisien. Untuk mengatasi hal ini, protokol HTTP mendefinisikan proses berikut untuk mengoptimalkan permintaan GET yang harus Anda dukung dalam web API:

  • Klien membuat permintaan GET yang berisi ETag untuk versi cache saat ini dari sumber daya yang dirujuk dalam header HTTP If-None-Match:

    GET https://adventure-works.com/orders/2 HTTP/1.1
    If-None-Match: "2147483648"
    
  • Operasi GET di web API memperoleh ETag saat ini untuk data yang diminta (urutan 2 dalam contoh di atas), dan membandingkannya dengan nilai di header If-None-Match.

  • Jika ETag saat ini untuk data yang diminta cocok dengan ETag yang disediakan oleh permintaan, sumber daya yang tidak berubah dan web API harus mengembalikan respons HTTP dengan isi pesan kosong dan kode status 304 (Tidak Diubah).

  • Jika ETag saat ini untuk data yang diminta tidak cocok dengan ETag yang disediakan oleh permintaan, maka data yang telah berubah dan web API harus mengembalikan respons HTTP dengan data baru di isi pesan dan kode status 200 (OK).

  • Jika data yang diminta tidak ada lagi, maka web API harus mengembalikan respons HTTP dengan kode status 404 (Tidak Ditemukan).

  • Klien menggunakan kode status untuk mempertahankan cache. Jika data tidak berubah (kode status 304) maka objek dapat tetap di-cache dan aplikasi klien harus terus menggunakan versi objek ini. Jika data telah berubah (kode status 200) maka objek cache harus dibuang dan memasukkan objek yang baru. Jika data tidak lagi tersedia (kode status 404) maka objek harus dihapus dari cache.

Catatan

Jika header respons berisi header Cache-Control no-store, maka objek harus selalu dihapus dari cache terlepas dari kode status HTTP.

Kode berikut menunjukkan metode yang FindOrderByID diperluas untuk mendukung header If-None-Match. Perhatikan bahwa jika header If-None-Match dihilangkan, urutan yang ditentukan selalu diambil:

public class OrdersController : ApiController
{
    [Route("api/orders/{id:int:min(0)}")]
    [HttpGet]
    public IHttpActionResult FindOrderByID(int id)
    {
        try
        {
            // Find the matching order
            Order order = ...;

            // If there is no such order then return NotFound
            if (order == null)
            {
                return NotFound();
            }

            // Generate the ETag for the order
            var hashedOrder = order.GetHashCode();
            string hashedOrderEtag = $"\"{hashedOrder}\"";

            // Create the Cache-Control and ETag headers for the response
            IHttpActionResult response;
            var cacheControlHeader = new CacheControlHeaderValue();
            cacheControlHeader.Public = true;
            cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);
            var eTag = new EntityTagHeaderValue(hashedOrderEtag);

            // Retrieve the If-None-Match header from the request (if it exists)
            var nonMatchEtags = Request.Headers.IfNoneMatch;

            // If there is an ETag in the If-None-Match header and
            // this ETag matches that of the order just retrieved,
            // then create a Not Modified response message
            if (nonMatchEtags.Count > 0 &&
                String.CompareOrdinal(nonMatchEtags.First().Tag, hashedOrderEtag) == 0)
            {
                response = new EmptyResultWithCaching()
                {
                    StatusCode = HttpStatusCode.NotModified,
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag
                };
            }
            // Otherwise create a response message that contains the order details
            else
            {
                response = new OkResultWithCaching<Order>(order, this)
                {
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag
                };
            }

            return response;
        }
        catch
        {
            return InternalServerError();
        }
    }
...
}

Contoh ini menggabungkan kelas IHttpActionResult khusus tambahan bernama EmptyResultWithCaching. Kelas ini hanya bertindak sebagai pembungkus di sekitar objek HttpResponseMessage yang tidak berisi badan respons:

public class EmptyResultWithCaching : IHttpActionResult
{
    public CacheControlHeaderValue CacheControlHeader { get; set; }
    public EntityTagHeaderValue ETag { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public Uri Location { get; set; }

    public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage(StatusCode);
        response.Headers.CacheControl = this.CacheControlHeader;
        response.Headers.ETag = this.ETag;
        response.Headers.Location = this.Location;
        return response;
    }
}

Tip

Dalam contoh ini, ETag untuk data dihasilkan dengan hashing data yang diambil dari sumber data yang mendasarinya. Jika ETag dapat dihitung dengan cara lain, maka proses dapat dioptimalkan selengkapnya dan data hanya perlu diambil dari sumber data jika telah berubah. Pendekatan ini sangat berguna jika datanya besar atau mengakses sumber data dapat menghasilkan latensi yang signifikan (misalnya, jika sumber data adalah database jarak jauh).

Menggunakan ETag untuk Mendukung Konkurensi Optimis

Untuk mengaktifkan pembaruan atas data cache sebelumnya, protokol HTTP mendukung strategi konkurensi yang optimis. Jika, setelah mengambil dan menyimpan sumber daya, aplikasi klien kemudian mengirim permintaan PUT atau DELETE untuk mengubah atau menghapus sumber daya, aplikasi tersebut harus menyertakan header If-Match yang mereferensikan ETag. Web API kemudian dapat menggunakan informasi ini untuk menentukan apakah sumber daya telah diubah oleh pengguna lain sejak diambil dan mengirimkan respons yang sesuai kembali ke aplikasi klien sebagai berikut:

  • Klien membuat permintaan PUT yang berisi detail baru untuk sumber daya dan ETag untuk versi cache saat ini dari sumber daya yang dirujuk dalam header HTTP If-Match. Contoh berikut menunjukkan permintaan PUT yang memperbarui pesanan:

    PUT https://adventure-works.com/orders/1 HTTP/1.1
    If-Match: "2282343857"
    Content-Type: application/x-www-form-urlencoded
    Content-Length: ...
    productID=3&quantity=5&orderValue=250
    
  • Operasi PUT di web API memperoleh ETag saat ini untuk data yang diminta (urutan 1 dalam contoh di atas), dan membandingkannya dengan nilai di header If-Match.

  • Jika ETag saat ini untuk data yang diminta cocok dengan ETag yang disediakan oleh permintaan, sumber daya tidak diubah dan web API harus melakukan pembaruan, mengembalikan pesan dengan kode status HTTP 204 (Tidak Ada Konten) jika berhasil. Responsnya dapat menyertakan header Cache-Control dan ETag untuk versi sumber daya yang diperbarui. Respons harus selalu menyertakan header Lokasi yang mereferensikan URI sumber daya yang baru diperbarui.

  • Jika ETag saat ini untuk data yang diminta tidak cocok dengan ETag yang disediakan oleh permintaan, maka data tersebut telah diubah oleh pengguna lain sejak diambil dan web API harus mengembalikan respons HTTP dengan isi pesan kosong dan kode status 412 (Prasyarat Gagal).

  • Jika sumber daya yang akan diperbarui tidak ada lagi, maka web API harus mengembalikan respons HTTP dengan kode status 404 (Tidak Ditemukan).

  • Klien menggunakan kode status dan header respons untuk mempertahankan cache. Jika data telah diperbarui (kode status 204) maka objek dapat tetap di-cache (selama header Cache-Control tidak menentukan no-store) tetapi ETag harus diperbarui. Jika data diubah oleh pengguna lain (kode status 412) atau tidak ditemukan (kode status 404) maka objek yang di-cache harus dibuang.

Contoh kode berikutnya menunjukkan implementasi operasi PUT untuk pengontrol Pesanan:

public class OrdersController : ApiController
{
    [HttpPut]
    [Route("api/orders/{id:int}")]
    public IHttpActionResult UpdateExistingOrder(int id, DTOOrder order)
    {
        try
        {
            var baseUri = Constants.GetUriFromConfig();
            var orderToUpdate = this.ordersRepository.GetOrder(id);
            if (orderToUpdate == null)
            {
                return NotFound();
            }

            var hashedOrder = orderToUpdate.GetHashCode();
            string hashedOrderEtag = $"\"{hashedOrder}\"";

            // Retrieve the If-Match header from the request (if it exists)
            var matchEtags = Request.Headers.IfMatch;

            // If there is an ETag in the If-Match header and
            // this ETag matches that of the order just retrieved,
            // or if there is no ETag, then update the Order
            if (((matchEtags.Count > 0 &&
                String.CompareOrdinal(matchEtags.First().Tag, hashedOrderEtag) == 0)) ||
                matchEtags.Count == 0)
            {
                // Modify the order
                orderToUpdate.OrderValue = order.OrderValue;
                orderToUpdate.ProductID = order.ProductID;
                orderToUpdate.Quantity = order.Quantity;

                // Save the order back to the data store
                // ...

                // Create the No Content response with Cache-Control, ETag, and Location headers
                var cacheControlHeader = new CacheControlHeaderValue();
                cacheControlHeader.Private = true;
                cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);

                hashedOrder = order.GetHashCode();
                hashedOrderEtag = $"\"{hashedOrder}\"";
                var eTag = new EntityTagHeaderValue(hashedOrderEtag);

                var location = new Uri($"{baseUri}/{Constants.ORDERS}/{id}");
                var response = new EmptyResultWithCaching()
                {
                    StatusCode = HttpStatusCode.NoContent,
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag,
                    Location = location
                };

                return response;
            }

            // Otherwise return a Precondition Failed response
            return StatusCode(HttpStatusCode.PreconditionFailed);
        }
        catch
        {
            return InternalServerError();
        }
    }
    ...
}

Tip

Penggunaan header If-Match sepenuhnya bersifat opsional, dan jika dihilangkan, web API akan selalu berusaha memperbarui urutan yang ditentukan, mungkin secara sembarangan menimpa pembaruan yang dibuat oleh pengguna lain. Untuk menghindari masalah karena pembaruan yang hilang, selalu sediakan header If-Match.

Menangani permintaan dan respons besar

Mungkin ada kesempatan ketika aplikasi klien perlu mengeluarkan permintaan yang mengirim atau menerima data yang mungkin berukuran beberapa megabyte (atau lebih besar). Menunggu sementara jumlah data ini ditransmisikan dapat menyebabkan aplikasi klien menjadi tidak responsif. Pertimbangkan poin-poin berikut saat Anda perlu menangani permintaan yang menyertakan sejumlah besar data:

Optimalkan permintaan dan respons yang melibatkan objek besar

Beberapa sumber daya mungkin objek besar atau menyertakan bidang besar, seperti gambar grafis atau jenis data biner lainnya. Web API harus mendukung streaming untuk mengaktifkan pengunggahan dan pengunduhan yang dioptimalkan dari sumber daya ini.

Protokol HTTP menyediakan mekanisme pengodean transfer yang dipotong untuk mengalirkan objek data yang besar kembali ke klien. Saat klien mengirim permintaan HTTP GET untuk objek yang besar, web API dapat mengirim balasan kembali dalam potongan sedikit demi sedikit melalui koneksi HTTP. Panjang data dalam balasan mungkin tidak diketahui awalnya (mungkin dihasilkan), sehingga server yang menghosting API web harus mengirim pesan respons dengan setiap gugus yang menentukan Transfer-Encoding: Chunked header daripada header Content-Length. Aplikasi klien dapat menerima setiap potongan secara bergantian untuk membangun respons yang lengkap. Transfer data selesai saat server mengirim kembali potongan terakhir dengan ukuran nol.

Satu permintaan dapat menghasilkan objek besar yang menghabiskan banyak sumber daya. Jika selama proses streaming web API menentukan bahwa jumlah data dalam permintaan telah melampaui beberapa batas yang dapat diterima, web API dapat membatalkan operasi dan mengembalikan pesan respons dengan kode status 413 (Entitas Permintaan Terlalu Besar).

Anda dapat mengecilkan ukuran objek besar yang ditransmisikan melalui jaringan dengan menggunakan pemadatan HTTP. Pendekatan ini membantu mengurangi jumlah lalu lintas jaringan dan latensi jaringan terkait, tetapi dengan biaya yang memerlukan pemrosesan tambahan di klien dan server yang menghosting web API. Misalnya, aplikasi klien yang mengharapkan untuk menerima data terkompresi dapat menyertakan Accept-Encoding: gzip header permintaan (algoritma kompresi data lainnya juga dapat ditentukan). Jika server mendukung pemadatan, server harus merespons dengan konten yang disimpan dalam format gzip di isi pesan dan Content-Encoding: gzip header respons.

Anda dapat menggabungkan pemadatan yang dienkode dengan streaming; padatkan data terlebih dahulu sebelum streaming, lalu tentukan pengodean konten gzip dan pengodean transfer yang dipoting di header pesan. Perhatikan juga bahwa beberapa server web (seperti Server Informasi Internet) dapat dikonfigurasi untuk memadatkan respons HTTP secara otomatis terlepas dari apakah web API memadatkan data atau tidak.

Menerapkan respons parsial untuk klien yang tidak mendukung operasi asinkron

Sebagai alternatif streaming asinkron, aplikasi klien dapat secara eksplisit meminta data untuk objek besar dalam potongan, yang dikenal sebagai respons parsial. Aplikasi klien mengirimkan permintaan HTTP HEAD untuk mendapatkan informasi tentang objek. Jika API web mendukung respons parsial, API harus merespons permintaan HEAD dengan pesan respons yang berisi Accept-Ranges header dan Content-Length header yang menunjukkan ukuran total objek, tetapi isi pesan harus kosong. Aplikasi klien dapat menggunakan informasi ini untuk membuat serangkaian permintaan GET yang menentukan kisaran byte yang akan diterima. API web harus mengembalikan pesan respons dengan status HTTP 206 (Konten Parsial), header Panjang Konten yang menentukan jumlah data aktual yang disertakan dalam isi pesan respons, dan header Rentang Konten yang menunjukkan bagian mana (seperti byte 4000 ke 8000) objek yang diwakili data ini.

Permintaan HTTP HEAD dan respons parsial dijelaskan secara lebih rinci dalam desain API.

Hindari mengirim pesan status 100-Continue yang tidak perlu di aplikasi klien

Aplikasi klien yang akan mengirim sejumlah besar data ke server dapat menentukan terlebih dahulu apakah server benar-benar bersedia menerima permintaan tersebut. Sebelum mengirim data, aplikasi klien dapat mengirimkan permintaan HTTP dengan jeader Expect: 100-Continue, header Content-Length yang menunjukkan ukuran data, tetapi isi pesan kosong. Jika server bersedia menangani permintaan, server harus merespons dengan pesan yang menetapkan status HTTP 100 (Continue). Aplikasi klien kemudian dapat melanjutkan dan mengirim permintaan lengkap termasuk data di isi pesan.

Jika Anda menghosting layanan dengan menggunakan Layanan Informasi Internet (IIS), driver HTTP.sys secara otomatis mendeteksi dan menangani Expect: 100-Continue headers sebelum meneruskan permintaan ke aplikasi web Anda. Hal ini berarti bahwa Anda tidak mungkin melihat header ini dalam kode aplikasi Anda, dan Anda dapat berasumsi bahwa IIS telah memfilter pesan apa pun yang dianggap tidak layak atau terlalu besar.

Jika Anda membangun aplikasi klien dengan menggunakan .NET Framework, maka semua pesan POST dan PUT akan terlebih dahulu mengirim pesan dengan Expect: 100-Continue headers secara default. Seperti pada sisi server, proses ditangani secara transparan oleh .NET Framework. Namun, proses ini akan menghasilkan setiap permintaan POST dan PUT mengirimkan dua perjalanan bolak-balik ke server, bahkan untuk permintaan kecil. Jika aplikasi Anda tidak mengirimkan permintaan dengan data dalam jumlah besar, Anda dapat menonaktifkan fitur ini dengan menggunakan kelas ServicePointManager untuk membuat objek ServicePoint di aplikasi klien. Objek ServicePoint menangani koneksi yang dibuat klien ke server berdasarkan skema dan fragmen host URI yang mengidentifikasi sumber daya di server. Anda kemudian dapat menyetel properti Expect100Continue dari objek ServicePoint ke false. Semua permintaan POST dan PUT berikutnya yang dibuat oleh klien melalui URI yang cocok dengan skema dan fragmen host dari objek ServicePoint akan dikirim tanpa header Expect: 100-Continue. Kode berikut menunjukkan cara mengonfigurasi objek ServicePoint yang mengonfigurasi semua permintaan yang dikirim ke URI dengan skema http dan host www.contoso.com.

Uri uri = new Uri("https://www.contoso.com/");
ServicePoint sp = ServicePointManager.FindServicePoint(uri);
sp.Expect100Continue = false;

Anda juga dapat menyetel properti Expect100Continue statik dari kelas ServicePointManager untuk menetapkan nilai default properti ini untuk semua objek ServicePoint yang dibuat selanjutnya.

Mendukung penentuan halaman untuk permintaan yang dapat mengembalikan objek dalam jumlah besar

Jika kumpulan berisi sejumlah besar sumber daya, mengeluarkan permintaan GET ke URI yang sesuai dapat mengakibatkan pemrosesan yang signifikan pada server yang menghosting web API yang memengaruhi performa, dan menghasilkan sejumlah besar lalu lintas jaringan yang mengakibatkan peningkatan latensi.

Untuk menangani kasus ini, web API harus mendukung string kueri yang memungkinkan aplikasi klien menyaring permintaan atau mengambil data dalam blok (atau halaman) yang lebih mudah dikelola dan terpisah. Kode berikut menunjukkan GetAllOrders metode dalam Orders pengontrol. Metode ini mengambil detail pesanan. Jika metode ini tidak dibatasi, metode ini dapat mengembalikan sejumlah besar data. Parameter limit dan offset dimaksudkan untuk mengurangi volume data menjadi subset yang lebih kecil, dalam hal ini hanya 10 pesanan pertama secara default:

public class OrdersController : ApiController
{
    ...
    [Route("api/orders")]
    [HttpGet]
    public IEnumerable<Order> GetAllOrders(int limit=10, int offset=0)
    {
        // Find the number of orders specified by the limit parameter
        // starting with the order specified by the offset parameter
        var orders = ...
        return orders;
    }
    ...
}

Aplikasi klien dapat mengeluarkan permintaan untuk mengambil 30 pesanan mulai dari offset 50 dengan menggunakan URI https://www.adventure-works.com/api/orders?limit=30&offset=50.

Tip

Hindari mengaktifkan aplikasi klien untuk menentukan untai (karakter) kueri yang menghasilkan URI yang panjangnya lebih dari 2000 karakter. Banyak klien web dan server tidak dapat menangani URI yang panjang ini.

Mempertahankan responsivitas, skalabilitas, dan ketersediaan

Web API yang sama mungkin digunakan oleh banyak aplikasi klien yang berjalan di mana saja di dunia. Penting untuk memastikan bahwa web API diimplementasikan untuk mempertahankan responsivitas di bawah beban yang berat, agar dapat diskalakan untuk mendukung beban kerja yang sangat bervariasi, dan untuk menjamin ketersediaan bagi klien yang melakukan operasi penting bisnis. Pertimbangkan poin-poin berikut saat menentukan bagaimana memenuhi persyaratan ini:

Berikan dukungan asinkron untuk permintaan yang sudah berjalan lama

Permintaan yang mungkin membutuhkan waktu lama untuk diproses harus dilakukan tanpa memblokir klien yang mengajukan permintaan. Web API dapat melakukan beberapa pemeriksaan awal untuk memvalidasi permintaan, memulai tugas terpisah untuk melakukan pekerjaan, dan kemudian mengembalikan pesan respons dengan kode HTTP 202 (Diterima). Tugas dapat berjalan secara asinkron sebagai bagian dari pemrosesan web API, atau dapat dipindahkan ke tugas latar belakang.

Web API juga harus menyediakan mekanisme untuk mengembalikan hasil pemrosesan ke aplikasi klien. Anda dapat mencapai hal ini dengan menyediakan mekanisme polling untuk aplikasi klien untuk secara berkala menanyakan apakah pemrosesan telah selesai dan mendapatkan hasilnya, atau mengaktifkan web API untuk mengirim pemberitahuan ketika operasi telah selesai.

Anda dapat menerapkan mekanisme polling sederhana dengan menyediakan polling URI yang berfungsi sebagai sumber daya virtual menggunakan pendekatan berikut:

  1. Aplikasi klien mengirimkan permintaan awal ke web API.
  2. API web menyimpan informasi tentang permintaan dalam tabel yang disimpan di Azure Table Storage atau Microsoft Azure Cache dan menghasilkan kunci unik untuk entri ini, mungkin dalam bentuk pengidentifikasi unik global (GUID). Atau, pesan yang berisi informasi tentang permintaan dan kunci unik juga dapat dikirim melalui Azure Bus Layanan.
  3. API web memulai pemrosesan sebagai tugas terpisah atau dengan pustaka seperti Hangfire. Web API merekam status tugas dalam tabel sebagai Berjalan.
    • Jika Anda menggunakan Azure Bus Layanan, pemrosesan pesan akan dilakukan secara terpisah dari API, mungkin dengan menggunakan Azure Functions atau AKS.
  4. API web mengembalikan pesan respons dengan kode status HTTP 202 (Diterima), dan URI yang berisi kunci unik yang dihasilkan - sesuatu seperti /polling/{guid}.
  5. Ketika tugas telah selesai, API web menyimpan hasil dalam tabel, dan mengatur status tugas menjadi Selesai. Perhatikan bahwa jika tugas gagal, web API juga dapat menyimpan informasi tentang kegagalan dan menyetel status ke Gagal.
    • Pertimbangkan untuk menerapkan teknik coba lagi untuk mengatasi kemungkinan kegagalan sementara.
  6. Saat tugas sedang berjalan, klien dapat terus melakukan pemrosesannya sendiri. Ini dapat secara berkala mengirim permintaan ke URI yang diterima sebelumnya.
  7. API web di URI meminta status tugas yang sesuai dalam tabel dan mengembalikan pesan respons dengan kode status HTTP 200 (OK) yang berisi status ini (Berjalan, Selesai, atau Gagal). Jika tugas telah selesai atau gagal, pesan respons juga dapat menyertakan hasil pemrosesan atau informasi apa pun yang tersedia tentang alasan kegagalan tersebut.
    • Jika proses jangka panjang memiliki status yang lebih menengah, lebih baik menggunakan pustaka yang mendukung pola saga, seperti NServiceBus atau MassTransit.

Opsi untuk menerapkan notifikasi meliputi:

Pastikan bahwa setiap permintaan tidak memiliki status

Setiap permintaan harus dianggap atom. Seharusnya tidak ada ketergantungan antara satu permintaan yang dibuat oleh aplikasi klien dan permintaan berikutnya yang diajukan oleh klien yang sama. Pendekatan ini membantu dalam skalabilitas; instans layanan web dapat digunakan di sejumlah server. Permintaan klien dapat diarahkan ke salah satu instans ini dan hasilnya harus selalu sama. Ini juga meningkatkan ketersediaan untuk alasan yang sama; jika server web gagal, permintaan dapat dirutekan ke instans lain (dengan menggunakan Azure Traffic Manager) saat server dimulai ulang tanpa efek buruk pada aplikasi klien.

Lacak klien dan terapkan pembatasan untuk mengurangi kemungkinan serangan DoS

Jika klien tertentu membuat sejumlah besar permintaan dalam jangka waktu tertentu, hal itu dapat memonopoli layanan dan memengaruhi performa klien lain. Untuk mengurangi masalah ini, web API dapat memantau panggilan dari aplikasi klien baik dengan melacak alamat IP dari semua permintaan yang masuk atau dengan mencatat setiap akses yang diautentikasi. Anda dapat menggunakan informasi ini untuk membatasi akses sumber daya. Jika klien melebihi batas yang ditentukan, web API dapat mengembalikan pesan respons dengan status 503 (Layanan Tidak Tersedia) dan menyertakan header Retry-After yang menentukan kapan klien dapat mengirim permintaan berikutnya tanpa ditolak. Strategi ini dapat membantu mengurangi kemungkinan serangan Denial Of Service (DoS) dari sekumpulan klien yang mengulur-ulur sistem.

Kelola koneksi HTTP persisten dengan hati-hati

Protokol HTTP mendukung koneksi HTTP persisten jika tersedia. Spesifikasi HTTP 1.0 menambahkan header Connection: Keep-Alive yang memungkinkan aplikasi klien untuk menunjukkan ke server bahwa aplikasi klien dapat menggunakan koneksi yang sama untuk mengirim permintaan berikutnya daripada membuka yang baru. Koneksi ditutup secara otomatis jika klien tidak menggunakan kembali sambungan dalam jangka waktu yang ditentukan oleh host. Perilaku ini adalah default di HTTP 1.1 seperti yang digunakan oleh layanan Azure, jadi tidak perlu menyertakan header Keep-Alive dalam pesan.

Menjaga koneksi tetap terbuka dapat membantu meningkatkan daya tanggap dengan mengurangi latensi dan kemacetan jaringan, tetapi dapat merusak skalabilitas dengan menjaga koneksi yang tidak perlu terbuka lebih lama dari yang dibutuhkan, membatasi kemampuan klien konkuren lainnya untuk terhubung. Hal ini juga dapat memengaruhi masa pakai baterai jika aplikasi klien berjalan di perangkat seluler; jika aplikasi hanya sesekali membuat permintaan ke server, mempertahankan koneksi terbuka dapat menyebabkan baterai lebih cepat habis. Untuk memastikan bahwa koneksi tidak dibuat persisten dengan HTTP 1.1, klien dapat menyertakan header Connection:Close dengan pesan untuk mengambil alih perilaku default. Demikian pula, jika server menangani klien dalam jumlah yang sangat besar, server dapat menyertakan header Connection:Close dalam pesan respons yang seharusnya menutup koneksi dan menghemat sumber daya server.

Catatan

Koneksi HTTP persisten adalah fitur opsional murni untuk mengurangi overhead jaringan yang terkait dengan pembuatan saluran komunikasi berulang kali. Baik web API maupun aplikasi klien tidak boleh bergantung pada koneksi HTTP persisten yang tersedia. Jangan gunakan koneksi HTTP persisten untuk mengimplementasikan sistem pemberitahuan gaya Comet; sebagai gantinya, Anda harus menggunakan soket (atau soket web jika tersedia) di lapisan TCP. Terakhir, perhatikan bahwa header Keep-Alive hanya dapat digunakan secara terbatas jika aplikasi klien berkomunikasi dengan server melalui proxy; hanya koneksi dengan klien dan proxy yang akan tetap ada.

Menerbitkan dan mengelola web API

Untuk membuat web API tersedia untuk aplikasi klien, web API harus disebarkan ke lingkungan host. Lingkungan ini biasanya berupa server web, meskipun mungkin bisa berupa beberapa jenis lain dari proses host. Anda harus mempertimbangkan poin-poin berikut saat memublikasikan web API:

  • Semua permintaan harus diautentikasi dan disahkan, dan tingkat kontrol akses yang sesuai harus ditegakkan.
  • Web API komersial mungkin tunduk pada berbagai jaminan kualitas terkait waktu respons. Penting untuk memastikan bahwa lingkungan host dapat diskalakan jika beban dapat bervariasi secara signifikan dari waktu ke waktu.
  • Mungkin perlu mengukur permintaan untuk tujuan monetisasi.
  • Mungkin perlu untuk mengatur arus lalu lintas ke web API, dan menerapkan pembatasan untuk klien tertentu yang telah kehabisan kuota.
  • Persyaratan peraturan mungkin memandatkan pengelogan dan audit semua permintaan dan respons.
  • Untuk memastikan ketersediaan, mungkin diperlukan untuk memantau kesehatan server yang menghosting web API dan memulai ulang jika perlu.

Sangat berguna untuk dapat memisahkan masalah ini dari masalah teknis mengenai implementasi API web. Karena alasan ini, pertimbangkan untuk membuat façade, yang berjalan sebagai proses terpisah dan yang mengarahkan permintaan ke web API. façade dapat menyediakan operasi manajemen dan meneruskan permintaan yang divalidasi ke web API. Menggunakan façade juga dapat membawa banyak keuntungan fungsional, termasuk:

  • Bertindak sebagai titik integrasi untuk beberapa web API.
  • Mengubah pesan dan menerjemahkan protokol komunikasi untuk klien yang dibangun dengan menggunakan berbagai teknologi.
  • Meng-cache permintaan dan respons untuk mengurangi beban pada server yang menghosting web API.

Menguji web API

Web API harus diuji secara menyeluruh seperti perangkat lunak lainnya. Anda harus mempertimbangkan untuk membuat pengujian unit untuk memvalidasi fungsionalitas.

Sifat web API membawa persyaratan tambahannya sendiri untuk memverifikasi bahwa web API beroperasi dengan benar. Anda harus memberi perhatian khusus pada aspek-aspek berikut:

  • Uji semua rute untuk memverifikasi bahwa semua rute telah meminta operasi yang benar. Waspadai kode status HTTP 405 (Metode Tidak Diizinkan) yang dikembalikan secara tidak terduga karena hal ini dapat menunjukkan ketidakcocokan antara rute dan metode HTTP (GET, POST, PUT, DELETE) yang dapat dikirim ke rute tersebut.

    Kirim permintaan HTTP ke rute yang tidak mendukungnya, seperti mengirimkan permintaan POST ke sumber daya tertentu (permintaan POST hanya boleh dikirim ke koleksi sumber daya). Dalam kasus ini, satu-satunya respons yang valid seharusnya adalah kode status 405 (Tidak Diizinkan).

  • Verifikasi bahwa semua rute dilindungi dengan benar dan tunduk pada pemeriksaan autentikasi dan otorisasi yang sesuai.

    Catatan

    Beberapa aspek keamanan seperti autentikasi pengguna kemungkinan besar menjadi tanggung jawab lingkungan host daripada web API, tetapi pengujian keamanan masih perlu disertakan sebagai bagian dari proses penyebaran.

  • Uji penanganan pengecualian yang dilakukan oleh setiap operasi dan verifikasi bahwa respons HTTP yang sesuai dan bermakna diteruskan kembali ke aplikasi klien.

  • Verifikasi bahwa pesan permintaan dan respons telah disusun dengan baik. Misalnya, jika permintaan HTTP POST berisi data untuk sumber daya baru dalam format x-www-form-urlencoded, konfirmasikan bahwa operasi terkait menguraikan data dengan benar, membuat sumber daya, dan mengembalikan respons yang berisi detail sumber daya baru, termasuk header Lokasi yang benar.

  • Verifikasi semua tautan dan URI dalam pesan respons. Misalnya, pesan HTTP POST harus mengembalikan URI dari sumber daya yang baru dibuat. Semua tautan HATEOAS harus valid.

  • Pastikan bahwa setiap operasi mengembalikan kode status yang benar untuk kombinasi input yang berbeda. Contohnya:

    • Jika kueri berhasil, kueri tersebut harus mengembalikan kode status 200 (OK)
    • Jika sumber daya tidak ditemukan, operasi harus mengembalikan kode status HTTP 404 (Tidak Ditemukan).
    • Jika klien mengirim permintaan yang berhasil menghapus sumber daya, kode status harus 204 (Tidak Ada Konten).
    • Jika klien mengirim permintaan yang membuat sumber daya baru, kode status harus 201 (Dibuat).

Hati-hati dengan kode status respons tak terduga dalam kisaran 5xx. Pesan-pesan ini biasanya dilaporkan oleh server host untuk menunjukkan bahwa server host tidak dapat memenuhi permintaan yang valid.

  • Uji kombinasi header permintaan berbeda yang dapat ditentukan oleh aplikasi klien dan pastikan bahwa web API mengembalikan informasi yang diharapkan dalam pesan respons.

  • Uji untai (karakter) kueri. Jika operasi dapat mengambil parameter opsional (seperti permintaan penentuan halaman), uji kombinasi dan urutan parameter yang berbeda.

  • Verifikasi bahwa operasi asinkron berhasil diselesaikan. Jika web API mendukung streaming untuk permintaan yang mengembalikan objek biner besar (seperti video atau audio), pastikan bahwa permintaan klien tidak diblokir saat data dialirkan. Jika web API mengimplementasikan polling untuk operasi modifikasi data yang berjalan lama, verifikasi bahwa operasi melaporkan statusnya dengan benar saat mereka melanjutkan.

Anda juga harus membuat dan menjalankan pengujian performa untuk memeriksa apakah web API beroperasi dengan baik di bawah tekanan. Anda dapat membangun performa web dan memuat proyek pengujian dengan menggunakan Visual Studio Ultimate.

Menggunakan Azure API Management

Di Azure, pertimbangkan untuk menggunakan Azure API Management untuk memublikasikan dan mengelola web API. Dengan menggunakan fasilitas ini, Anda dapat menghasilkan layanan yang bertindak sebagai façade untuk satu atau lebih web API. Layanan itu sendiri adalah layanan web yang dapat diskalakan yang dapat Anda buat dan konfigurasikan dengan menggunakan portal Microsoft Azure. Anda dapat menggunakan layanan ini untuk memublikasikan dan mengelola web API sebagai berikut:

  1. Sebarkan web API ke situs web, layanan cloud Azure, atau mesin virtual Azure.

  2. Hubungkan layanan manajemen API ke web API. Permintaan yang dikirim ke URL API manajemen dipetakan ke URI di web API. Layanan manajemen API yang sama dapat merutekan permintaan ke lebih dari satu web API. Tindakan ini memungkinkan Anda untuk menggabungkan beberapa web API ke dalam satu layanan manajemen. Demikian pula, web API yang sama dapat dirujuk dari lebih dari satu layanan manajemen API jika Anda perlu membatasi atau mempartisi fungsionalitas yang tersedia untuk aplikasi yang berbeda.

    Catatan

    URI, dalam tautan HATEOAS yang dihasilkan sebagai bagian dari respons untuk permintaan HTTP GET, harus mereferensikan URL layanan manajemen API dan bukan server web yang menghosting API web.

  3. Untuk setiap web API, tentukan operasi HTTP yang diekspos oleh web API bersama dengan parameter opsional apa pun yang dapat diambil oleh operasi sebagai input. Anda juga dapat mengonfigurasi apakah layanan manajemen API harus meng-cache respons yang diterima dari web API untuk mengoptimalkan permintaan berulang untuk data yang sama. Catat detail respons HTTP yang dapat dihasilkan setiap operasi. Informasi ini digunakan untuk menghasilkan dokumentasi bagi pengembang, jadi penting untuk memastikan keakuratan dan kelengkapannya.

    Anda dapat menentukan operasi secara manual menggunakan panduan yang disediakan oleh portal Microsoft Azure, atau Anda dapat mengimpornya dari file yang berisi definisi dalam format WADL atau Swagger.

  4. Konfigurasikan pengaturan keamanan untuk komunikasi antara layanan manajemen API dan server web yang menghosting web API. Layanan manajemen API saat ini mendukung autentikasi dasar dan autentikasi bersama menggunakan sertifikat, dan otorisasi pengguna Open Authorization (OAuth) 2.0.

  5. Membuat produk. Produk adalah unit publikasi; Anda menambahkan web API yang sebelumnya Anda sambungkan ke layanan manajemen ke produk. Saat produk diterbitkan, web API tersedia untuk pengembang.

    Catatan

    Sebelum memublikasikan produk, Anda juga dapat menentukan grup pengguna yang dapat mengakses produk dan menambahkan pengguna ke grup ini. Tindakan ini memberi Anda kendali atas pengembang dan aplikasi yang dapat menggunakan web API. Jika web API harus disetujui, sebelum dapat mengaksesnya, pengembang harus mengirim permintaan ke administrator produk. Administrator dapat memberikan atau menolak akses ke pengembang. Pengembang yang ada juga dapat diblokir jika keadaan berubah.

  6. Konfigurasikan kebijakan untuk setiap web API. Kebijakan mengatur aspek seperti apakah panggilan lintas domain harus diizinkan, cara mengautentikasi klien, apakah akan mengonversi antara format data XML dan JSON secara transparan, apakah akan membatasi panggilan dari rentang IP tertentu, kuota penggunaan, dan apakah akan membatasi tingkat panggilan. Kebijakan dapat diterapkan secara global di seluruh produk, untuk satu web API dalam suatu produk, atau untuk operasi individual di web API.

Untuk informasi selengkapnya, lihat dokumentasi API Management.

Tip

Azure menyediakan Azure Traffic Manager yang memungkinkan Anda menerapkan failover dan load-balancing, dan mengurangi latensi di beberapa instans situs web yang dihosting di lokasi geografis yang berbeda. Anda dapat menggunakan Azure Traffic Manager bersama dengan Layanan API Management; Layanan API Management dapat merutekan permintaan ke instans situs web melalui Azure Traffic Manager. Untuk informasi selengkapnya, lihat Metode perutean Traffic Manager.

Dalam struktur ini, jika Anda menggunakan nama DNS kustom untuk situs web Anda, Anda harus mengonfigurasi catatan CNAME yang sesuai untuk setiap situs web untuk menunjuk ke nama DNS situs web Azure Traffic Manager.

Mendukung pengembang sisi klien

Pengembang yang membangun aplikasi klien biasanya memerlukan informasi tentang cara mengakses web API, dan dokumentasi mengenai parameter, jenis data, jenis pengembalian, dan kode pengembalian yang menjelaskan berbagai permintaan dan respons antara layanan web dan aplikasi klien.

Dokumentasikan operasi REST untuk web API

Layanan API Management Azure menyertakan portal pengembang yang menjelaskan operasi REST yang diekspos oleh web API. Ketika sebuah produk telah diterbitkan, produk akan muncul di portal ini. Pengembang dapat menggunakan portal ini untuk mendaftar akses; administrator kemudian dapat menyetujui atau menolak permintaan tersebut. Jika pengembang disetujui, mereka diberi kunci langganan yang digunakan untuk mengautentikasi panggilan dari aplikasi klien yang mereka kembangkan. Kunci ini harus diberikan dengan setiap panggilan web API jika tidak maka akan ditolak.

Portal ini juga menyediakan:

  • Dokumentasi untuk produk, daftar operasi yang diekspos, parameter yang diperlukan, dan respons berbeda yang dapat dikembalikan. Perhatikan bahwa informasi ini dihasilkan dari detail yang disediakan di langkah 3 dalam daftar di bagian menerbitkan web API dengan menggunakan bagian Layanan API Management Microsoft Azure.
  • Cuplikan kode yang menunjukkan cara menjalankan operasi dari beberapa bahasa, termasuk JavaScript, C#, Java, Ruby, Python, dan PHP.
  • Konsol pengembang yang memungkinkan pengembang mengirim permintaan HTTP untuk menguji setiap operasi dalam produk dan melihat hasilnya.
  • Halaman tempat pengembang dapat melaporkan masalah atau masalah yang ditemukan.

Portal Microsoft Azure memungkinkan Anda untuk menyesuaikan portal pengembang untuk mengubah gaya dan tata letak agar sesuai dengan merek organisasi Anda.

Menerapkan SDK klien

Membangun aplikasi klien yang memanggil permintaan REST untuk mengakses web API memerlukan penulisan kode dalam jumlah yang signifikan untuk menyusun setiap permintaan dan memformatnya dengan tepat, mengirim permintaan ke server yang menghosting layanan web, dan mengurai respons untuk mengetahui apakah permintaan berhasil atau gagal dan ekstrak data yang dikembalikan. Untuk melindungi aplikasi klien dari masalah ini, Anda dapat menyediakan SDK yang membungkus antarmuka REST dan mengabstraksi detail tingkat rendah ini di dalam kumpulan metode yang lebih fungsional. Aplikasi klien menggunakan metode ini, yang secara transparan mengubah panggilan menjadi permintaan REST dan kemudian mengubah respons kembali menjadi nilai pengembalian metode. Proses ini adalah teknik umum yang diterapkan oleh banyak layanan, termasuk Azure SDK.

Membuat SDK sisi klien adalah tugas yang cukup berat karena harus diterapkan secara konsisten dan diuji dengan hati-hati. Namun, sebagian besar dari proses ini dapat dibuat secara mekanis, dan banyak vendor menyediakan alat yang dapat mengotomatiskan banyak tugas ini.

Memantau web API

Bergantung pada cara Anda memublikasikan dan menerapkan web API, Anda dapat memantau web API secara langsung, atau mengumpulkan informasi penggunaan dan kesehatan dengan menganalisis lalu lintas yang melewati layanan API Management.

Memantau web API secara langsung

Jika Anda telah menerapkan web API Anda dengan menggunakan template ASP.NET Web API (baik sebagai proyek web API atau sebagai peran Web dalam layanan cloud Azure) dan Visual Studio 2013, Anda dapat mengumpulkan ketersediaan, performa, dan data penggunaan dengan menggunakan ASP.NET Application Insights. Application Insights adalah paket yang secara transparan melacak dan mencatat informasi tentang permintaan dan respons saat web API disebarkan ke cloud; setelah paket dipasang dan dikonfigurasi, Anda tidak perlu mengubah kode apa pun di web API Anda untuk menggunakannya. Saat Anda menyebarkan web API ke situs web Azure, semua lalu lintas diperiksa dan statistik berikut dikumpulkan:

  • Waktu respons server.
  • Jumlah permintaan server dan detail setiap permintaan.
  • Permintaan paling lambat teratas dalam hal waktu respons rata-rata.
  • Detail dari setiap permintaan yang gagal.
  • Jumlah sesi yang dimulai oleh browser dan agen pengguna yang berbeda.
  • Halaman yang paling sering dilihat (terutama berguna untuk aplikasi web daripada web API).
  • Peran pengguna yang berbeda mengakses web API.

Anda dapat melihat data ini secara real time di portal Microsoft Azure. Anda juga dapat membuat pengujian web yang memantau kesehatan web API. Pengujian web mengirimkan permintaan berkala ke URI tertentu di web API dan menangkap responsnya. Anda dapat menentukan definisi respons yang berhasil (seperti kode status HTTP 200), dan jika permintaan tidak mengembalikan respons ini, Anda dapat mengatur agar peringatan dikirim ke administrator. Jika perlu, administrator dapat memulai ulang server hosting web API jika permintaan gagal.

Untuk informasi selengkapnya, lihat Application Insights - Memulai dengan ASP.NET.

Memantau web API melalui Layanan API Management

Jika Anda telah menerbitkan web API Anda dengan menggunakan layanan Manajemen API, halaman API Management di portal Microsoft Azure berisi dasbor yang memungkinkan Anda untuk melihat keseluruhan performa layanan. Halaman Analytics memungkinkan Anda menelusuri detail tentang bagaimana produk digunakan. Halaman ini berisi tab berikut:

  • Penggunaan. Tab ini memberikan informasi tentang jumlah panggilan API yang dilakukan dan bandwidth yang digunakan untuk menangani panggilan ini dari waktu ke waktu. Anda dapat memfilter detail penggunaan menurut produk, API, dan operasi.
  • Kesehatan. Tab ini memungkinkan Anda untuk melihat hasil permintaan API (kode status HTTP yang ditampilkan), efektivitas kebijakan cache, waktu respons API, dan waktu respons layanan. Sekali lagi, Anda dapat memfilter data kesehatan berdasarkan produk, API, dan operasi.
  • Aktivitas. Tab ini memberikan ringkasan teks tentang jumlah panggilan yang berhasil, panggilan yang gagal, panggilan yang diblokir, waktu respons rata-rata, dan waktu respons untuk setiap produk, web API, dan operasi. Halaman ini juga mencantumkan jumlah panggilan yang dilakukan oleh setiap pengembang.
  • Sekilas. Tab ini menampilkan ringkasan data performa, termasuk pengembang yang bertanggung jawab untuk membuat sebagian besar panggilan API, dan produk, web API, dan operasi yang menerima panggilan ini.

Anda dapat menggunakan informasi ini untuk menentukan apakah web API atau operasi tertentu menyebabkan penyempitan, dan jika perlu, menskalakan lingkungan host dan menambahkan lebih banyak server. Anda juga dapat memastikan apakah satu atau beberapa aplikasi menggunakan volume sumber daya yang tidak proporsional dan menerapkan kebijakan yang sesuai untuk menetapkan kuota dan membatasi tarif panggilan.

Catatan

Anda dapat mengubah detail untuk produk yang dipublikasikan, dan perubahan akan segera diterapkan. Misalnya, Anda dapat menambahkan atau menghapus operasi dari web API tanpa mengharuskan Anda memublikasikan ulang produk yang berisi web API.

Langkah berikutnya