在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):vasilakisfil/Introspected-REST开源软件地址(OpenSource Url):https://github.com/vasilakisfil/Introspected-REST开源编程语言(OpenSource Language):开源软件介绍(OpenSource Introduction):Introspected REST: An alternative to REST and GraphQLIn this manifesto, we will give a specific definition of what REST is, according to Roy, and see the majority of APIs and API specs (JSONAPI, HAL etc) fail to follow this model. We will see what problems a RESTful API brings and why API designers have been constantly avoiding using it but instead come up with half-way solutions or retreat to alternative models like RPC-over-HTTP or, lately, GraphQL. Then, we will propose a new model, Introspected REST, that solves the issues that REST creates and allows the design of progressively evolvable APIs, in a much simpler way than conventional REST. As part of this manifesto we will also present the concept of MicroTypes, small reusable modules that compose a Media Type and facilitate the evolvability and extensibility of our new model. For the implementation of our new model in HTTP, we will have to go back in time, dig deep in existing RFCs and uncover forgotten concepts, like reactive content negotiation and Media Type parameters, in order to bend the existing Internet infrastructure, which has been mostly influenced by REST concepts, and successfully apply our new model.
1. DefinitionsFirst some definitions, that we will use through the text:
We will use the term APIs and networked APIs interchangeably. 2. IntroductionREST defined by Roy was a magnificent piece of work, much ahead of its time which took us many years to understand what its capabilities are. However, now, almost 20 years later REST model shows its age. It's inflexible, difficult to implement, difficult to test, with performance and implementation issues. But most importantly, any implementation of REST model is very complex. Now, one could say that, most APIs are not build with mind to last for decades and maybe that's the reason that this model hasn't seen much adoption. The former is true but even if the latter is also true could it mean that this model is not suitable for short-term APIs? We firmly believe that REST is much better than any API that does not follow Imagine the following scenario: we have built an Online Social Network and an iOS app that talks to the API on our backend. Now imagine that, after a company meeting, our CEO needs us to make tiny yet important change in the signup page: require the user to fill in her age, a field in the signup form we didn't have before. Essentially, this means, in API terms, add an extra field in the accepted object and require it from the client to be filled in by the user before sending it over. If our API is RESTly and not REST, this means that we need to fix the code in the iOS side, test it and send a new iOS app to Apple store. It takes roughly 1 week for Apple to review our app and if our app doesn't get rejection during the review process for some reason, our tiny change will take action at least a week later after requested. If our API was REST, that would mean a simple change on the server's response, denoting which fields are required to submit the form. We would have the change deployed 10 minutes later. Roy notes in his thesis:
In the world of startups and money-driven companies, the following will sound more appealing:
An API that can change the state of the client without needing the latter to change. Given that, how can we have a simpler model than REST, yet derive the same, if not more, functionality of REST? As we will show, Introspected REST is an API architectural style that solves that. An architectural style is not an implementation and it's not a spec either. As Roy notes:
Introspected REST is based on Roy's initial model but removes the need for runtime HATEOAS. Instead, the client derives the state on demand, using introspection, by retrieving the necessary metadata that are of interest. Eventually this brings the same advantages as Roy's model while being it's much simpler, much more flexible and backwards compatible with any RESTly or RESTless API. But first let's discuss about Networked Services. 3. Networked Services and APIsNowadays JSON has become so popular that developers almost forget that there is whole bunch of protocols below it. Developers also forget that JSON is just a specification in the message level, like XML. It's not the only one and definitely it's not the best we could use. Nevertheless it's simple and simplicity is a virtue. While OSI model is the conceptual model that we use to describe computer networks, with TCP/IP following 5 out of 7 OSI's abstraction layers, in our case, we will make a more API-specific description. When we want to request a resource from a networked hypermedia-based API, we roughly have the following levels: 3.1. Application levelIn the application level, the client starts content negotiation (or content selection), usually asking for only one Media Type. A Media Type provides information about the structure of the content and the message format used in the data it describes, as described by RFC 2046. In the HTTP the content negotiation is achieved by a client using the Accept header which denotes the Media Types that it can understand, in a preference order. Then the server responds with a Media Type proposed by the client in Content-Type header.
Media Types can be a bit more complex as well:
In theory, JSONAPI spec semantics could also be applied using XML as the data format (like in the case of HAL), or even YAML, however in practice we tend to forget that and we treat all Media Types as single and not composite. However, it should also be noted that the Media Types and the content negotiation in general, are not restricted to HTTP only. Although HTTP is one of the most popular application network protocols today, the same logic could be applied in other (mostly text-based) protocols like SIP, CoAP, QUIC etc. To sum up, the application level semantics are defined by the Media Type requested and should not be tightly coupled to the semantics of the message level (like JSON) or the underlying protocol level (like HTTP). 3.2. Message levelIn the message level we find the format that is used for the actual representation. Nowadays we have almost mixed the message level with JSON but in practice other formats could successfully be used: XML, YAML, TOML to name a few. Usually the message format that is used is described by the Media Type's suffix. 3.3. Protocol levelIn the protocol level, the requests are usually sent using the HTTP. After all, nowadays most of the development happens around the Web and HTTP is the only protocol that browsers officially support. Nonetheless, there are other similar stateless protocols as well. QUIC is a HTTP alternative protocol that is targeted for low latency and uses UDP underneath. CoAP is targeted in the IoT and also uses UDP underneath (full TCP/IP stack is quite heavy for constraint devices). SIP is also a text-based protocol with the same semantics as HTTP and is used in VoIP. 3.4. Network levelFinally, in the network level, the browser (or any other non-browser client) sends the networked request in one of the TCP, UDP, etc. The actual protocol depends on the protocol used in the protocol level. 4. Roy's REST modelRoy came up with the REST model in order to solve issues that were arising by the unique properties of networked services during the infancy of Internet. When we develop an application that will be deployed in a networked environment and is expected to be accessed by other networked services, we need to think about its evolvability: if we need to add, remove or change functionality of that application we cannot expect services on the other end that talk with our application to be updated by humans. Such problems that arise from the peculiarities of networks, like discovery and evolvability must be solved using machine-to-machine communication. REST model that solves such issues is an architectural style which is not tied to any spec, protocol or format of the aforementioned levels.
When Roy talks about REST, he mentions 5 crucial properties of 4.1. Access methods have the same semantics for all resources
Failure to provide consistency on access would imply that we don't provide a generic interface but instead we have resource-specific or even object-specific interfaces. Actually a common interface is one of the most crucial parts of REST: without a common uniform interface it would be impossible to derive REST.
Subsequently, the next 4 constraints, the core of REST, is a result of the effort to obtain a uniform interface between different components. 4.2. All important resources are identified by one resource identifier mechanism
Roy explains that very well in his thesis:
4.3. Resources are manipulated through the exchange of representations
The representation that we expose from our public API could be totally different from our implementation internally or how the data are stored in our database. It could also be the same. Nevertheless the client expects and is expected to manipulate any resource using the representation we expose. 4.4. Representations are exchanged via self-descriptive messages
This would mean that the data of the response should follow the Media Type that the client requested and understands. Given that the client negotiated for that Media Type, it should be able to parse and understand any part of the response.
If our Media Type is very weak (like Breaking our Media Type's semantics, or just extending them with new functionality, will have exactly the same result for the client: not self-descriptive messages that will require out-of-band information, like documentation. 4.5. Hypertext as the engine of application state (HATEOAS)
This is one of the most misunderstood parts of Roy's REST model. The idea here is that, once the client and server have reached a consensus on the Media Type after the negotiation, the server's response should strictly provide all the available options for the client to manipulate the resource and navigate to other resources, using semantics defined by the Media Type agreed. As Roy notes:
However, one of the requirements for HATEOAS to work is that the Media Type itself must allow
to its vocabulary hypermedia.
For instance, with Instead, the server and client must agree on a format that provide such mechanisms. Unfortunately though, the common practice is to put 5. API Clients and Applications5.1. Client and Application responsibilitiesClient and Application responsibilities some times are mixed together. A client is responsible for understanding, interacting with the API and manipulating any API's resources, based on the Media Type's semantics and runtime HATEOAS. The client is responsible for providing in the application the list of resources that are available in the API, their fields, their capabilities, available actions and any hypermedia available. The application responsibility on the other hand should not include API specific details. Instead, using the client, it should fetch whatever is needed by the application domain, within the API's capabilities. Think about the traditional home telephone devices. The phone wire and its signals is the API. The device used to encode/decode the wire signals is the API client. On top of a device we can run our application. The PSTN, ISDN, (A)DSL etc are all different Media Types for the same API (wire signals). For each one, we need a client (device/modem) that will understand (encode/decode) the wire signals of that Media Type. Using that client we can built any type of application, in the feasible space of the Media Type. The application does not deal with the API's semantics, but instead it uses the Client to perform its tasks. 5.2. The Human factor principleThere are 2 types of human involvement when building an API client:
An API that follows the REST model should be evolvable without the need of human involvement in the client side, given that the client understands the Media Type. A side effect of such evolvability and 1-fold clients is that the versioning should not take place in the URL but in the Media Type itself.
6. REST applied in a modern APIWhen engineering a REST API, there are 2 approaches:
Specialized APIs could be more efficient, or have crucial advantageous characteristics for the domain that were built for since they are optimized only for that specific case. However, they pose difficulties when they need to be reused by any other application which does not share the same UI. As a result, such APIs are very special and a bit rare. On the other hand, the data-driven APIs, are more generic and facilitate any application to request the data optimized (in the framework of the API's capabilities) for its use case. Being able to specify our application's needs when requesting data from an API is crucial, especially if our business depends on the adoptability of our API. For the following subsections, we will mostly focus in the generic data APIs, however most of the things mentioned here can also be applied in a specialized or UI-driven API. 6.1. Requirements from a modern REST APIREST model is built for machine-to-machine communication, of any type. However, as this form of communication is getting more and more common, clients are expecting more options (capabilities) from the server for their responses. It's not enough to just request and get the resource but we should be able to specify to the server what transformations should apply, according to our needs. Nowadays we have been using networked APIs so much that now we essentially have to provide an ORM to the client over the HTTP (or any other protocol). We provide here a list of features (we call them capabilities) that we think should be built in a modern networked API, in 2017. 6.1.1. Sparse fields (collection/resource)The client should be able to ask and get specific attributes (i.e. a subset) of the resource representation. Also related, we should note that a representation of a resource could have completely different set of attributes for different clients, usually depending on the client's permissions or user role that it represents. 6.1.2. Associations on demand (collection/resource)The client should be able to ask related associations to the main initial resource, in the same request. What differentiates an association from an attribute is that the former has a dedicated identification. What is more, if the API exposes the association as a dedicated resource, the id can be used as identification. 6.1.3. Sorting & pagination (collection only)The client should be able to sort based on one or more attributes and paginate the collection based on the page, page size and possibly an offset. 6.1.4. Filtering collections (collection only)The client should be able to run any sort of collection filtering, as long as it does not pose any security threat or slows down the API performance. 6.1.5. Aggregation queries (collection only)The client should be able to run any sort of aggregation queries, as long as it does not pose any security threat or slows down the API performance. 6.1.6. Data types !The client should know the data types of the attributes of the requested representation of a resource.
Message formats provide some data types but they are pretty basic.
For instance, JSON defines We feel that these 5 data types that JSON provides are just a joke for modern APIs and that we should
have a much larger list of options to select from.
Additionally, we should be able to provide custom types in an easy way, for instance, a field is 6.1.7 Plot twist: this list is endlessAlthough we feel that today these capabilities should exist in any modern API, this list is not exclusive. In fact, there could be capabilities in the future that might not seem necessary today. For example, joining together one or more resources, other database-inspired operations applied on resources, internationalization and localization of the data, HTTP/2 Server Push on some requests, Generic Event Delivery Using HTTP Push on other resources on specific states and other capabilities that we haven't even imagined yet. In any case, these capabilities must be transparent and self-descriptive to the client without any documentation or human involvement, other than programming the client to support the Media Type(s) and pointing it to the initial API URI. 6.2. Media Types vs HATEOASNow the reader could be wondering: where is the appropriate place to describe those capabilities, in our API's Media Type or using HATEOAS ? What goes where? 6.2.1. Defining a new Media Type is not easy and should be avoidedCreating a new Media Type for our API is generally considered bad practice. Create a new Media Type only if you are sure that none of the already published Media Types can fit in your API design. Also, extending an existing Media Type or adding a complementing Media Type to an
existing one (like 6.2.2. HATEOAS can get pretty heavyImagine if we have to describe in a resource, all the available actions along with the available API capabilities in that specific resource. Our API response would just explode in terms of size while making our API super complex. 6.2.3. Balancing between Media Types and HATEOASThe idea is that Media Types describe the generic capabilities while HATEOAS describe the resource-specific capabilities. However we should note that Media Types are not parsed by the client (there was never such intention anyway) which means that the client must be programmed by a human before hand in order to support that Media Type. As a result, the Media Type can't be very restrictive because that would mean it would restrict the API designer's freedom to design the API the way she wants. For instance, for pagination, most RESTy APIs use a On the other hand, if everyone follows that Media Type then it's easier to program our clients. Specifically, especially when having a restrictive Media Type, if we create a client that parses responses using that Media Type then it's easy to "configure" it for another API which also follows the same Media Type. Essentially, HATEOAS should complement the Media Type by providing the Media Type's hypermedia-ed defined semantics in runtime
for the client to work properly.
For instance, HATEOAS could describe on a per-resource basis if the pagination is supported, what is the maximum 6.2.4. An alternative architectureWe feel that the current Media Type specification and use is outdated. If Software Engineering has learned us something, is that composition can enforce Single Responsibility Principle, if used correctly. Inspired by that, later, we will suggest a new concept, MicroTypes, small reusable modules that combined together can form a Media Type. As a result, clients should be able to even negotiate parts of the Media Type and not the Media Type as a whole. Also, instead of mixing up data with HATEOAS in the API responses, we will introduce introspectiveness of our resources. 7. API Specs TodayNow that we defined what REST is, according to Roy, what capabilities modern APIs should support, and where they should provide them, let's see the specs for REST(y) APIs available as today, September |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论