unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Operate upon POST request with Guile webserver
@ 2022-08-17 11:50 Gentoo Arch
  2022-08-19 11:00 ` Daniel Meißner
  2022-08-21 19:47 ` Ricardo Wurmus
  0 siblings, 2 replies; 5+ messages in thread
From: Gentoo Arch @ 2022-08-17 11:50 UTC (permalink / raw)
  To: guile-user

Hi,

I'd like to ask for your advice.

I'm trying to figure out how Guile webserver works to develop a simple 
BBS  and I'm kinda stuck with POST request.

My Guile script generates a form on any path and the form sends to 
"/post" path, where I can easily render content sent by the form.

(define (show-page request body)
   (if (equal? (get-path request) ; get-path is my other function to retrieve the path
               '("post"))
       (values '((content-type . (text/plain)))
                body)
       (respond ; literally a respond template from webserver doc
        `((h1 "Oops!")
      (p (@ (id "test")) "The path: " ,(get-path request))
      (form (@ (method "POST") (action "/post"))
            (label (@ (for "test")) "Content: ")
            (input (@ (id "test") (type "text") (name "content")))
            (input (@ (type "submit") (value "Submit"))))))))

But I would like to operate upon the content the form sends like web 
apps usually do: insert it in the database or simply write to file. So 
my question is how I can proceed with something like that:

(define tf (open-file "test-file.txt" "a"))

(define (show-page request body)
   (if (equal? (get-path request)
               '("post"))
       ((values '((content-type . (text/plain)))
               body)
        (display body tf)); I'd like to write/append body to the file, but it does nothing
       (respond
        `((h1 "Oops!")
      (p (@ (id "test")) "The path: " ,(get-path request))
      (form (@ (method "POST") (action "/post"))
            (label (@ (for "test")) "Content: ")
            (input (@ (id "test") (type "text") (name "content")))
            (input (@ (type "submit") (value "Submit"))))))))

If I remove (values), it does write to a file (with a complaint in 
webserver output), but I'd also like to add some kind of redirect from 
"/post" page after the script has written stuff to file and I can't use 
two functions per response.

Could you please advise how I can achieve this functionality?


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Operate upon POST request with Guile webserver
  2022-08-17 11:50 Operate upon POST request with Guile webserver Gentoo Arch
@ 2022-08-19 11:00 ` Daniel Meißner
  2022-08-20 12:03   ` Gentoo Arch
  2022-08-21 19:47 ` Ricardo Wurmus
  1 sibling, 1 reply; 5+ messages in thread
From: Daniel Meißner @ 2022-08-19 11:00 UTC (permalink / raw)
  To: guile-user

Hi,

Am 17. August 2022 13:50:46 MESZ schrieb Gentoo Arch <gentoocore@firemail.cc>:
> Hi,
> 
> I'd like to ask for your advice.
> 
> I'm trying to figure out how Guile webserver works to develop a simple BBS  and I'm kinda stuck with POST request.
> 
> My Guile script generates a form on any path and the form sends to "/post" path, where I can easily render content sent by the form.
> 
> (define (show-page request body)
>   (if (equal? (get-path request) ; get-path is my other function to retrieve the path
>               '("post"))
>       (values '((content-type . (text/plain)))
>                body)
>       (respond ; literally a respond template from webserver doc
>        `((h1 "Oops!")
>      (p (@ (id "test")) "The path: " ,(get-path request))
>      (form (@ (method "POST") (action "/post"))
>            (label (@ (for "test")) "Content: ")
>            (input (@ (id "test") (type "text") (name "content")))
>            (input (@ (type "submit") (value "Submit"))))))))
> 
> But I would like to operate upon the content the form sends like web apps usually do: insert it in the database or simply write to file. So my question is how I can proceed with something like that:
> 
> (define tf (open-file "test-file.txt" "a"))
> 
> (define (show-page request body)
>   (if (equal? (get-path request)
>               '("post"))
>       ((values '((content-type . (text/plain)))
>               body)
>        (display body tf)); I'd like to write/append body to the file, but it does nothing
>       (respond
>        `((h1 "Oops!")
>      (p (@ (id "test")) "The path: " ,(get-path request))
>      (form (@ (method "POST") (action "/post"))
>            (label (@ (for "test")) "Content: ")
>            (input (@ (id "test") (type "text") (name "content")))
>            (input (@ (type "submit") (value "Submit"))))))))
> 
> If I remove (values), it does write to a file (with a complaint in webserver output), but I'd also like to add some kind of redirect from "/post" page after the script has written stuff to file and I can't use two functions per response.
> 
> Could you please advise how I can achieve this functionality?

I think you probably want to replace the true-branch of your `if' form with

(begin
  (display body tf)
  (values '((content-type . (text/plain)) body))

It is important that the handler returns two values, so the `values' form must come last in the sequence. These two return values make up the response.

Best
Daniel



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Operate upon POST request with Guile webserver
  2022-08-19 11:00 ` Daniel Meißner
@ 2022-08-20 12:03   ` Gentoo Arch
  0 siblings, 0 replies; 5+ messages in thread
From: Gentoo Arch @ 2022-08-20 12:03 UTC (permalink / raw)
  To: Daniel Meißner; +Cc: guile-user

Hi Daniel,

Thanks a lot, that worked! I also can print debugging output to server 
log and do other useful stuff.

I totally forgot about (begin); procedural habit I guess.

On 8/19/22 14:00, Daniel Meißner wrote:
> Hi,
>
> Am 17. August 2022 13:50:46 MESZ schrieb Gentoo Arch <gentoocore@firemail.cc>:
>> Hi,
>>
>> I'd like to ask for your advice.
>>
>> I'm trying to figure out how Guile webserver works to develop a simple BBS  and I'm kinda stuck with POST request.
>>
>> My Guile script generates a form on any path and the form sends to "/post" path, where I can easily render content sent by the form.
>>
>> (define (show-page request body)
>>    (if (equal? (get-path request) ; get-path is my other function to retrieve the path
>>                '("post"))
>>        (values '((content-type . (text/plain)))
>>                 body)
>>        (respond ; literally a respond template from webserver doc
>>         `((h1 "Oops!")
>>       (p (@ (id "test")) "The path: " ,(get-path request))
>>       (form (@ (method "POST") (action "/post"))
>>             (label (@ (for "test")) "Content: ")
>>             (input (@ (id "test") (type "text") (name "content")))
>>             (input (@ (type "submit") (value "Submit"))))))))
>>
>> But I would like to operate upon the content the form sends like web apps usually do: insert it in the database or simply write to file. So my question is how I can proceed with something like that:
>>
>> (define tf (open-file "test-file.txt" "a"))
>>
>> (define (show-page request body)
>>    (if (equal? (get-path request)
>>                '("post"))
>>        ((values '((content-type . (text/plain)))
>>                body)
>>         (display body tf)); I'd like to write/append body to the file, but it does nothing
>>        (respond
>>         `((h1 "Oops!")
>>       (p (@ (id "test")) "The path: " ,(get-path request))
>>       (form (@ (method "POST") (action "/post"))
>>             (label (@ (for "test")) "Content: ")
>>             (input (@ (id "test") (type "text") (name "content")))
>>             (input (@ (type "submit") (value "Submit"))))))))
>>
>> If I remove (values), it does write to a file (with a complaint in webserver output), but I'd also like to add some kind of redirect from "/post" page after the script has written stuff to file and I can't use two functions per response.
>>
>> Could you please advise how I can achieve this functionality?
> I think you probably want to replace the true-branch of your `if' form with
>
> (begin
>    (display body tf)
>    (values '((content-type . (text/plain)) body))
>
> It is important that the handler returns two values, so the `values' form must come last in the sequence. These two return values make up the response.
>
> Best
> Daniel
>



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Operate upon POST request with Guile webserver
  2022-08-17 11:50 Operate upon POST request with Guile webserver Gentoo Arch
  2022-08-19 11:00 ` Daniel Meißner
@ 2022-08-21 19:47 ` Ricardo Wurmus
  2022-08-21 20:03   ` Gentoo Arch
  1 sibling, 1 reply; 5+ messages in thread
From: Ricardo Wurmus @ 2022-08-21 19:47 UTC (permalink / raw)
  To: Gentoo Arch; +Cc: guile-user

Hi,

Gentoo Arch <gentoocore@firemail.cc> writes:

> I'm trying to figure out how Guile webserver works to develop a simple
> BBS  and I'm kinda stuck with POST request.
>
> My Guile script generates a form on any path and the form sends to
> "/post" path, where I can easily render content sent by the form.

Daniel already solved your immediate problem, but I wanted to show you a
common pattern many of us use to build web servers:

--8<---------------cut here---------------start------------->8---
(define* (render-html sxml #:optional (headers '()))
  (list (append '((content-type . (text/html))) headers)
        (lambda (port)
          (sxml->xml sxml port))))

(define (request-path-components request)
  (split-and-decode-uri-path (uri-path (request-uri request))))

(define (controller request body)
  (match (cons (request-method request)
               (request-path-components request))
    (('POST "api" "container" id "launch")
     (render-html `(html
                    (head (title "whatever"))
                    (body "who cares"))))
    (('PUT "api" "container" id "connect")
     ...)
    (('DELETE "api" "container" id)
     ...)
    (('GET "api" "containers")
     ...)
    (('GET "api" "container" id "info")
     ...)
    (_ (not-found (request-uri request)))))

(define (handler request body)
  (format (current-error-port)
          "~a ~a~%"
          (request-method request)
          (uri-path (request-uri request)))
  (if (getenv "DEBUG")
      (call-with-error-handling
        (lambda ()
          (apply values (controller request body))))
      (apply values (controller request body))))

(define (run-my-server port)
  (run-server handler #:port port))
--8<---------------cut here---------------end--------------->8---

The controller uses match on the method and the path, which makes for
pretty clear code.  You can bind parts of the path to variables and
operate on them.

The “values” stuff is handled in “handler” here, but it also be done
directly in the controller.

To operate on the POST payload, you’d need to parse the form data from
the body, which is available to the controller.  See also guile-webutils
for some handy tools.

Hope this helps!

-- 
Ricardo



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Operate upon POST request with Guile webserver
  2022-08-21 19:47 ` Ricardo Wurmus
@ 2022-08-21 20:03   ` Gentoo Arch
  0 siblings, 0 replies; 5+ messages in thread
From: Gentoo Arch @ 2022-08-21 20:03 UTC (permalink / raw)
  To: Ricardo Wurmus; +Cc: guile-user

Hi Ricardo,

Thank you, that really looks like a prototype for MVC app I wanted to craft.

On 8/21/22 22:47, Ricardo Wurmus wrote:
> Hi,
>
> Gentoo Arch <gentoocore@firemail.cc> writes:
>
>> I'm trying to figure out how Guile webserver works to develop a simple
>> BBS  and I'm kinda stuck with POST request.
>>
>> My Guile script generates a form on any path and the form sends to
>> "/post" path, where I can easily render content sent by the form.
> Daniel already solved your immediate problem, but I wanted to show you a
> common pattern many of us use to build web servers:
>
> --8<---------------cut here---------------start------------->8---
> (define* (render-html sxml #:optional (headers '()))
>    (list (append '((content-type . (text/html))) headers)
>          (lambda (port)
>            (sxml->xml sxml port))))
>
> (define (request-path-components request)
>    (split-and-decode-uri-path (uri-path (request-uri request))))
>
> (define (controller request body)
>    (match (cons (request-method request)
>                 (request-path-components request))
>      (('POST "api" "container" id "launch")
>       (render-html `(html
>                      (head (title "whatever"))
>                      (body "who cares"))))
>      (('PUT "api" "container" id "connect")
>       ...)
>      (('DELETE "api" "container" id)
>       ...)
>      (('GET "api" "containers")
>       ...)
>      (('GET "api" "container" id "info")
>       ...)
>      (_ (not-found (request-uri request)))))
>
> (define (handler request body)
>    (format (current-error-port)
>            "~a ~a~%"
>            (request-method request)
>            (uri-path (request-uri request)))
>    (if (getenv "DEBUG")
>        (call-with-error-handling
>          (lambda ()
>            (apply values (controller request body))))
>        (apply values (controller request body))))
>
> (define (run-my-server port)
>    (run-server handler #:port port))
> --8<---------------cut here---------------end--------------->8---
>
> The controller uses match on the method and the path, which makes for
> pretty clear code.  You can bind parts of the path to variables and
> operate on them.
>
> The “values” stuff is handled in “handler” here, but it also be done
> directly in the controller.
>
> To operate on the POST payload, you’d need to parse the form data from
> the body, which is available to the controller.  See also guile-webutils
> for some handy tools.
>
> Hope this helps!
>



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-08-21 20:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-17 11:50 Operate upon POST request with Guile webserver Gentoo Arch
2022-08-19 11:00 ` Daniel Meißner
2022-08-20 12:03   ` Gentoo Arch
2022-08-21 19:47 ` Ricardo Wurmus
2022-08-21 20:03   ` Gentoo Arch

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).