mikekelly / hal_specification

HAL Specification
http://stateless.co/hal_specification.html
618 stars 58 forks source link

Should _links only contain links with GET method ? #24

Open redben opened 9 years ago

redben commented 9 years ago

Say an api exposes blog posts and with each blog post, one would want to include a link to post comments, should that links be in _links ? or should there be some other property like _actions ?

Maks3w commented 9 years ago

You have the HTTP method OPTIONS for query about available actions.

redben commented 9 years ago

Sometimes it is not feasible to do yet another query to get options, you'd want to include available actions in the entity:

{
    _links: {
        self: {
            href: "https://blog/posts/example-1" 
        },
        comments: {
            href: "https://blog/posts/example-1/comments",
        },
        comment: {
            href: "https://blog/comments?post=example-1",
            method: "POST"
        }
    }

}

or

{
    _links: {
        self: {
            href: "https://blog/posts/example-1" 
        },
        comments: {
            href: "https://blog/posts/example-1/comments",
        },
    },
    _actions: {
        comment: {
            href: "https://blog/comments?post=example-1",
            method: "POST"
        }
    }

}
mikekelly commented 9 years ago

You can just use a normal link and document the relation to specify what methods are available to perform possible actions. On 17 Dec 2014 12:06, "Reda" notifications@github.com wrote:

Say an api exposes blog posts and with each blog post, one would want to include a link to post comments, should that links be in _links ? or should there be some other property like _actions ?

— Reply to this email directly or view it on GitHub https://github.com/mikekelly/hal_specification/issues/24.

vchrm commented 9 years ago

Hi, I found this thread because I was actually thinking in the same direction as @redben was.

To explain myself, please consider having this link as mentioned in examples:

{
    "_links": {
        "next": { "href": "/page=2" }
    }
}

I suppose the server is going to omit the "next" link once there is no next page that can be followed with the link. Using this analogy I can think of resources that are available using GET method only (as well as resources available using POST only -- for example as mentioned in http://stackoverflow.com/questions/16877968/call-a-server-side-method-on-a-resource-in-a-restful-way#answer-16878494). Unfortunately, I won't be able to tell this from hal response itself.

As @Maks3w wrote, we are able to find out what methods are supported by the resource using OPTIONS method, however you'd need to make OPTIONS request to server for every given link to retrieve this information.

In HAL I love the mechanism that using links the API tells me that there still is other "next" resource available. However, I have to say I miss similar mechanism for the linked resources to be able to list methods that are available. Without this, Hal browser is going to offer invalid actions (methods) on resources (and is going to get 404's or 400's from server needlessly). In my opinion, the information "what you can or cannot do with the linked resource" could be encapsulated in _link itself. Otherwise, if I wanted to be precise and not do anything bad with the api, I'd have to rely on making OPTIONS request on every provided _link.

In REST api there is pretty much clear what each HTTP method is going to do to the resource. Using dog's barking example from the StackOverflow link above, I think that simple listing of available methods would be sufficient. So at the end I'd love to retrieve something like this from server:

{
    "_links": {
        "barks": { "href": "/v1/dogs/1/barks", "methods": ["POST"] }
    }
}

Of course I am aware that my servers can return whatever I want but I see this optional "methods" property as something that is missing in HAL specification itself :-).

I hope I made my motives clear. If I'm misreading something, please do correct me where my mindset is wrong and why all of this is bad idea.

Thank you!

mikekelly commented 9 years ago

The "HAL way" of doing this is to use the link rel documentation to convey the available methods in a human readable form. ie. your barks example would look like this (notice the "barks" link rel is now a URL)

{
    "_links": {
        "http://docs.example.com/barks": { "href": "/v1/dogs/1/barks" }
    }
}

and if a developer fetches the URL http://docs.example.com/barks in a browser, the documentation can specify the available methods, valid request bodies, potential response codes, response bodies, etc. The developer will then codify this into the client she is building.

redben commented 9 years ago

What if the available methods are variable, for example POST would be available for an open discussion (imagine this is the type of resource) but not for a closed one.

I think @vchrm 's suggestion, the methods field, is great.

mikekelly commented 9 years ago

@redben this breaks down if you have variability in behaviour beyond the method (eg. admins can still POST administrative stuff to a closed discussion). In my experience, it's easier to convey these types of conditional transition through rules attached to the potential state of a resource ie. documentation for a link that says "when the discussion is 'open' (has no closed_at property), you can POST stuff"

redben commented 9 years ago

@mikekelly I see what you mean. Conveying these rules in the state of the resource or making it explicit tends to be more of an developer (consumer) experience (DX, gush someone already coined this buzz term). The options are :

  1. Rules in state as you suggest: simple, if simple and small set of rules that don't change often.
  2. OPTIONS / Allowed Methods as suggested by @Maks3w: better for complexe rules. If not allowed then it's not allowed. Reasons can be set as the body of the 405 Method Not Allowed Response. Cons: one more round trip (if that's a concern).
  3. Allowed methods (in the link) in the state as suggested by @vchrm:. Best of both. No round trip (like 1), flexibility and tolerance to change of rules (like 2)
lazee commented 8 years ago

@redben I think this already is possible with use of _embedded.

From the spec: "Embedded Resources MAY be a full, partial, or inconsistent version of the representation served from the target URI."

By taking advantage of the "inconsistency" part of that statement we could end up with something like

{
    "_links": {
        "self": {
            "href": "https://blog/posts/example-1" 
        },
        "comments": {
            "href": "https://blog/posts/example-1/comments",
        },
    },
    "_embedded": {
        "comments": {
            "_meta" : {
                "examples": { "https://blog/comments?post=example-1" },
                "method": "POST"
            }
        }
    }
}

_meta is just a container to prevent conflicts with other attributes you want to embed.

Alternatively you you can do it something like this to be able to provide examples for each method.

{
    "_links": {
        "self": {
            "href": "https://blog/posts/example-1" 
        },
        "comments": {
            "href": "https://blog/posts/example-1/comments",
        },
    },
    "_embedded": {
        "comments": {
            "_meta" : {
                "_methods" : {
                    "POST": {
                        "examples": { "https://blog/comments?post=example-1" }
                    }
                }
            }
        }
    }
}

This approach is what we are considering right now. Feedback are more than welcome!

ivan-gammel commented 7 years ago

Not sure, if this discussion is still open, but I have some experience of implementing APIs based on latest published draft of HAL spec at this moment and I think this issue is still valid. Here's my story: API that I implement serves both our own frontend web application and 3rd party clients. There's rather complex access control and there are performance considerations.

Since there's no "method" specification in resource link currently and we'd like to keep close to the proposed standard (we prefer HAL to other existing specs for a number of reasons), we apply almost all workarounds possible:

  1. Documentation: is ok for some cases, but even if client knows, which operations are potentially available, it still needs to discover the current status of resource in runtime based on user credentials. Also, we cannot rely on some resource properties for that, because this will mean, that we'll need to implement access decision logic twice - both on client and on server. We already have a mechanism for server to tell, whether a resource is accessible and that should be enough.

  2. OPTIONS method: requires series of queries to database and authorization server sometimes plus it degrades UX for frontend app on clients with low bandwidth (e.g. mobile clients).

  3. Multiple links with different rels to the same resource, e.g. someNS:delete:someObject, someNS:update:someObject etc. Clients may implement the naming convention to determine, which operations are available to someObject specified by curie someNS, or just know all the links for all the operations and methods corresponding to it. Pros: it's possible to give a title to each operation, document it separately, compatible with this specification. Cons: non-standard, verbose, does not explicitly bind links to HTTP methods.

We did not yet consider _meta resource in _embedded, but I think that approach is not convenient from the user perspective, since there's no encapsulation of data in single node (for that reason we consider including either link or embedded value for a resource in our implementations). However, that may work for us, if it will be included in the specification.

That said, I'd vote for optional methods node in the link definition (with single GET value by default), either as an array of method names or as an array of elements, containing method name, schema href (for operations with request body) and title. This solution could be generified for use beyond HTTP, e.g. by naming this node actions and adding optional namespace prefix for action name (without namespace it will default to HTTP and behavior for ops with namespace can be reserved for future specifications).

donaldpipowitch commented 7 years ago

Would love to see a standard solution to this in HAL for the same reasons mentioned by @ivan-gammel, @vchrm, @redben... I would love to see in one response in a standard way (so libs can be build on top of this), if my current person is allowed to delete something or not for example.

donaldpipowitch commented 7 years ago

I just found out about HAL-FORMS. Never heard of this. Is this popular to use in the HAL community? (Would require