Tautan eksternal (deep linking) - di Internet, ini adalah penempatan hyperlink di situs yang menunjuk ke halaman di situs web lain, alih-alih menunjuk ke halaman home (home, start) dari situs itu. Tautan semacam itu disebut tautan eksternal (tautan dalam).
Wikipedia
Istilah
"tautan dalam" akan digunakan lebih lanjut sebagai yang terdekat dengan "tautan dalam" dalam bahasa Inggris. Artikel ini akan fokus pada REST API, jadi tautan yang dalam berarti tautan ke sumber daya HTTP. Sebagai contoh, tautan mendalam
habr.com/en/post/426691 menunjuk ke artikel tertentu di habr.com.
HATEOAS adalah komponen arsitektur REST yang memungkinkan menyediakan informasi kepada klien API melalui hypermedia. Klien tahu satu-satunya alamat tetap, titik masuk API; ia mempelajari semua tindakan yang mungkin dari sumber daya yang diterima dari server. Tampilan sumber daya berisi tautan ke tindakan atau sumber daya lainnya; klien berinteraksi dengan API, secara dinamis memilih tindakan dari tautan yang tersedia. Anda dapat membaca lebih lanjut tentang HATEOAS di
Wikipedia atau di
artikel hebat
ini tentang HabrΓ©.
HATEOAS adalah level berikutnya dari REST API. Berkat penggunaan hypermedia, ia menjawab banyak pertanyaan yang muncul selama pengembangan API: cara mengontrol akses ke tindakan di sisi server, cara menyingkirkan konektivitas yang ketat antara klien dan server, cara mengubah alamat sumber daya jika perlu. Tapi itu tidak memberikan jawaban atas pertanyaan tentang bagaimana seharusnya tautan mendalam ke sumber daya.
Dalam implementasi REST "klasik", klien mengetahui struktur alamat, ia tahu cara mendapatkan sumber daya dengan pengidentifikasi di REST API. Misalnya, pengguna mengikuti tautan dalam ke halaman buku di toko online. Bilah URL
https://domain.test/books/1
ditampilkan di bilah alamat peramban. Klien tahu bahwa "1" adalah pengidentifikasi sumber daya buku, dan untuk mendapatkannya Anda harus mengganti pengenal ini di URL API REST
https://api.domain.test/api/books/{id}
. Dengan demikian, tautan yang dalam ke sumber daya buku ini di REST API terlihat seperti ini:
https://api.domain.test/api/books/1
.
Di HATEOAS, klien tidak tahu tentang pengidentifikasi sumber daya atau struktur alamat. Dia tidak melakukan hardcode, tetapi tautan "menemukan". Selain itu, struktur URL dapat berubah tanpa sepengetahuan klien, HATEOAS memungkinkannya. Karena perbedaan ini, tautan dalam tidak dapat diimplementasikan dengan cara yang sama dengan API REST klasik. Anehnya, pencarian di Internet untuk resep untuk menerapkan tautan semacam itu di HATEOAS tidak menghasilkan banyak hasil, hanya beberapa pertanyaan membingungkan tentang Stackoverflow. Karena itu, kami akan mempertimbangkan beberapa opsi yang mungkin dan mencoba untuk memilih yang terbaik.
Opsi nol di luar kompetisi bukanlah untuk menerapkan tautan dalam. Ini mungkin cocok untuk beberapa admin atau aplikasi seluler yang tidak memerlukan kemampuan untuk langsung beralih ke sumber daya internal. Ini sepenuhnya dalam semangat HATEOAS, pengguna dapat membuka halaman hanya secara berurutan, mulai dari titik masuk, karena klien tidak tahu cara menuju ke sumber daya internal secara langsung. Tetapi opsi ini tidak cocok untuk aplikasi web - kami berharap bahwa tautan ke halaman internal dapat di-bookmark, dan memperbarui halaman tidak akan memindahkan kami kembali ke halaman utama situs.
Jadi, opsi pertama: hardcode URL HATEOAS API. Klien tahu struktur alamat sumber daya yang memerlukan tautan dalam, dan tahu cara mendapatkan pengidentifikasi sumber daya untuk pencarian. Misalnya, server mengembalikan alamat
https://api.domain.test/api/books/1
sebagai referensi ke sumber daya buku. Klien tahu bahwa "1" adalah pengidentifikasi buku dan dapat menghasilkan URL ini sendiri ketika mengklik tautan yang dalam. Ini tentu saja merupakan opsi yang berfungsi, tetapi melanggar prinsip-prinsip HATEOAS. Struktur alamat dan pengidentifikasi sumber daya tidak lagi dapat diubah, jika tidak klien akan putus, ada koneksi yang kaku. Ini bukan HATEOAS, yang artinya opsi tidak cocok untuk kita.
Opsi kedua adalah mengganti URL API REST di URL klien. Untuk contoh dengan buku, tautan dalam akan terlihat seperti ini:
https://domain.test/books?url=https://api.domain.test/api/books/1
. Di sini, klien mengambil tautan sumber daya yang diterima dari server dan menggantinya sepenuhnya di alamat halaman. Ini lebih seperti HATEOAS, klien tidak tahu tentang pengidentifikasi dan struktur alamat, ia menerima tautan dan menggunakannya sebagaimana adanya. Saat mengklik tautan mendalam seperti itu, klien akan menerima sumber daya yang diinginkan melalui tautan REST API dari parameter url. Tampaknya solusinya bekerja, dan cukup dalam semangat HATEOAS. Tetapi jika Anda menambahkan tautan seperti itu ke bookmark Anda, di masa mendatang kami tidak akan lagi dapat mengubah alamat sumber daya di API (atau kami akan selalu harus mengarahkan ulang ke alamat baru). Sekali lagi, salah satu kelebihan HATEOAS hilang, opsi ini juga tidak ideal.
Jadi, kami ingin memiliki permalink, yang, bagaimanapun, dapat berubah. Solusi semacam itu ada dan banyak digunakan di Internet - banyak situs menyediakan tautan pendek ke halaman internal yang dapat dibagikan. Selain singkatnya, keuntungan mereka adalah bahwa situs dapat mengubah alamat halaman yang sebenarnya, tetapi tautan tersebut tidak akan putus. Sebagai contoh, Microsoft menggunakan tautan Windows untuk membantu halaman dalam bentuk
http://go.microsoft.com/fwlink/?LinkId=XXX
. Selama bertahun-tahun, situs Microsoft telah dirancang ulang beberapa kali, tetapi tautan dalam versi Windows yang lebih lama terus berfungsi.
Tetap hanya mengadaptasi solusi ini ke HATEOAS. Dan ini adalah opsi ketiga - menggunakan pengidentifikasi tautan dalam unik di REST API. Sekarang alamat halaman buku akan terlihat seperti ini:
https://domain.test/books?deepLinkId=3f0fd552-e564-42ed-86b6-a8e3055e2763
. Ketika mengklik tautan yang begitu dalam, klien harus bertanya kepada server: tautan sumber apa yang sesuai dengan pengenal
deepLinkId
? Server akan mengembalikan tautan
https://api.domain.test/api/books/1
(baik, atau segera sumber daya, agar tidak pergi dua kali). Jika alamat sumber daya di REST API berubah, server hanya akan mengembalikan tautan lain. Catatan disimpan dalam database yang pengidentifikasi referensi 3f0fd552-e564-42ed-86b6-a8e3055e2763 sesuai dengan pengidentifikasi entitas buku 1.
Untuk ini, sumber daya harus mengandung bidang
deepLinkId
dengan pengidentifikasi tautan dalam mereka, dan klien harus menggantinya di alamat halaman. Alamat ini dapat ditandai dengan aman dan dikirim ke teman-teman. Tidak terlalu baik bahwa klien bekerja secara independen dengan pengidentifikasi tertentu, tetapi ini memungkinkan Anda untuk mempertahankan keuntungan HATEOAS untuk API secara keseluruhan.
Contoh
Artikel ini tidak akan lengkap tanpa contoh implementasi. Untuk menguji konsep, pertimbangkan contoh situs katalog toko online hipotetis dengan backend pada Spring Boot / Kotlin dan frontend SPA pada Vue / JavaScript. Toko menjual buku dan pensil, situs ini memiliki dua bagian di mana Anda dapat melihat daftar produk dan membuka halaman mereka.
Bagian "Buku":

Satu halaman buku:

Untuk penyimpanan barang, entitas Spring Data JPA didefinisikan:
enum class EntityType { PEN, BOOK } @Entity class Pen(val color: String) { @Id @Column(columnDefinition = "uuid") val id: UUID = UUID.randomUUID() @OneToOne(cascade = [CascadeType.ALL]) val deepLink: DeepLink = DeepLink(EntityType.PEN, id) } @Entity class Book(val name: String) { @Id @Column(columnDefinition = "uuid") val id: UUID = UUID.randomUUID() @OneToOne(cascade = [CascadeType.ALL]) val deepLink: DeepLink = DeepLink(EntityType.BOOK, id) } @Entity class DeepLink( @Enumerated(EnumType.STRING) val entityType: EntityType, @Column(columnDefinition = "uuid") val entityId: UUID ) { @Id @Column(columnDefinition = "uuid") val id: UUID = UUID.randomUUID() }
Untuk membuat dan menyimpan
DeepLink
tautan dalam, entitas
DeepLink
, sebuah instance yang dibuat dengan setiap objek domain. Pengidentifikasi itu sendiri dihasilkan sesuai dengan standar UUID pada saat entitas dibuat. Tabelnya berisi pengidentifikasi tautan dalam, pengidentifikasi, dan jenis entitas yang digunakan tautan itu.
API REST server diatur sesuai dengan konsep HATEOAS, titik masuk API berisi tautan ke koleksi produk, serta tautan
#deepLink
untuk membentuk tautan dalam dengan mengganti pengidentifikasi:
GET http://localhost:8080/api { "_links": { "pens": { "href": "http://localhost:8080/api/pens" }, "books": { "href": "http://localhost:8080/api/books" }, "deepLink": { "href": "http://localhost:8080/api/links/{id}", "templated": true } } }
Klien, ketika membuka bagian "Buku", meminta koleksi sumber daya di tautan
#books
di titik masuk:
GET http://localhost:8080/api/books ... { "name": "Harry Potter", "deepLinkId": "4bda3c65-e5f7-4e9b-a8ec-42d16488276f", "_links": { "self": { "href": "http://localhost:8080/api/books/1272e287-07a5-4ebc-9170-2588b9cf4e20" } } }, { "name": "Cryptonomicon", "deepLinkId": "a23d92c2-0b7f-48d5-88bc-18f45df02345", "_links": { "self": { "href": "http://localhost:8080/api/books/5d04a6d0-5bbc-463e-a951-a9ff8405cc70" } } } ...
SPA menggunakan Vue Router, yang lintasan ke halaman buku didefinisikan
{ path: '/books/:deepLinkId', name: 'book', component: Book, props: true }
, dan tautan dalam daftar buku terlihat seperti ini:
<router-link :to="{name: 'book', params: {link: book._links.self.href, deepLinkId: book.deepLinkId}}">{{ book.name }}</router-link>
.
Yaitu, ketika Anda membuka halaman buku tertentu, komponen
Book
dipanggil, yang menerima dua parameter:
link
(tautan ke sumber daya buku di REST API, nilai bidang
href
dari tautan
#self
) dan
deepLinkId
dari sumber daya itu.
const Book = { template: `<div>{{ 'Book: ' + book.name }}</div>`, props: { link: null, deepLinkId: null }, data() { return { book: { name: "" } } }, mounted() { let url = this.link == null ? '/api/links/' + this.deepLinkId : this.link; fetch(url).then((response) => { return response.json().then((json) => { this.book = json }) }) } }
Vue Router menetapkan nilai
deepLinkId
ke alamat halaman
/books/:deepLinkId
, dan komponen meminta sumber daya dengan tautan langsung dari properti
link
. Saat memaksa refresh halaman, Vue Router menetapkan properti komponen
deepLinkId
, mendapatkannya dari alamat halaman. Properti
link
tetap
null
. Komponen memeriksa: jika ada tautan langsung yang diperoleh dari koleksi, sumber daya diminta di dalamnya. Jika hanya pengidentifikasi
deepLinkId
yang
deepLinkId
, ia diganti ke tautan
#deepLink
dari titik masuk untuk menerima sumber daya melalui tautan dalam.
Di backend, metode pengontrol untuk tautan dalam terlihat seperti ini:
@GetMapping("/links/{id}") fun deepLink(@PathVariable id: UUID?, response: HttpServletResponse?): ResponseEntity<Any> { id!!; response!! val deepLink = deepLinkRepo.getOne(id) val path: String = when (deepLink.entityType) { EntityType.PEN -> linkTo(methodOn(MainController::class.java).getPen(deepLink.entityId)) EntityType.BOOK -> linkTo(methodOn(MainController::class.java).getBook(deepLink.entityId)) }.toUri().path response.sendRedirect(path) return ResponseEntity.notFound().build() }
Dengan pengidentifikasi adalah esensi dari tautan yang dalam. Bergantung pada jenis entitas aplikasi, tautan dibentuk ke metode pengontrol, yang mengembalikan sumber dayanya oleh
entityId
. Permintaan diarahkan ke alamat ini. Dengan demikian, jika di masa depan tautan ke pengontrol entitas berubah, akan sangat mungkin untuk mengubah logika pembentukan tautan dalam metode
deepLink
.
Kode sumber lengkap untuk contoh tersedia di
Github .