Ứng dụng web dưới dạng máy khách API REST: cách xử lý mã định danh tài nguyên


21

Một số khái niệm liên quan đến xung đột REST trong đầu khi tôi thử thực hiện nó.

Tôi có một hệ thống API back-end REST-Ful chứa logic nghiệp vụ và một ứng dụng web cung cấp UI. Từ các tài nguyên khác nhau về REST (đặc biệt, REST trong Thực hành: Hypermedia và Kiến trúc hệ thống ) Tôi biết rằng tôi không nên phơi bày các định danh thô của các thực thể của mình, mà nên trả về các siêu liên kết rel="self".

Hãy xem xét ví dụ. Api REST có tài nguyên trả về một người:

<Person>
  <Links>
    <Link rel="self" href="http://my.rest.api/api/person/1234"/>
  </Links>
  <Pets>
    <Link rel="pet" href="http://my.rest.api/api/pet/678"/>
  </Pets>
</Person>

Vấn đề phát sinh với ứng dụng web. Giả sử nó trả về một trang có chứa một siêu liên kết đến các trình duyệt:

<body class="person">
  <p>
    <a href="http://my.web.app/pet/???????" />
  </p>
</body>

Tôi nên đặt gì vào hrefthuộc tính? Làm cách nào để giữ URL thực thể API trong ứng dụng web để có thể nhận thực thể khi người dùng mở trang đích?

Các yêu cầu có vẻ mâu thuẫn:

  1. Các siêu liên kết hrefsẽ dẫn đến ứng dụng web vì đó là hệ thống lưu trữ giao diện người dùng
  2. Các hrefnên có một số id của thực thể bởi vì các ứng dụng web phải có khả năng giải quyết các thực thể khi trang mục tiêu mở
  3. Ứng dụng web không nên phân tích / xây dựng các URL REST vì nó không phải là REST-Ful, Cuốn sách được đề cập nói

URI phải mờ đục đối với người tiêu dùng. Chỉ người phát hành URI mới biết cách diễn giải nó và ánh xạ nó tới một tài nguyên.

Vì vậy, tôi không thể lấy 1234từ URL phản hồi API vì với tư cách là khách hàng RESTful, tôi nên coi nó như thể nó giống như thế http://my.rest.api/api/AGRIDd~ryPQZ^$RjEL0j. Mặt khác, tôi phải cung cấp một số URL dẫn đến ứng dụng web của mình và đủ để ứng dụng khôi phục URL gốc của API và sử dụng URL đó để truy cập tài nguyên API.

Cách đơn giản nhất có lẽ chỉ là sử dụng URL tài nguyên API làm định danh chuỗi của chúng. Nhưng các trang web như url http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234là xấu xí.

Tất cả có vẻ khá dễ dàng đối với một ứng dụng máy tính để bàn hoặc ứng dụng javascript một trang. Vì chúng sống liên tục, chúng chỉ có thể giữ các URL trong bộ nhớ cùng với các đối tượng dịch vụ trong suốt vòng đời của ứng dụng và sử dụng chúng khi cần thiết.

Với một ứng dụng web tôi có thể tưởng tượng ra một số cách tiếp cận, nhưng tất cả đều có vẻ kỳ lạ:

  1. Thay thế máy chủ lưu trữ trong các URL API và chỉ giữ lại kết quả. Nhược điểm rất lớn là nó yêu cầu ứng dụng web xử lý bất kỳ URL nào mà API tạo ra, nghĩa là khớp nối quái dị. Hơn nữa, nó không phải là RESTful nữa, vì ứng dụng web của tôi bắt đầu diễn giải các URL.
  2. Đưa ra các id thô trong API REST cùng với các liên kết, sử dụng chúng để xây dựng URL của Ứng dụng web và sau đó sử dụng id trên máy chủ ứng dụng web để tìm tài nguyên cần thiết trong API. Điều này tốt hơn, nhưng sẽ ảnh hưởng đến hiệu suất của máy chủ ứng dụng web vì ứng dụng web sẽ phải thông qua điều hướng dịch vụ REST đưa ra một chuỗi các yêu cầu get-by-id dưới dạng nào đó để xử lý bất kỳ yêu cầu nào từ trình duyệt. Đối với một tài nguyên hơi lồng nhau, điều này có thể tốn kém.
  3. Lưu trữ tất cả các selfURL được trả lại bởi api trong ánh xạ liên tục (DB?) Trên máy chủ ứng dụng web. Tạo một số id cho chúng, sử dụng id để xây dựng URL của trang ứng dụng web và để lấy URL của tài nguyên dịch vụ REST. Tức là tôi giữ http://my.rest.api/pet/678URL ở đâu đó bằng một khóa mới, giả sử 3và tạo URL trang web dưới dạng http://my.web.app/pet/3. Điều này trông giống như một triển khai HTTP Cache của một số loại. Tôi không biết tại sao, nhưng nó có vẻ kỳ lạ với tôi.

Hoặc tất cả có nghĩa là API RESTful không thể phục vụ như phụ trợ cho các ứng dụng web?


1
Không rõ những gì bạn đang cố gắng thực hiện, có lẽ vì mục đích đơn giản của bạn được bao phủ bên dưới các lớp kiến ​​trúc mà bạn đang đặt lên nhau, vì vậy thật khó để nói liệu "API RESTful" có thực sự giúp bạn hay không. Từ những gì tôi hiểu về vấn đề của bạn, tùy chọn 2 là một giải pháp đơn giản và khả thi. "Vấn đề" ở đây là vốn có của "API RESTful". RestIsJustSqlReinvented và bạn thực sự sẽ gặp vấn đề tương tự khi bạn cố gắng truy xuất một sơ đồ con đủ phức tạp từ bất kỳ RDBMS nào. Sử dụng bộ đệm hoặc biểu diễn được tối ưu hóa cho các truy vấn của bạn.
back2dos

Câu trả lời:


5

Đã chỉnh sửa để cập nhật câu hỏi, đã xóa câu trả lời trước đó

Nhìn qua những thay đổi của bạn đối với câu hỏi của bạn, tôi nghĩ rằng tôi hiểu vấn đề bạn đang gặp phải hơn một chút. Vì không có trường nào là định danh trên tài nguyên của bạn (chỉ là một liên kết), bạn không có cách nào để tham khảo tài nguyên cụ thể đó trong GUI của bạn (tức là liên kết đến một trang mô tả một thú cưng cụ thể).

Điều đầu tiên để xác định là nếu một con vật cưng có ý nghĩa mà không có chủ. Nếu chúng ta có thể có một con thú cưng mà không có bất kỳ chủ sở hữu nào thì tôi sẽ nói rằng chúng ta cần một số loại tài sản duy nhất trên thú cưng mà chúng ta có thể sử dụng để tham khảo nó. Tôi không tin rằng điều này sẽ vi phạm nếu không để lộ ID trực tiếp vì ID tài nguyên thực tế vẫn sẽ bị giấu trong một liên kết mà máy khách REST sẽ không phân tích. Với ý nghĩ đó, tài nguyên thú cưng của chúng ta có thể trông giống như:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/pets/1" />
    <Link rel="owner" href="http://example.com/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Bây giờ chúng tôi có thể cập nhật tên của thú cưng đó từ Spot thành Fido mà không phải gặp rắc rối với bất kỳ ID tài nguyên thực sự nào trong suốt ứng dụng. Tương tự như vậy, chúng ta có thể tham chiếu đến thú cưng đó trong GUI của mình bằng một cái gì đó như:

http://example.com/GUI/pets/Spot

Nếu thú cưng không có ý nghĩa gì nếu không có chủ (hoặc thú cưng không được phép vào hệ thống mà không có chủ) thì chúng ta có thể sử dụng chủ như một phần của "danh tính" của thú cưng trong hệ thống:

http://example.com/GUI/owners/John/pets/1 (thú cưng đầu tiên trong danh sách cho John)

Một lưu ý nhỏ, nếu cả Thú cưng và Con người có thể tồn tại tách biệt với nhau, tôi sẽ không tạo điểm vào cho API là tài nguyên "Con người". Thay vào đó, tôi sẽ tạo một tài nguyên chung hơn có chứa liên kết đến Người và Thú cưng. Nó có thể trả về một tài nguyên trông giống như:

<Entity type="ResourceList">
    <Link rel="people" href="http://example.com/api/people" />
    <Link rel="pets" href="http://example.com/api/pets" />
</Entity>

Vì vậy, chỉ bằng cách biết điểm nhập cảnh đầu tiên vào API và không xử lý bất kỳ URL nào để tìm ra số nhận dạng hệ thống, chúng tôi có thể làm một cái gì đó như thế này:

Người dùng đăng nhập vào ứng dụng. Máy khách REST truy cập toàn bộ danh sách các tài nguyên con người có sẵn, có thể trông như sau:

<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/1" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/1" />
        <Link rel="pet" href="http://example.com/api/pets/2" />
    </Pets>
    <UniqueName>John</UniqueName>
</Entity>
<Entity type="Person">
    <Link rel="self" href="http://example.com/api/people/2" />
    <Pets>
        <Link rel="pet" href="http://example.com/api/pets/3" />
    </Pets>
    <UniqueName>Jane</UniqueName>
</Entity>

GUI sẽ lặp qua từng tài nguyên và in ra một mục danh sách cho mỗi người bằng cách sử dụng UniqueName làm "id":

<a href="http://example.com/gui/people/1">John</a>
<a href="http://example.com/gui/people/2">Jane</a>

Trong khi làm điều này, nó cũng có thể xử lý từng liên kết mà nó tìm thấy bằng một "vật nuôi" và lấy tài nguyên thú cưng như:

<Entity type="Pet">
    <Link rel="self" href="http://example.com/api/pets/1" />
    <Link rel="owner" href="http://example.com/api/people/1" />
    <UniqueName>Spot</UniqueName>
</Entity>

Sử dụng nó có thể in một liên kết như:

<!-- Assumes that a pet can exist without an owner -->
<a href="http://example.com/gui/pets/Spot">Spot</a>

hoặc là

<!-- Assumes that a pet MUST have an owner -->
<a href="http://example.com/gui/people/John/pets/Spot">Spot</a>

Nếu chúng tôi đi với liên kết đầu tiên và giả sử rằng tài nguyên nhập cảnh của chúng tôi có liên kết với mối quan hệ "vật nuôi" thì luồng điều khiển sẽ đi như thế này trong GUI:

  1. Trang được mở và Spot thú cưng được yêu cầu.
  2. Tải danh sách tài nguyên từ điểm nhập API.
  3. Tải tài nguyên có liên quan đến thuật ngữ "vật nuôi".
  4. Xem qua từng tài nguyên từ phản hồi của "thú cưng" và tìm một tài nguyên phù hợp với Spot.
  5. Hiển thị thông tin tại chỗ.

Sử dụng liên kết thứ hai sẽ là một chuỗi các sự kiện tương tự với ngoại lệ là People là điểm khởi đầu của API và trước tiên chúng tôi sẽ nhận được danh sách tất cả mọi người trong hệ thống, tìm một sự kiện phù hợp, sau đó tìm tất cả thú cưng thuộc về cho người đó (sử dụng lại thẻ rel) và tìm cái được đặt tên là Spot để chúng tôi có thể hiển thị thông tin cụ thể liên quan đến nó.


Cảm ơn, Mike. Tôi đã cập nhật câu hỏi của mình để làm cho nó rõ ràng hơn một chút. Vấn đề với câu trả lời của bạn là tôi không thể đồng ý khách hàng REST có thể phân tích cú pháp URL. Nếu có, nó sẽ được ghép với các URL. Và điều này vi phạm một trong những ý tưởng cốt lõi của REST: khách hàng nên sử dụng rels để chọn các liên kết, nhưng không nên thừa nhận bất kỳ kiến ​​thức nào về cấu trúc của URL. REST khẳng định API miễn phí thay đổi URL theo ý muốn với điều kiện rellà vẫn giữ nguyên. Các URL phân tích cú pháp giúp chúng ta gần gũi hơn với SOAP hơn là REST.
Pavel Gatilov

Cám ơn bạn một lần nữa. Bạn đã mô tả cách tiếp cận chúng tôi đã thực hiện cho đến nay. Theo một cách nào đó, chúng tôi làm lộ định danh. Điều duy nhất là chúng tôi cố gắng để lộ các định danh tự nhiên bất cứ khi nào có thể.
Pavel Gatilov

6

Có phải tất cả có nghĩa là API RESTful không thể phục vụ như phụ trợ cho các ứng dụng web?

Tôi thách thức liệu có đáng để phân biệt giữa API REST và ứng dụng web hay không. "Ứng dụng web" của bạn chỉ nên là các đại diện thay thế (HTML) của cùng một tài nguyên - có nghĩa là, tôi không hiểu cách thức hoặc lý do bạn mong đợi truy cập http://my.rest.api/...http://my.web.app/...chúng đồng thời giống nhau và khác nhau.

"Khách hàng" của bạn là trình duyệt trong trường hợp này và nó hiểu HTML và JavaScript. Đó ứng dụng web theo ý kiến ​​của tôi. Bây giờ bạn có thể không đồng ý và nghĩ rằng bạn truy cập ứng dụng web đã nói bằng foo.com và hiển thị mọi thứ khác qua api.foo.com - nhưng sau đó bạn phải hỏi, làm thế nào foo.com cung cấp cho tôi đại diện của tài nguyên? "Back-end" của foo.com hoàn toàn có khả năng hiểu cách khám phá các tài nguyên từ api.foo.com. Ứng dụng web của bạn chỉ đơn thuần trở thành một proxy - không khác gì so với việc bạn đang nói chuyện với một API khác (từ người khác) cùng nhau.

Vì vậy, câu hỏi của bạn có thể được khái quát thành "Làm thế nào tôi có thể mô tả tài nguyên bằng cách sử dụng các URI của riêng tôi tồn tại trong các hệ thống khác?" thật là tầm thường khi bạn cho rằng đó không phải là máy khách (HTML / JavaScript) phải hiểu cách thực hiện việc này mà là máy chủ. Nếu bạn đồng ý với thử thách đầu tiên của tôi, thì bạn có thể nghĩ đơn giản về ứng dụng web của mình như một API REST riêng biệt ủy nhiệm hoặc ủy quyền cho một API REST khác.

Vì vậy, khi khách hàng của bạn truy cập, my.web.app/pets/1nó biết thể hiện giao diện thú cưng bởi vì đó là những gì được trả về bởi mẫu phía máy chủ hoặc nếu đó là một yêu cầu không đồng bộ cho một số đại diện khác (ví dụ JSON hoặc XML), tiêu đề kiểu nội dung sẽ cho nó biết .

Máy chủ cung cấp đây là máy chủ chịu trách nhiệm tìm hiểu thú cưng là gì và cách phát hiện thú cưng trên hệ thống từ xa. Cách bạn thực hiện việc này tùy thuộc vào bạn - bạn chỉ cần lấy ID và tạo một URI khác, đó là điều bạn cảm thấy không phù hợp hoặc bạn có thể có cơ sở dữ liệu của riêng mình lưu trữ URI từ xa và ủy quyền yêu cầu. Lưu trữ URI này là tốt - nó tương đương với đánh dấu trang. Bạn sẽ làm tất cả những điều này chỉ để có một tên miền riêng. Tôi thực sự không biết tại sao bạn muốn điều này - URI API REST của bạn cũng phải có khả năng đánh dấu trang.

Bạn đã đưa ra hầu hết những điều này trong câu hỏi của bạn, nhưng tôi cảm thấy bạn đã đóng khung nó theo cách thực sự không thừa nhận rằng đó là cách thực tế để làm những gì bạn muốn làm (dựa trên những gì tôi cảm thấy là một ràng buộc tùy ý - rằng API và ứng dụng tách biệt). Bằng cách hỏi xem API REST có thể là back-end cho các ứng dụng web hay không và cho rằng hiệu suất sẽ là một vấn đề, tôi nghĩ rằng bạn đang tập trung vào tất cả những thứ sai. Nó giống như nói rằng bạn không thể tạo ra một Mashup. Nó giống như nói rằng web không hoạt động.


Tôi không mong đợi ứng dụng web chỉ đơn giản là một đại diện cho api. Nó có thể có nhiều sự khác biệt, ví dụ: hiển thị một số tài nguyên con cùng với một số tài nguyên gốc trên một trang. Tôi không muốn các url ứng dụng web chứa id bên trong của bộ lưu trữ dữ liệu api, nếu bạn muốn nói điều này bằng cách nói rằng tôi hy vọng 2 hệ thống giống nhau. Tôi không quan tâm đến hiệu suất ở đây, nó không phải là vấn đề. Câu hỏi thực sự là 'Làm cách nào để đặt 3 vào my.web.app/pets/3mà không phân tích cú pháp API REST'?
Pavel Gatilov

Sửa lỗi cụm từ của riêng tôi: 'Làm cách nào để đặt 3 vào my.web.app/pets/3mà không phân tích URL của tài nguyên API REST tương ứng my.rest.api/v0/persons/2/pets/3? Hay tôi để gì ở đó? '
Pavel Gatilov

Tôi nghĩ rằng bạn đang nhầm lẫn trạng thái của khách hàng với các đại diện xác định trạng thái đó. Bạn không đặt 3vào app/pets/3app/pets/3mờ đục, nó trỏ đến bất kỳ tài nguyên nào mà ứng dụng web của bạn muốn. Nếu đó là chế độ xem tổng hợp của một số tài nguyên khác (trong các hệ thống khác - API của bạn là một trong số đó) thì bạn có thể lưu trữ các siêu liên kết đến các hệ thống đó trong máy chủ ứng dụng web và sau đó truy xuất chúng, giải quyết chúng cho các đại diện của chúng ( ví dụ JSON hoặc XML) và sau đó phân phát chúng như một phần phản hồi của bạn.
Doug

Hãy nghĩ về nó theo cách này - quên API và ứng dụng của bạn. Giả sử bạn muốn tạo một trang web cho phép mọi người thu thập các bài đăng trên Facebook và Twitter yêu thích của họ. Đó là những hệ thống từ xa. Bạn sẽ không cố gắng tạo đường hầm hoặc tạo mẫu URI cho các hệ thống đó thông qua hệ thống của riêng bạn. Bạn sẽ tạo tài nguyên 'bảng' và máy chủ của bạn sẽ biết rằng nó board/1trỏ đến facebook.com/post/123twitter.com/status/789- khi bạn cung cấp một đại diện cho bảng của mình, bạn sẽ phải giải quyết các URI đó thành một đại diện mà bạn có thể làm việc. Cache khi cần thiết.
Doug

Và vì vậy, vì bạn muốn API của bạn khác biệt đáng kể so với ứng dụng của bạn (tôi vẫn nghĩ rằng điều này có thể nghi ngờ) - đối xử với nó như một hệ thống từ xa trở nên không khác gì. Bạn nói rằng hiệu suất không phải là vấn đề, nhưng bạn cũng đã nói trong câu hỏi của mình rằng một cái gì đó như thế này sẽ 'ảnh hưởng đến hiệu suất'.
Doug

5

Lời nói đầu

Câu trả lời này đặc biệt giải quyết câu hỏi về cách quản lý lược đồ URL của riêng bạn, bao gồm các URL có thể đánh dấu duy nhất cho các tài nguyên mà API REST phía sau không hiển thị rõ ràng một mã định danh và không diễn giải các URL do API cung cấp.


Khả năng khám phá đòi hỏi một lượng kiến ​​thức nhất định, vì vậy đây là cách tôi thực hiện trong một kịch bản trong thế giới thực:

Giả sử chúng tôi muốn có một trang tìm kiếm trong http://my.web.app/personđó kết quả bao gồm một liên kết đến trang chi tiết cho mỗi người. Một điều mà mã mặt trước của chúng tôi phải biết để làm bất cứ điều gì là URL cơ sở cho nguồn dữ liệu REST của nó : http://my.rest.api/api. Phản hồi cho yêu cầu NHẬN URL này có thể là:

<Links>
    <Link ref="self" href="http://my.rest.api/api" />
    <Link rel="person" href="http://my.rest.api/api/person" />
    <Link rel="pet" href="http://my.rest.api/api/pet" />
</Links>

Vì mục đích của chúng tôi là hiển thị danh sách những người, tiếp theo chúng tôi sẽ gửi GETyêu cầu tới href từ personlink href, có thể trả về:

<Links>
    <Link ref="self" href="http://my.rest.api/api/person" />
    <Link rel="search" href="http://my.rest.api/api/person/search" />
</Links>

Chúng tôi muốn hiển thị kết quả tìm kiếm, vì vậy chúng tôi sẽ sử dụng dịch vụ tìm kiếm bằng cách gửi GETyêu cầu đến searchlink href, có thể trả về:

<Persons>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/1"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/10"/>
        </Pets>
    </Person>
    <Person>
        <Links>
            <Link rel="self" href="http://my.rest.api/api/person/2"/>
        </Links>
        <Pets>
            <Link rel="pet" href="http://my.rest.api/api/pet/20"/>
        </Pets>
    </Person>
</Persons>

Cuối cùng chúng tôi cũng có kết quả, nhưng làm cách nào để xây dựng các URL mặt trước của chúng tôi?

Chúng ta hãy loại bỏ phần mà chúng ta biết chắc chắn: URL cơ sở API và sử dụng phần còn lại làm định danh giao diện người dùng của chúng tôi:

  • cơ sở API đã biết: http://my.rest.api/api
  • URL đã cho cho thực thể cá nhân: http://my.rest.api/api/person/1
  • id duy nhất: /person/1
  • URL cơ sở của chúng tôi: http://my.web.app
  • URL mặt trước được tạo của chúng tôi: http://my.web.app/person/1

Kết quả của chúng tôi có thể trông như sau:

<ul>
    <li><a href="http://my.web.app/person/1">A person</a></li>
    <li><a href="http://my.web.app/person/2">A person</a></li>
</ul>

Khi người dùng theo liên kết mặt trước đó đến trang chi tiết, chúng tôi sẽ gửi GETyêu cầu chi tiết cụ thể về URL nào person? Chúng tôi biết phương pháp của chúng tôi để ánh xạ các URL phía sau thành các URL phía trước, vì vậy chúng tôi chỉ cần đảo ngược nó:

  • URL mặt trước: http://my.web.app/person/1
  • URL cơ sở của chúng tôi: http://my.web.app
  • id duy nhất: /person/1
  • cơ sở API đã biết: http://my.rest.api/api
  • URL API được tạo: http://my.rest.api/api/person/1

Nếu API REST thay đổi sao cho personURL hiện tại http://my.rest.api/api/different-person-base/person/1và ai đó đã được đánh dấu trước đó http://my.web.app/person/1, API REST sẽ (ít nhất là trong một thời gian) cung cấp khả năng tương thích ngược bằng cách phản hồi URL cũ bằng chuyển hướng đến URL mới. Tất cả các liên kết đầu cuối được tạo sẽ bao gồm cấu trúc mới tự động.

Như bạn có thể nhận thấy, có một số điều chúng ta phải biết để điều hướng API:

  • URL cơ sở API
  • các personmối quan hệ
  • các searchmối quan hệ

Tôi không nghĩ có gì sai với điều này; chúng tôi không giả định cấu trúc URL cụ thể tại bất kỳ thời điểm nào, vì vậy cấu trúc của URL thực http://my.rest.api/api/person/1thể có thể thay đổi và miễn là API cung cấp khả năng tương thích ngược, mã của chúng tôi vẫn sẽ hoạt động.


Bạn đã hỏi làm thế nào logic định tuyến của chúng tôi có thể cho biết sự khác biệt giữa hai URL giao diện người dùng:

  • http://my.rest.api/api/person/1
  • http://my.rest.api/api/pet/3.

Trước tiên tôi sẽ chỉ ra rằng bạn đã sử dụng cơ sở API trong nhận xét của mình khi trong ví dụ của chúng tôi, chúng tôi đang sử dụng các URL cơ sở riêng cho API UI và REST. Tôi sẽ tiếp tục ví dụ sử dụng các cơ sở riêng biệt, nhưng chia sẻ một cơ sở không phải là vấn đề. Chúng tôi có thể (hoặc có thể) ánh xạ các phương thức định tuyến UI bằng cách sử dụng loại phương tiện từ tiêu đề Chấp nhận của yêu cầu.

Đối với việc định tuyến đến một trang chi tiết cụ thể, chúng tôi không thể phân biệt hai URL đó nếu chúng tôi nghiêm ngặt về việc tránh mọi kiến ​​thức về cấu trúc của selfURL được cung cấp bởi API (ví dụ: id chuỗi mờ). Để thực hiện công việc này, hãy bao gồm một phần thông tin đã biết khác của chúng tôi - loại thực thể mà chúng tôi đang làm việc - trong các URL giao diện người dùng của chúng tôi.

Các URL giao diện người dùng trước đây của chúng tôi có định dạng: ${UI base}/${opaque string id}

Định dạng mới có thể là: ${UI base}/${entity type}/${opaque string id}

Vì vậy, bằng cách sử dụng /person/1ví dụ, chúng tôi sẽ kết thúc với http://my.web.app/person/person/1.

Với định dạng này, logic định tuyến giao diện người dùng của chúng tôi sẽ hoạt động /person/person/1và biết rằng mã thông báo đầu tiên trong chuỗi được chúng tôi chèn vào, chúng tôi có thể kéo nó ra và định tuyến đến trang chi tiết thích hợp (người, trong ví dụ này) dựa trên nó. Nếu bạn cảm thấy ngượng ngùng về URL đó, vì vậy chúng tôi có thể chèn thêm một chút vào đó; có lẽ: http://my.web.app/person/detail/person/1

Trong trường hợp đó, chúng tôi sẽ phân tích cú pháp /person/detailđể định tuyến và sử dụng phần còn lại làm id chuỗi mờ.


Tôi nghĩ rằng điều này giới thiệu sự kết hợp cực kỳ chặt chẽ của ứng dụng web với api.

Tôi đoán bạn có nghĩa là, vì URL mặt trước được tạo của chúng tôi chứa một phần của URL API, nếu cấu trúc URL API thay đổi mà không hỗ trợ cấu trúc cũ, chúng tôi sẽ cần thay đổi mã để dịch URL được đánh dấu vào phiên bản mới của URL API. Nói cách khác, nếu API REST thay đổi ID của tài nguyên (chuỗi mờ), thì chúng ta không thể nói chuyện với máy chủ về tài nguyên đó bằng ID cũ. Tôi không nghĩ rằng chúng ta có thể tránh được sự thay đổi mã trong tình huống đó.

Điều gì xảy ra nếu tôi muốn cấu trúc URL cho ứng dụng web khác với cấu trúc của api?

Bạn có thể sử dụng bất kỳ cấu trúc URL nào bạn muốn. Vào cuối ngày, một URL có thể đánh dấu cho một tài nguyên cụ thể phải bao gồm một cái gì đó mà bạn có thể sử dụng để có được một URL API xác định duy nhất tài nguyên đó. Nếu bạn tạo số nhận dạng của riêng mình và lưu bộ đệm đó bằng URL API như trong cách tiếp cận số 3 của bạn, điều đó sẽ hoạt động cho đến khi ai đó cố gắng sử dụng URL được đánh dấu đó sau khi mục đó bị xóa khỏi bộ đệm.

Điều gì xảy ra nếu các thực thể của ứng dụng web của tôi không ánh xạ tới các thực thể api 1-1?

Câu trả lời phụ thuộc vào mối quan hệ. Dù bằng cách nào, bạn sẽ cần một cách để ánh xạ các giao diện người dùng vào URL API.


Tôi có một vấn đề với cách tiếp cận này. Đó thực sự là số 1 trong danh sách các giải pháp của tôi. Điều tôi không nhận được là đây: nếu ứng dụng web không diễn giải URL và coi id duy nhất là chuỗi mờ (chỉ person/1, pet/3), thì làm sao biết rằng nếu trình duyệt mở http://my.rest.api/api/person/1thì nó sẽ hiển thị UI cá nhân và nếu nó Mở http://my.rest.api/api/pet/3, rồi UI thú cưng?
Pavel Gatilov

Câu hỏi hay! Tôi đã cập nhật câu trả lời với câu trả lời của tôi.
Mike Partridge

Cảm ơn, Mike. Tôi nghĩ rằng điều này giới thiệu sự kết hợp cực kỳ chặt chẽ của ứng dụng web với api. Điều gì xảy ra nếu tôi muốn cấu trúc URL cho ứng dụng web khác với cấu trúc của api? Điều gì xảy ra nếu các thực thể của ứng dụng web của tôi không ánh xạ tới các thực thể api 1-1? Tôi vẫn nghĩ rằng tốt hơn tôi nên sử dụng cách tiếp cận để lộ một số định danh, nhưng thúc giục khách hàng sử dụng các liên kết để điều hướng.
Pavel Gatilov

Đây là một chủ đề thú vị, vì vậy tôi hy vọng tôi không bỏ sót điều gì. Tôi đã cập nhật câu trả lời của mình với câu trả lời cho nhận xét của bạn. Tôi nghĩ việc phơi bày một số định danh là một sự thỏa hiệp tốt giữa tính đầy đủ và khả năng sử dụng.
Mike Partridge

Mối quan tâm chính của tôi ở đây là một chút thực tế hơn. Tôi sử dụng ASP.NET MVC để triển khai ứng dụng web và do một số quy tắc nội bộ, tôi phải xác định các mẫu url mà ứng dụng hỗ trợ. Tức là nếu / a / {id} được xác định, thì ứng dụng sẽ xử lý / a / 1, nhưng không phải / a / 1 / b / 2. Điều này dẫn đến yêu cầu biên dịch lại ứng dụng web nếu các url API REST thay đổi không chỉ để bảo toàn các URL được đánh dấu mà còn đơn giản là làm cho ứng dụng web hoạt động khi được điều hướng từ thư mục gốc. Đơn giản là vì các siêu liên kết được nhúng vào các trang html sẽ không hoạt động mà không có điều đó.
Pavel Gatilov

2

Hãy đối mặt với nó, không có giải pháp kỳ diệu. Bạn đã đọc mô hình trưởng thành của Richardson chưa? Nó chia sự trưởng thành của kiến ​​trúc REST thành 3 cấp độ: Tài nguyên, động từ HTTP và điều khiển hypermedia.

Tôi không nên để lộ các định danh thô của các thực thể của mình mà nên trả về các siêu liên kết với rel = "self"

Đây là điều khiển hypermedia. Bạn có thực sự cần nó? Cách tiếp cận này có một số lợi ích rất tốt (bạn có thể đọc về chúng ở đây ). Nhưng không có thứ gọi là bữa ăn miễn phí và bạn sẽ phải làm việc chăm chỉ (ví dụ giải pháp thứ hai của bạn) nếu bạn muốn có được chúng.

Đó là một câu hỏi về sự cân bằng - bạn có muốn hy sinh hiệu năng (và làm cho mã của bạn phức tạp hơn) nhưng có được một hệ thống linh hoạt hơn không? Hoặc bạn thích giữ mọi thứ nhanh hơn và đơn giản hơn nhưng trả tiền sau khi bạn giới thiệu các thay đổi cho api / model của mình?

Là một người đã phát triển một hệ thống tương tự (tầng logic nghiệp vụ, tầng web và máy khách web), tôi đã chọn tùy chọn thứ hai. Vì nhóm của tôi đã phát triển tất cả các tầng, chúng tôi quyết định tốt hơn là nên có một chút khớp nối (bằng cách cho tầng web biết về id thực thể và xây dựng các url api) và đổi lại lấy mã đơn giản hơn. Khả năng tương thích ngược cũng không liên quan trong trường hợp của chúng tôi.

Nếu ứng dụng web được phát triển bởi bên thứ 3 hoặc nếu khả năng tương thích ngược là vấn đề, chúng tôi có thể đã chọn khác vì có thể thay đổi cấu trúc url mà không thay đổi ứng dụng web. Đủ để biện minh cho việc làm phức tạp mã.

Có phải tất cả có nghĩa là API RESTful không thể phục vụ như phụ trợ cho các ứng dụng web?

Tôi nghĩ điều đó có nghĩa là bạn không phải tạo ra một triển khai REST hoàn hảo. Bạn có thể đi với giải pháp thứ hai của mình hoặc để lộ id thực thể hoặc có thể vượt qua các api api . Không sao, miễn là bạn hiểu được ý nghĩa và sự đánh đổi.


0

Tôi điều nếu bạn dính vào một cái gì đó tương tự như Atom Syndication Formatbạn là tốt.

Ở đây siêu dữ liệu mô tả Mục nhập được đại diện có thể được chỉ định bằng cách sử dụng các yếu tố / thuộc tính bổ sung:

  • Theo [RFC4287] , chứa một URI xác định duy nhất Mục nhập

  • Theo [RFC4287] , yếu tố này là tùy chọn. Nếu được bao gồm, nó chứa URI mà khách hàng nên sử dụng để truy xuất Entry.

Đây chỉ là hai xu của tôi.


Có thể tôi không nhận được một cái gì đó, nhưng dường như với tôi rằng câu trả lời của bạn không giải thích cách tạo URL của ứng dụng web là ứng dụng khách cho API REST, phải không?
Pavel Gatilov

0

Đừng lo lắng về các URL, lo lắng về các loại phương tiện truyền thông.

Xem ở đây (điểm đạn thứ ba nói riêng).

API REST nên dành hầu hết tất cả nỗ lực mô tả của nó để xác định (các) loại phương tiện được sử dụng để thể hiện tài nguyên và trạng thái ứng dụng lái xe hoặc trong việc xác định tên quan hệ mở rộng và / hoặc đánh dấu siêu văn bản cho các loại phương tiện tiêu chuẩn hiện có .. .


Trong trường hợp của một ứng dụng web điển hình, khách hàng là một con người ; trình duyệt chỉ là một tác nhân .

Vì vậy, một thẻ neo như

          <a href="example.com/foo/123">click here</a>

tương ứng với một cái gì đó như

          <link type="text/html" rel="self" href="example.com/foo/123">

URL vẫn mờ đục đối với người dùng, tất cả những gì cô ấy quan tâm là các loại phương tiện (ví dụ text/html, application/pdf, application/flv, video/x-flv, image/jpeg, image/funny-cat-picture etc). Văn bản mô tả có trong neo (và trong thuộc tính tiêu đề) chỉ là một cách mở rộng loại mối quan hệ theo cách dễ hiểu đối với con người.

Lý do bạn muốn URI mờ đục đối với các máy khách là để bạn giảm khớp nối (một trong những mục tiêu chính của REST). Máy chủ có thể thay đổi / sắp xếp lại các URI mà không ảnh hưởng đến máy khách (miễn là bạn có chính sách lưu trữ tốt - điều đó có nghĩa là không có bộ đệm nào cả).

Tóm tắt

Chỉ cần đảm bảo khách hàng (người hoặc máy) quan tâm đến các loại phương tiện và các mối quan hệ thay vì các URL và bạn sẽ ổn.


Rodrick, câu hỏi của tôi không phải là về việc xây dựng API, mà là về việc xây dựng một ứng dụng web nằm trên API RESTful. Tôi khó có thể hiểu làm thế nào các loại phương tiện có thể giúp tôi xây dựng URL cho ứng dụng web. Mặc dù các loại phương tiện truyền thông rất quan trọng cho hợp đồng dịch vụ và khả năng khám phá.
Pavel Gatilov

@PavelGatilov - Khách hàng của ứng dụng web của bạn có phải là con người không?
Rodrick Chapman

Vâng, nó là. Và một cái rất thiếu
Pavel Gatilov

0

Cách đơn giản nhất có lẽ chỉ là sử dụng URL tài nguyên API làm định danh chuỗi của chúng. Nhưng các url của trang web như http://my.web.app/person/http%3A%2F%2Fmy.rest.api%2Fapi%2Fperson%2F1234 là xấu xí.

Tôi nghĩ bạn đã đúng, đó là cách đơn giản nhất. Bạn có thể tương đối hóa các URL http://my.rest.api/apiđể làm cho chúng bớt xấu đi:

http://my.web.app/person/person%2F1234

Nếu URL được cung cấp bởi API không liên quan đến cơ sở đó, thì URL sẽ biến thành dạng xấu xí:

http://my.web.app/person/http%3A%2F%2Fother.api.host%2Fapi%2Fperson%2F1234

Để tiến thêm một bước, hãy kiểm tra phản hồi từ máy chủ API để xác định loại chế độ xem bạn muốn trình bày và dừng mã hóa dấu phân cách và dấu hai chấm của đường dẫn:

http://my.web.app/person/1234 (best case)
http://my.web.app/http://other.api.host/api/person/1234 (ugly case)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.