Ktor Client, how to specify body parameters - kotlin

I'm trying to send a POST request to the server, this post requires parameters "email" and "password".
but I don't know how to specify parameters, I read the documentation but I didn't understand.
this is my code:
val request=client.post<String> {
url(BASE_URL+"login.php")
body="email=$email,password=$password"
}

fwiw I use something like following here....though I would have thought specifying url like you do should also work. What issue do you see? The body might also be some json for example, or maybe a data class etc if you have serialization setup.
response = client.post(url) {
body = "some params/data etc"
}

It should work if you use serialization, but I solved my problem by using 'Uploading multipart/form-data'
val request=client.post(url) {
body=MultiPartFormDataContent(formData {
append("email","data")
append("password","data")
})
}
see Documentation

Related

How to send a List<Map<String, String>> as parameter for a GET API

I have a requirement to pass List<Map<String, String>> as a parameter for REST GET API.
I need help to know how this can be passed from Postman or similar tool.
I tried to set it as a BODY for a GET API, it is giving me errors.
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.
Any help is appreciated.
You can very well !
I tried and this worked for me
Create a model class which has variable of type List<Map<String, String>> myList;
Define a controller similar to below
#PostMapping("/addList")
public ResponseEntity<List<Map<String, String>>> passList(#RequestBody ListModel listModel) {
System.out.println("List mapped " + listModel);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Create a request from Postman or any tool like this
{
"myList": [
{
"one": "1",
"two": "2"
}
]
}
Response I got
List mapped ListModel [myList=[{one=1, two=2}]]
Make sure you map correct variable name ( for e.g. I have defined myList, so that must be passed so it gets properly mapped in Controller class ) also assuming toString, GetterSetters , and your familiarity with few basic REST annotations related to Spring/SpringBoot :)

Kotlin (Ktor) request class for multipart form data

I want to create a request class that collects all the parts (files and items) and validate it, something similar to the example I put (below) with the json requests.
REQUEST JSON SERIALIZABLE EXAMPLE CLASS
import kotlinx.serialization.Serializable
#Serializable
class CreateGroupRequest(
val name: String,
val description: String? = null,
val visibility: String? = "PUBLIC"
)
HANDLE JSON REQUEST EXAMPLE
route("create") {
post {
val request = call.receive<CreateGroupRequest>()
try {
//CODE
call.respond(HttpStatusCode.OK)
} catch (e: SharedDomainException) {
call.respond(HttpStatusCode(e.errorCode, e.errorMessage))
}
}
}
What I mean, for example, is that in this case I want to change it because the groups also has a profile photo that I want to upload or in other cases, posts domain has text, author and a multiple images.
I have read this stackOverflow post but I can't see how I can make a general class to read the multipart requests without having to duplicate code in each handler.
So, does anyone know how I can read the request multipart-form-data body in a shared class and validate it with kotlin/ktor?
In principle, you can use the ContentNegotiation and register a content converter for the multipart/form-data Content-type. In the convertForReceive method you can use CIOMultipartDataBase to parse multipart data and then deserialize it using kotlinx.serialization library. For deserialize method call you need to provide a decoder for the MultiPartData objects, which you need to implement.
The above approach will work but is very inefficient for parts with a large binary body because parts in an HTTP message go one after another so all of them will be eagerly read into memory.

How can I parse HTTP headers using ktor

I'm using ktor 1.5.3 HTTP client and wondering how can I deserialize HTTP response headers into a list of LinkHeader values. I have the following value in my code:
response.headers.getAll("Link")
which is a list of strings, and I want to get a value of type
List<LinkHeader>
UPDATED:
The details of my use-case:
I have a backend that uses the following response headers to manage pagination:
Link: <https://hostname/v2/issues?orderBy=updated&orderAsc=false&perPage=15>; rel="first"
Link: <https://hostname/v2/issues?orderBy=updated&orderAsc=false&page=2&perPage=15>; rel="prev"
Link: <https://hostname/v2/issues?orderBy=updated&orderAsc=false&page=4&perPage=15>; rel="next"
Link: <https://hostname/v2/issues?orderBy=updated&orderAsc=false&page=116922&perPage=15>; rel="last"
I just have to parse them to understand where is the last page
Since there is not such functionality in Ktor right now, I've created this feature request to address your problem. As a workaround, you can use regular expressions for your particular case to parse headers' values:
data class Link(val url: Url, val rel: String)
fun parse(value: String): Link {
val matches = Regex("""<(.+?)>;\s*rel="(.+?)"""").matchEntire(value) ?: throw Exception("Cannot parse Link header value $value")
val (_, urlString, rel) = (matches.groupValues)
return Link(URLBuilder(urlString).build(), rel)
}
As there is no accurate solution from Ktor, I've implemented a workaround from this article. The same do-while loop worked in my case as well. It makes a redundant API call for an empty last page but works.

How to translate the following rally lookback api request to the Ext request equivalent?

So I have this lookback API request:
https://rally1.rallydev.com/analytics/v2.0/service/rally/workspace/xxxxxxx/artifact/snapshot/query.js?find={"ObjectID":92444754348,"__At":"2017-02-23T00:00:00Z"}&fields=true&start=0&pagesize=10&removeUnauthorizedSnapshots=true
How can I make that request using the Ext equivalent. I have tried many ways, including this one:
let snapshot = Ext.create('Rally.data.lookback.SnapshotStore', {
find: {
ObjectID: 92444754348,
__At: "2017-02-23T00:00:00Z"
}
});
return snapshot.load();
This example returns an object that has the field "raw", which to my understanding is supposed to have all the artifact's fields along with the values they had at the specified time. But, "raw" only has ObjectID, Project, _ValidFrom, and _ValidTo.
Right now I'm able to solve my issue by using an ajax GET request and parsing the JSON; but I would like to use the Ext solution instead (which seems to be the recommended one).
Thanks.
If you include a fetch in your config when you're creating the store it will autocreate the correct model for you.
let snapshot = Ext.create('Rally.data.lookback.SnapshotStore', {
find: {
ObjectID: 92444754348,
__At: "2017-02-23T00:00:00Z"
},
fetch: ['ObjectID'] //add all the fields you want here
});
fields=true is a nice shorthand to get all the data back, but the store/model have no idea how to interpret that...
The store also has config properties for compress, removeUnauthorizedSnapshots and most of the other parameters Lookback Api supports.

An interesting Restlet Attribute behavior

Using Restlet 2.1 for Java EE, I am discovering an interesting problem with its ability to handle attributes.
Suppose you have code like the following:
cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
and on your browser you provide the following URL:
http://localhost:8100/testpath/command
then, of course, the attr attribute gets set to "command".
Unfortunately, suppose you want the attribute to be something like command/test, as in the following URL:
http://localhost:8100/testpath/command/test
or if you want to dynamically add things with different levels, like:
http://localhost:800/testpath/command/test/subsystems/network/security
in both cases the attr attribute is still set to "command"!
Is there some way in a restlet application to make an attribute that can retain the "slash", so that one can, for example, make the attr attribute be set to "command/test"? I would like to be able to just grab everything after testpath and have the entire string be the attribute.
Is this possible? Someone please advise.
For the same case I usually change the type of the variable :
Route route = cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
route.getTemplate().getVariables().get("attr") = new Variable(Variable.TYPE_URI_PATH);
You can do this by using url encoding.
I made the following attachment in my router:
router.attach("/test/{cmd}", TestResource.class);
My test resource class looks like this, with a little help from Apache Commons Codec URLCodec
#Override
protected Representation get() {
try {
String raw = ResourceWrapper.get(this, "cmd");
String decoded = new String(URLCodec.decodeUrl(raw.getBytes()));
return ResourceWrapper.wrap(raw + " " + decoded);
} catch(Exception e) { throw new RuntimeException(e); }
}
Note my resource wrapper class is simply utility methods. The get returns the string of the url param, and the wrap returns a StringRepresentation.
Now if I do something like this:
http://127.0.0.1/test/haha/awesome
I get a 404.
Instead, I do this:
http://127.0.0.1/test/haha%2fawesome
I have URLEncoded the folder path. This results in my browser saying:
haha%2fawesome haha/awesome
The first is the raw string, the second is the result. I don't know if this is suitable for your needs as it's a simplistic example, but as long as you URLEncode your attribute, you can decode it on the other end.