Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

URI questions #15

Open
atgreen opened this issue Nov 19, 2018 · 12 comments
Open

URI questions #15

atgreen opened this issue Nov 19, 2018 · 12 comments

Comments

@atgreen
Copy link

atgreen commented Nov 19, 2018

I was hoping to have URIs like...

http://localhost/api/v1/hello
http://localhost/api/v1/test

But I see that this doesn't work:
(defroute api/v1/hello ...

I don't see any mention of how method names map to paths. How would I do something like this?

Thanks!

@joaotavora
Copy link
Owner

I don't see any mention of how method names map to paths.

Heh, I'm quite surprised: the README goes into this at length, it's the main function in Snooze!!

I think you have to decide first what your REST resource is. Is it "API"? If so your route should be

`(defroute api (:get :text/html version string) ...)

And then version and string will be bound to the uninterned symbols #v1 and #hello respectively, in the first case, and #v1 and #test in the second case. There are ample ways to configure this (see the README).

If however, you want two rest resources: "api/v1/hello" and "api/v1/test", then you should write two routes defining two resources.

(defroute api/v1/hello (:get :text/html &rest arguments-if-you-want-them) ...)

and

(defroute api/v1/test (:get :text/html &rest arguments-if-you-want-them) ...)

@atgreen
Copy link
Author

atgreen commented Nov 20, 2018

When I do this...

(snooze:defroute api/v1/start (:get "text/plain")
(setf player-count (+ 1 player-count))
(format nil "~A" player-count))

I get a 404 error. Hunchentoot reports this:

127.0.0.1 - [2018-11-20 08:38:39] "GET /api/v1/start/ HTTP/1.1" 404 13 "-" "Go-http-client/1.1"

@joaotavora
Copy link
Owner

Indeed snooze seems to have trouble when the resource name has "/" in it. This seems like a bug, I will take a look at it.

@joaotavora
Copy link
Owner

This works for you, right?

(snooze:defroute foo (:get "text/plain") "bar")

And then accessing http://localhost:<yourport>/foo right?

@joaotavora
Copy link
Owner

joaotavora commented Nov 20, 2018

It's not a bug, it's like this for design. Snooze provides a customization point for your case, snooze:*resource-name-function*. Here is its doc, which you can find in the api.lisp file:

(defparameter *resource-name-function* 'default-resource-name
  "How to search for resource names in URI paths.

Value is a function designator called on every request with the
request's URI path. The function might be called with the empty
string.

This function should return two values: a resource designator (a
string, symbol or a resource) and a relative URI string stripped of the
resource-designating part.  If the first value returned is nil,
*HOME-RESOURCE* is used to lookup a suitable resource.

The function should *not* attempt any URI-decoding of the component
string. That is done automatically elsewhere.

The default value is DEFAULT-RESOURCE-NAME, which returns the first
path component as the first value and the remaining URI as the second
value..

Can be let-bound to modify the URI scheme used by a particular
server.")

So you need to write a suitable function for something that will split the string up to three "/" in the string and split it there, returning both parts as separate values.

@atgreen
Copy link
Author

atgreen commented Nov 20, 2018 via email

@atgreen
Copy link
Author

atgreen commented Nov 20, 2018 via email

@mdbergmann
Copy link

mdbergmann commented Jun 26, 2020

I'm also having some trouble here.
I didn't want to create a new ticket.

I hope I didn't overlook something but I've read the readme a few times now.

Basically I have two routes:

(defroute blog (:get :text/html)              ;; index
(defroute blog (:get :text/html name)    ;; named resource

When compiling this I get an error on the second definition:

Lambda list of method #<STANDARD-METHOD BLOG (SNOOZE-VERBS:GET
                                              SNOOZE-TYPES:TEXT/HTML)> 
is incompatible with that of the generic function BLOG.
Method's lambda-list : (SNOOZE-VERBS:HTTP-VERB SNOOZE-TYPES:TYPE)
Generic-function's   : (SNOOZE-VERBS:HTTP-VERB SNOOZE-TYPES:TYPE
                        NAME)

In principle it's clear, two methods with the same name. But my lacking CL knowledge doesn't let me overcome this.
Any ideas?

@joaotavora
Copy link
Owner

When you use defroute you are basically writing defmethod. Just expand it (in sly it's C-c C-m on the ( of the defroute. In fact, you can defmethod directly.

Now, this should be enough to hint that a route in Snooze is only a method in a generic function. So when you do the first blog you establish a certain protocol for the generic function, when you try to change that protocol, Lisp complains. Most Lisps (I think SBCL at least) give you the option to replace the protocol of the generic function with the new one.

You should learn about "Generic Functions". In Snooze, a REST resource is represented by a generic function. The methods of the generic functions are the different routes that provide access to that resource. Different types of access. Read access is a GET specializer on the first HTTP-VERB arg, write access if PUT or POST or DELETE on that arg. And the same with the content types.

Also, in your case, it seems like you want a /blog/<name-of-blog> route. Ask yourself: is <name-of-blog> mandatory, or is it optional? If it's optional it should be an &optional or &key argument. If it's mandatory, then pointing your browser to just /blog automatically generates a 400 error, as one would expect.

@mdbergmann
Copy link

or is it optional? If it's optional it should be an &optional

Great. This was a good pointer. Got it working with &optional.

And then version and string will be bound to the uninterned symbols #v1 and #hello respectively, in the first case, and #v1 and #test in the second case. There are ample ways to configure this (see the README).

Are you refering to the uri-to-arguments method?

@mdbergmann
Copy link

I believe you can close this one as well.

@joaotavora
Copy link
Owner

Are you refering to the uri-to-arguments method?

Yes, and its counterpart arguments-to-uri. Whatever you do to uri-to-arguments you should also do to arguments-to-uri, so that you can later use blog-path to generate perfectly matching URIs for your blog REST resource.

You could also use read-for-resource and write-for-resource, if you're concerned with the string representation of all the arguments for a particular resource. Most times, you will want uri-to-arguments and arguments-to-uri though, leveraging their default implementations with call-next-method, of course.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants