Search code examples
htmlkotlinktorktor-client

How to pass a url parameter with Ktor Kotlin and HttpClient?


I want to use the Google Books API and therefore I have a search field to find a book by it's isbn. I'm using Kotlin with Ktor and frontend with HTML/CSS. With the Routing class I want to call the api when the search field is clicked and pass the parameter to build the whole url and return the json data.

The problem is: When I try to pass the parameter in Routing class by reading the form parameters nothing happens and the page is not redirected to my search page.

So how can I build this to pass the isbn in the search field and build the correct url for the API call?

Routing.kt:

fun Application.configureRouting() {

routing {
    static("/static") {
        resources("files")
    }
    get("/") {
        call.respondRedirect("books")
    }

    route("search") {
        get {
            call.respond(FreeMarkerContent("search.ftl", model = null))
        }

        get("field") {
            val formParameters = call.receiveParameters()
            val isbn = formParameters.getOrFail("isbn").toLong()
            val client = HttpClient(CIO)
            val response: HttpResponse = client.get("https://www.googleapis.com/books/v1/volumes?q=isbn:$isbn")
            println(response.status)
            val stringBody: String = response.body()
            println(stringBody)
            client.close()
            call.respondRedirect("/search")
        }
    }
}
}

When I remove these two lines and pas a isbn directly in the url, I get the json response:

val formParameters = call.receiveParameters()
val isbn = formParameters.getOrFail("isbn").toLong()

The url for testing:

 val response: HttpResponse = client.get("https://www.googleapis.com/books/v1/volumes?q=isbn:9783453528420")

search.ftl:

<#import "_layout.ftl" as layout />
<@layout.header>
    <div>
        <div class="text-center">
            <h1 class="display-4">Search</h1>
        </div>
        <div class="container">
            <div class="row">
                <div class="form-group has-search">
                    <span class="fa fa-search form-control-feedback"></span>
                    <form action="/search/field" method="get">
                    <input type="text" class="form-control" name="isbn">
                    </form>
                </div>
            </div>
</@layout.header>


Solution

  • The form is submitted as GET. GETs don't have form parameters, but query parameters.

    The server side code is expecing a GET, but it's trying to read a form parameter.

    Try changing the line to

    val isbn = call.request.queryParameters.getOrFail("isbn").toLong()
    

    I haven't tested this code, but the solution is around using queryParameters.