I am trying to build a multi-part file upload REST route in Quarkus (using Kotlin), but having issues with the route mapping.
From the Client side I am posting a form that contains a text value and a file value.
const formData = new FormData();
formData.append("text", text);
formData.append("file", files[0]);
fetch('http://localhost:8080/data', {
method: 'POST',
body: formData
})
From the serverside, I am trying to retrieve the values as follows.
class FormData(#FormParam("text") #PartType(MediaType.TEXT_PLAIN) var text:String,
#FormParam("file") #PartType(MediaType.APPLICATION_OCTET_STREAM) var file:InputStream)
#Path("/data")
class FormUploadResource {
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
fun upload(#MultipartForm form:FormData) {
println(form.text)
println(form.file)
}
}
However, when I execute the endpoint, I get a org.jboss.resteasy.spi.ReaderException: java.lang.NoSuchMethodException: error.
I have tried to make sure that the text and file parameters are correctly being received, and have inspected the payload coming in with the following code
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
fun upload(input:MultipartFormDataInput) {
var map = input.getFormDataMap()
map.entries.forEach {
println("""${it.key} - ${it.value}""")
if (it.value is List<InputPart>) {
it.value.forEach { ip ->
println(""" --- ${ip.getMediaType()} """ )
}
}
}
}
And it correctly says
text - [org.jboss.resteasy.plugins.providers.multipart.MultipartInputImpl$PartImpl#660c4317]
--- text/plain;charset=UTF-8
file - [org.jboss.resteasy.plugins.providers.multipart.MultipartInputImpl$PartImpl#3aee346]
--- application/octet-stream;charset=UTF-8
I assume there is something going wrong with the FormData class that RestEasy isn't automagically mapping to it. I have tried changing the type for "file" to be ByteArray and File, and they both fail also.
I have struggled to find Kotlin specific answers, so it is possible this is a Kotlin oddity also.
I just went through this issue, showing no logs at all. I managed to make it work, it seems that a empty constructor is required :
class FormData() {
#FormParam("text") #PartType(MediaType.TEXT_PLAIN)
var text: String? = null
#FormParam("file") #PartType(MediaType.APPLICATION_OCTET_STREAM)
var file: InputStream? = null
}
However I must still be missing something because
class FormData(
#FormParam("text") #PartType(MediaType.TEXT_PLAIN) var text: String?,
#FormParam("file") #PartType(MediaType.APPLICATION_OCTET_STREAM) var file: InputStream?
) {
constructor() : this(null, null)
}
enters in the method but doesn't init values
Edit: after testing secondary constructors and primary constructors like this working one,
class FormData
{
#FormParam("file")
#PartType(MediaType.APPLICATION_OCTET_STREAM)
var file: InputStream? = null
constructor() {
this.file = null
}
}
It seems that an EMPTY PRIMARY CONSTRUCTOR is required :)
Hope it helped !
I won't mark this answer as correct, as I am not happy with the solution, but it at least works.
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
fun upload(input:MultipartFormDataInput) {
val map = input.getFormDataMap()
val text = map.get("text")?.first()?.getBodyAsString() ?: ""
val file = map.get("file")?.first()?.getBodyAsString() ?: ""
println(form.text)
println(form.file)
}
As you can see, I am getting the data directly from the MultipartFormDataInput, rather than auto-constructing the object. Hopefully someone is able to shed some light on why this work around is needed, or whether a better solution is available.
Related
I'm new to Kotlin and Android developing.
I'm trying to use the HttpsURL to POST a value of '0' or any other value
to the 'data' variable https://www.mytestsite.com/on-test.php where is a 'data'
variable set inside.
My Kotlin code is reaching the '../on-php' url but the POST method is likely not executed
or no data was transferred for some unknown reason.
I also made another 'post.php' from where I'm posting to the same on-test.php like my Android app,
and that way it is working.
I have no any idea what I'm doing wrong.
Any help I would appreciate.
Here is my code I'm trying to make working:
btn_test.setOnClickListener {
object : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void?): Void? {
val url = "https://www.mytestsite.com/on-test.php"
val data = JSONObject()
data.put("data", "0")
val connection = URL(url).openConnection() as HttpsURLConnection
connection.requestMethod = "POST"
connection.doOutput = true
connection.setRequestProperty("Content-Type", "application/json")
connection.connect()
val wr = DataOutputStream(connection.outputStream)
wr.writeBytes(data.toString())
wr.flush()
wr.close()
error_data = connection.responseMessage
return null
}
}.execute()
text_status.setText(error_data)
}
I come from Java and I'm following a tutorial online regarding using the Volley library to make web requests in Android.
The instructor created the request variable like this:
val registerRequest = object : StringRequest(Method.POST, URL_REGISTER, Response.Listener {
println(it) // will print the response
complete(true)
}, Response.ErrorListener {
Log.d("ERROR", "Could not register user: $it")
complete(false)
}) {
override fun getBodyContentType(): String {
return "application/json; charset=utf-8"
}
override fun getBody(): ByteArray {
return requestBody.toByteArray()
}
}
I understand that he's creating a registerRequest variable of type StringRequest. But what I don't understand is why he prefixed StringRequest with object : here.
Also I understand that StringRequest constructor takes in an Int, String, Lambda, Lambda. After that it becomes confusing to me because the developer was able to declare some override methods after the constructor closes. Why did they do this? From what I can tell, this is similar to subclassing StringRequest, then writing the override methods there? Am I right?
Coming from Java, this way of writing code is quite unusual to me.
Am building an API and using intercept(ApplicationCallPipeline.Call){} to run some logic before each route execution. I need to pass data from the intercept() method to the called route and
am setting data by using call.attributes.put() in the intercept() like this:
val userKey= AttributeKey<User>("userK")
call.attributes.put(userKey, userData)
And retrieve userData with call.attributes[userKey] .
What happens is that call.attributes[userKey] only works in the intercept() method where I have set the attribute. It doesn't work in the route where I need it.
It throws me
java.lang.IllegalStateException: No instance for key AttributeKey: userK
I wonder if am doing things in the right way
Here is the simplest code reproducing what you describe:
class KtorTest {
data class User(val name: String)
private val userKey = AttributeKey<User>("userK")
private val expected = "expected name"
private val module = fun Application.() {
install(Routing) {
intercept(ApplicationCallPipeline.Call) {
println("intercept")
call.attributes.put(userKey, User(expected))
}
get {
println("call")
val user = call.attributes[userKey]
call.respond(user.name)
}
}
}
#Test fun `pass data`() {
withTestApplication(module) {
handleRequest {}.response.content.shouldNotBeNull() shouldBeEqualTo expected
}
}
}
I intercept the call, put the user in the attributes, and finally respond with the user in the get request.
The test passes.
What ktor version are you using and which engine?
I played about with Kotlin's unsupported JavaScript backend in 1.0.x and am now trying to migrate my toy project to 1.1.x. It's the barest bones of a single-page web app interfacing with PouchDB. To add data to PouchDB you need JavaScript objects with specific properties _id and _rev. They also need to not have any other properties beginning with _ because they're reserved by PouchDB.
Now, if I create a class like this, I can send instances to PouchDB.
class PouchDoc(
var _id: String
) {
var _rev: String? = null
}
However, if I do anything to make the properties virtual -- have them override an interface, or make the class open and create a subclass which overrides them -- the _id field name becomes mangled to something like _id_mmz446$_0 and so PouchDB rejects the object. If I apply #JsName("_id") to the property, that only affects the generated getter and setter -- it still leaves the backing field with a mangled name.
Also, for any virtual properties whose names don't begin with _, PouchDB will accept the object but it only stores the backing fields with their mangled names, not the nicely-named properties.
For now I can work around things by making them not virtual, I think. But I was thinking of sharing interfaces between PouchDoc and non-PouchDoc classes in Kotlin, and it seems I can't do that.
Any idea how I could make this work, or does it need a Kotlin language change?
I think your problem should be covered by https://youtrack.jetbrains.com/issue/KT-8127
Also, I've created some other related issues:
https://youtrack.jetbrains.com/issue/KT-17682
https://youtrack.jetbrains.com/issue/KT-17683
And right now You can use one of next solutions, IMO third is most lightweight.
interface PouchDoc1 {
var id: String
var _id: String
get() = id
set(v) { id = v}
var rev: String?
var _rev: String?
get() = rev
set(v) { rev = v}
}
class Impl1 : PouchDoc1 {
override var id = "id0"
override var rev: String? = "rev0"
}
interface PouchDoc2 {
var id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
}
class Impl2 : PouchDoc2 {
init {
id = "id1"
rev = "rev1"
}
}
external interface PouchDoc3 { // marker interface
}
var PouchDoc3.id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var PouchDoc3.rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
class Impl3 : PouchDoc3 {
init {
id = "id1"
rev = "rev1"
}
}
fun keys(a: Any) = js("Object").getOwnPropertyNames(a)
fun printKeys(a: Any) {
println(a::class.simpleName)
println(" instance keys: " + keys(a).toString())
println("__proto__ keys: " + keys(a.asDynamic().__proto__).toString())
println()
}
fun main(args: Array<String>) {
printKeys(Impl1())
printKeys(Impl2())
printKeys(Impl3())
}
I got a good answer from one of the JetBrains guys, Alexey Andreev, over on the JetBrains forum at https://discuss.kotlinlang.org/t/controlling-the-jsname-of-fields-for-pouchdb-interop/2531/. Before I describe that, I'll mention a further failed attempt at refining #bashor's answer.
Property delegates
I thought that #bashor's answer was crying out to use property delegates but I couldn't get that to work without infinite recursion.
class JSMapDelegate<T>(
val jsobject: dynamic
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return jsobject[property.name]
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
jsobject[property.name] = value
}
}
external interface PouchDoc4 {
var _id: String
var _rev: String
}
class Impl4() : PouchDoc4 {
override var _id: String by JSMapDelegate<String>(this)
override var _rev: String by JSMapDelegate<String>(this)
constructor(_id: String) : this() {
this._id = _id
}
}
The call within the delegate to jsobject[property.name] = value calls the set function for the property, which calls the delegate again ...
(Also, it turns out you can't put a delegate on a property in an interface, even though you can define a getter/setter pair which work just like a delegate, as #bashor's PouchDoc2 example shows.)
Using an external class
Alexey's answer on the Kotlin forums basically says, "You're mixing the business (with behaviour) and persistence (data only) layers: the right answer would be to explicitly serialise to/from JS but we don't provide that yet; as a workaround, use an external class." The point, I think, is that external classes don't turn into JavaScript which defines property getters/setters, because Kotlin doesn't let you define behaviour for external classes. Given that steer, I got the following to work, which does what I want.
external interface PouchDoc5 {
var _id: String
var _rev: String
}
external class Impl5 : PouchDoc5 {
override var _id: String
override var _rev: String
}
fun <T> create(): T = js("{ return {}; }")
fun Impl5(_id: String): Impl5 {
return create<Impl5>().apply {
this._id = _id
}
}
The output of keys for this is
null
instance keys: _id
__proto__ keys: toSource,toString,toLocaleString,valueOf,watch,unwatch,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__,__proto__,constructor
Creating external classes
Three notes about creating instances of external classes. First, Alexey said to write
fun <T> create(): T = js("{}")
but for me (with Kotlin 1.1) that turns into
function jsobject() {
}
whose return value is undefined. I think this might be a bug, because the official doc recommends the shorter form, too.
Second, you can't do this
fun Impl5(_id: String): Impl5 {
return (js("{}") as Impl5).apply {
this._id = _id
}
}
because that explicitly inserts a type-check for Impl5, which throws ReferenceError: Impl5 is not defined (in Firefox, at least). The generic function approach skips the type-check. I'm guessing that's not a bug, since Alexey recommended it, but it seems odd, so I'll ask him.
Lastly, you can mark create as inline, though you'll need to suppress a warning :-)
I want to write a Spek test in Kotlin.
How to read an HTML file from the src/test/resources folder?
class MySpec : Spek(
{
describe("blah blah") {
given("blah blah") {
var fileContent: String = ""
beforeEachTest {
// How to read the file.html in src/test/resources/html/
fileContent = ...
}
it("should blah blah") {
...
}
}
}
}
)
val fileContent = MySpec::class.java.getResource("/html/file.html").readText()
No idea why this is so hard, but the simplest way I've found (without having to refer to a particular class) is:
fun getResourceAsText(path: String): String? =
object {}.javaClass.getResource(path)?.readText()
It returns null if no resource with this name is found (as documented).
And then passing in an absolute URL, e.g.
val html = getResourceAsText("/www/index.html")!!
another slightly different solution:
#Test
fun basicTest() {
"/html/file.html".asResource {
// test on `it` here...
println(it)
}
}
fun String.asResource(work: (String) -> Unit) {
val content = this.javaClass::class.java.getResource(this).readText()
work(content)
}
A slightly different solution:
class MySpec : Spek({
describe("blah blah") {
given("blah blah") {
var fileContent = ""
beforeEachTest {
html = this.javaClass.getResource("/html/file.html").readText()
}
it("should blah blah") {
...
}
}
}
})
Kotlin + Spring way:
#Autowired
private lateinit var resourceLoader: ResourceLoader
fun load() {
val html = resourceLoader.getResource("classpath:html/file.html").file
.readText(charset = Charsets.UTF_8)
}
Using Google Guava library Resources class:
import com.google.common.io.Resources;
val fileContent: String = Resources.getResource("/html/file.html").readText()
private fun loadResource(file: String) = {}::class.java.getResource(file).readText()
val fileContent = javaClass.getResource("/html/file.html").readText()
This is the way that I prefer to do it:
fun getResourceText(path: String): String {
return File(ClassLoader.getSystemResource(path).file).readText()
}
this top-level kotlin function will do the job in any case
fun loadResource(path: String): URL {
return Thread.currentThread().contextClassLoader.getResource(path)
}
or if you want a more robust function
fun loadResource(path: String): URL {
val resource = Thread.currentThread().contextClassLoader.getResource(path)
requireNotNull(resource) { "Resource $path not found" }
return resource
}
FYI: In all the above cases. getResource() is unsafe way of using nullable.
Haven't tried locally but I prefer this way:
fun readFile(resourcePath: String) = String::class.java.getResource(resourcePath)?.readText() ?: "<handle default. or handle custom exception>"
Or even as custom datatype function
private fun String.asResource() = this::class.java.getResource(resourcePath)?.readText() ?: "<handle default. or handle custom exception>"
and then you can call directly on path like:
// For suppose
val path = "/src/test/resources"
val content = path.asResource()
I prefer reading resources in this way:
object {}.javaClass.getResourceAsStream("/html/file.html")?.use { it.reader(Charsets.UTF_8).readText() }
Explenation:
getResourceAsStream instead getResource. The resource on classpath can be basically anywhere. e.g. packed inside another .jar file.
In these situations accessing resource via URL class returned from getResource method will fail. But accessing via method getResourceAsStream works in every situation.
object {} - This is not nice syntax, but it is not dependent on name of your class MyClass and works even in static (compenion object) block.
use to close stream - in most cases it is not necessary, but there can be some special classloaders, which may need it.
reader(Charsets.UTF_8) - UTF_8 is default encoding, but I prefer to be explicit. If you will encode your resource files in other encoding e.g. ISO-8859-2 you will not overlook it.
Another variation that handles null resource in place:
val content = object {}.javaClass
.getResource("/html/file.html")
?.let(URL::readText)
?: error("Cannot open/find the file")
// ?: "default text" // Instead of error()
You might find the File class useful:
import java.io.File
fun main(args: Array<String>) {
val content = File("src/main/resources/input.txt").readText()
print(content)
}