I deployed a NLP model as an API with Flask. Now I want to call the API from my simple Android app to process some text and to return a prediction, however when the Android App performs the HTTP request using Volley it does not add the parameters in the URL for some reason. Here is the code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// button
val showButton = findViewById<Button>(R.id.showInput)
// text
val editText = findViewById<EditText>(R.id.editText)
// Setting On Click Listener
showButton.setOnClickListener {
// user input
val text = editText.text
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(this)
val url = "http://192.168.100.12:5000/"
// Request a string response from the provided URL.
val request = object : StringRequest(Request.Method.POST,
url,
Response.Listener {
response ->
editText.setText(response.toString())
},
Response.ErrorListener { error ->
Log.i("error ", error.toString())
editText.setText("Volley error: $it ")
}) {
override fun getBodyContentType(): String {
return "application/json"
}
#Throws(AuthFailureError::class)
override fun getBody(): ByteArray {
val params = HashMap<String,String>()
params.put("text", text.toString())
return params.toString().toByteArray()
}
}
// Add the request to the RequestQueue.
queue.add(request)
// Showing the response
Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
}
}
}
In essence, I am expecting the URL to be "http://192.168.100.12:5000/?text=<insert_text_here>", but the actual URL that the app calls is just "http://192.168.100.12:5000" without the parameters.
I know this because my Flask app returns this error:
AttributeError: 'NoneType' object has no attribute 'lower'
The error response code is 500.
I tested the Flask API with both the browser and Postman and it is working fine, but just in case I will leave the code here:
from flask import Flask,render_template,url_for,request,jsonify
from sklearn.externals import joblib
import traceback
app = Flask(__name__)
#app.route("/",methods=['GET','POST'])
def predict():
try:
tweet = request.args.get('text')
model = open('model.pkl','rb')
model = joblib.load(model)
prediction = model.predict([tweet])
if prediction == [0]:
return 'This tweet does not violate our Community Guidelines'
else:
return 'This tweet violates out Community Guidelines for hate speech'
except:
return traceback.format_exc()
if __name__ == '__main__':
app.run(host = '192.168.100.12',debug=True)
So far I also have tried the code from this post as well as using the GET method with getParams() function instead of getBody() as shown below, but none of them worked.
#Throws(AuthFailureError::class)
override fun getParams(): Map<String, String>? {
val params: HashMap<String, String> = HashMap()
params["text"] = text.toString()
return params
}
Can you please help me solve this issue? I would be very grateful.
Related
I'm using Retrofit2 to get data in my Android applications. It looks like this:
interface ApiChatService {
#GET("ncs-chat-web/rest/v1/message")
suspend fun getChatMessages(#Header("Authorization") jwtToken: String, #Query("page") page: Long, #Query("count") count: Int): Response<List<ChatMessageApi>>
}
I call this Retrofit function this way:
override suspend fun getChatMessages(
jwtToken: String,
page: Long
): OperationResult<List<ChatMessageApi>> {
return try {
val response: Response<List<ChatMessageApi>> =
apiChatService.getChatMessages(normalizeJwtToken(jwtToken), page = page, count = MESSAGE_PAGE_SIZE)
if (response.isSuccessful) {
OperationResult(operationResult = Result.OK, resultObject = response.body())
} else {
Log.d("ApiDatasourceImpl.getChatMessages", response.errorBody()?.string()?: "Empty error message")
OperationResult(
operationResult = Result.ERROR,
operationInfo = response.errorBody()?.string()
)
}
} catch (e: Exception) {
Log.d("ApiDatasourceImpl.getChatMessages", e.localizedMessage ?: "Empty error message")
OperationResult(operationResult = Result.ERROR, operationInfo = e.localizedMessage)
}
}
In my Android code I got response code 500 with message "Internal server error"
When I call this request in Postman with such URL
https://my-server.com/ncs-chat-web/rest/v1/message?count=10&page=1
I got 200 code and expected payload.
I'm wondering is there any way to get URL which create Retrofit based on my interface function?
I request your help in solving this pesky problem. I am trying to return the value of 'response' returned by the REST API call, to the calling program. I get null string. Here is the code for your reference. In this code, I want the value of the variable 'response' returned to the caller in onCreate, ie 'restApiResponse' variable.
Thank you very much.
-Vittal
PS: I am a newbie to Kotlin/Android programming.
class OpeningActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_opening)
var restApiResponse = getAPIResponse() // <<<< restApiResponse is empty string
val submitButton = findViewById<Button>(R.id.submitBtn)
submitButton.setOnClickListener() {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
fun getAPIResponse() : String {
val myUrl = "YOUR REST API CALL URL"
var jsonObject = JSONObject();
val queue = Volley.newRequestQueue(this);
cpyResponse = StringBuilder();
val stringRequest = object: StringRequest(Request.Method.GET, myUrl,
Response.Listener<String> { response ->
Log.d("A", "VK: Response is: " + response.substring(0, 500))
cpyResponse.append(response.toString())
// .. cpyResponse now has the content of response.
},
Response.ErrorListener { })
{
override fun getHeaders(): MutableMap<String, String> {
val headers = HashMap<String, String>()
headers.put("X-API-KEY", "r0IS395C2D8ITjSKV05F610yPXsDQZjllmprr");
return headers
}
}
queue.add(stringRequest)
Log.d("A", "VK: Response is: " + response) // <<<<-- value of response is gone.. it is an empty string!!!!!! :(
return cpyResponse.toString() // <<< --- cpyResource is also empty string
}
I'm creating a login for my application.
I am currently stuck in posting problems to my API
This is my API that which is made to support login.
{
success: false,
message: "Please provide complete and accurate information.",
data: [ ]
}
fun loginUrlSuccess(urlApi : String) {
Log.d("login", urlApi)
authSignin_cgi = gson.fromJson(urlApi, DtoProfile::class.java)
loginsSuccess = authSignin_cgi.success
val queue = Volley.newRequestQueue(context)
val stringReq = object : StringRequest(Request.Method.POST,urlApi,Response.Listener<String>{ response ->
Log.w("response",response)
Toast.makeText(context,"Loging success..",Toast.LENGTH_SHORT).show()
if (loginsSuccess){
Toast.makeText(context,authSignin_cgi.message,Toast.LENGTH_LONG).show()
} else {
Toast.makeText(context,authSignin_cgi.message,Toast.LENGTH_LONG).show()
}
},Response.ErrorListener { error ->
Log.w("error", error.toString())
Toast.makeText(context, "error..$error",Toast.LENGTH_SHORT).show()
}){
override fun getParams(): MutableMap<String, String> {
val param = HashMap<String, String>()
val userEmail = textEmail.text.toString().trim()
val userPassword = textPassword.text.toString().trim()
param["useremail"] = userEmail
param["userpassword"] = userPassword
return param
}
}
queue.add(stringReq)
}
I get an error from the Logcat screen.
So what do I have to do?
04-04 15:31:43.614 8365-8699/com.example.atimeonlin5 E/Volley: [700] NetworkDispatcher.processRequest: Unhandled exception java.lang.RuntimeException: Bad URL {"success":false,"message":"โปรดระบุข้อมูลให้ถูกต้องครบถ้วน","data":[]}
java.lang.RuntimeException: Bad URL {"success":false,"message":"โปรดระบุข้อมูลให้ถูกต้องครบถ้วน","data":[]}
All right , you should try to construct an Url object instead of type String !
You should use an url (like "http://www.google.com"), not a random string. Your urlApi is not url.
Example from doc:
val textView = findViewById<TextView>(R.id.text)
// ...
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(this)
val url = "http://www.google.com"
// Request a string response from the provided URL.
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
// Display the first 500 characters of the response string.
textView.text = "Response is: ${response.substring(0, 500)}"
},
Response.ErrorListener { textView.text = "That didn't work!" })
// Add the request to the RequestQueue.
queue.add(stringRequest)
I'm doing a post call to my OAUTH2 authentication with Android, trying to recibe my Bearer Token.
I'm using Kotlin language and i'm doing the post request with Volley.
The problem is that when i do my post request with Postman, it work's perfect, but when i do it in the same way using Volley post, my API REST yells: Invalid Request Exception, missing grant type.
And my android yells: Unexpected response code 400 for http://192.168.1.254:8081/oauth/token
Android call:
private fun loginUser() {
var grant_type = "password"
var username = etUsername.text.toString()
var password = etPassword.text.toString()
val credentials = "angularapp"+":"+"12345"
// Post parameters
// Form fields and values
val params = HashMap<String,String>()
params["grant_type"] = grant_type
params["username"] = username
params["password"] = password
val jsonObject = JSONObject(params)
val request = CustomJsonObjectRequestBasicAuth(Request.Method.POST, Network.API_URL_LOGIN,jsonObject,
Response.Listener{ response->
Log.d("RESPONSEEEE", response.toString())
try {
// Parse the json object here
Log.d("Response" ,response.toString())
val intent = Intent(this, PatientsActivity::class.java)
intent.putExtra(Tags.FLOOR.toString(), ((spiFloor?.selectedItemId!!+1)))
startActivity(intent)
}catch (e:Exception){
e.printStackTrace()
}
}, Response.ErrorListener{
Log.d("ERROR", "VOLLEY ERROR")
},credentials
)
// Add the volley request to request queue
VolleySingleton.getInstance(this).addToRequestQueue(request)
}
// Class to make a volley json object request with basic authentication
class CustomJsonObjectRequestBasicAuth(
method:Int, url: String,
jsonObject: JSONObject?,
listener: Response.Listener<JSONObject>,
errorListener: Response.ErrorListener,
credentials:String
)
: JsonObjectRequest(method,url, jsonObject, listener, errorListener) {
private var mCredentials:String = credentials
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers["Content-Type"] = "application/x-www-form-urlencoded"
val auth = "Basic " + Base64.encodeToString(mCredentials.toByteArray(), Base64.NO_WRAP)
headers["Authorization"] = auth
System.out.println(headers.toString())
return headers
}
}
I´m trying to get data from a url inside an AsyncTask but I get an error when creating a new instance of HttpUrlConnection.
Something like this on Java
URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
finally {
urlConnection.disconnect();
}
But I keep getting the error shown below.
class GetWeatherTask : AsyncTast<Void, Void, Void>() {
override fun doInBackground(vararg params: Void?): Void? {
val httpClient = HttpURLConnection();
return null
}
override fun onPreExecute() {
super.onPreExecute()
}
override fun onPostExecute(result: Void?) {
super.onPostExecute(result)
}
}
Cannot access '': it is 'protected/protected and package/' in 'HttpURLConnection' Cannot create an instance of an abstract class
Am I missing something? I tryied to create a class object extending HttpUrlConnection and try to implement the init method but I couldn't
Thanks in advance.
Here is a simplification of the question and answer.
Why does this fail?
val connection = HttpURLConnection()
val data = connection.inputStream.bufferedReader().readText()
// ... do something with "data"
with error:
Kotlin: Cannot access '': it is 'protected/protected and package/' in 'HttpURLConnection'
This fails because you are constructing a class that is not intended to directly be constructed. It is meant to be created by a factory, which is in the URL class openConnection() method. This is also not a direct port of the sample Java code in the original question.
The most idiomatic way in Kotlin to open this connection and read the contents as a string would be:
val connection = URL("http://www.android.com/").openConnection() as HttpURLConnection
val data = connection.inputStream.bufferedReader().readText()
This form will auto close everything when done reading the text or on an exception. If you want to do custom reading:
val connection = URL("http://www.android.com/").openConnection() as HttpURLConnection
connection.inputStream.bufferedReader().use { reader ->
// ... do something with the reader
}
NOTE: the use() extension function will open and close the reader and handle closing on errors automatically.
About the disconnect() method
The docs for disconnect say:
Each HttpURLConnection instance is used to make a single request
but the underlying network connection to the HTTP server may be
transparently shared by other instances. Calling the close() methods
on the InputStream or OutputStream of an HttpURLConnection
after a request may free network resources associated with this
instance but has no effect on any shared persistent connection.
Calling the disconnect() method may close the underlying socket
if a persistent connection is otherwise idle at that time.
So you decide if you want to call it or not. Here is a version of the code that calls disconnect:
val connection = URL("http://www.android.com/").openConnection() as HttpURLConnection
try {
val data = connection.inputStream.bufferedReader().use { it.readText() }
// ... do something with "data"
} finally {
connection.disconnect()
}
The simplest way to do a get post request using HTTPUrlConnection is to create a common helper class that can be called from anywhere in the app to call the GET and POST request methods, without writing the same code again and again.
Below is the helper object (Singleton) class that you can use for the network call for GET and POST requests.
package com.dewari.ajay.androidnetworkcommunication.network
import org.json.JSONObject
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection
object RequestHandler {
const val GET : String = "GET"
const val POST : String = "POST"
#Throws(IOException::class)
fun requestPOST(r_url: String?, postDataParams: JSONObject): String? {
val url = URL(r_url)
val conn: HttpURLConnection = url.openConnection() as HttpURLConnection
conn.readTimeout = 3000
conn.connectTimeout = 3000
conn.requestMethod = POST
conn.doInput = true
conn.doOutput = true
val os: OutputStream = conn.outputStream
val writer = BufferedWriter(OutputStreamWriter(os, "UTF-8"))
writer.write(encodeParams(postDataParams))
writer.flush()
writer.close()
os.close()
val responseCode: Int = conn.responseCode // To Check for 200
if (responseCode == HttpsURLConnection.HTTP_OK) {
val `in` = BufferedReader(InputStreamReader(conn.inputStream))
val sb = StringBuffer("")
var line: String? = ""
while (`in`.readLine().also { line = it } != null) {
sb.append(line)
break
}
`in`.close()
return sb.toString()
}
return null
}
#Throws(IOException::class)
fun requestGET(url: String?): String? {
val obj = URL(url)
val con = obj.openConnection() as HttpURLConnection
con.requestMethod = GET
val responseCode = con.responseCode
println("Response Code :: $responseCode")
return if (responseCode == HttpURLConnection.HTTP_OK) { // connection ok
val `in` =
BufferedReader(InputStreamReader(con.inputStream))
var inputLine: String?
val response = StringBuffer()
while (`in`.readLine().also { inputLine = it } != null) {
response.append(inputLine)
}
`in`.close()
response.toString()
} else {
""
}
}
#Throws(IOException::class)
private fun encodeParams(params: JSONObject): String? {
val result = StringBuilder()
var first = true
val itr = params.keys()
while (itr.hasNext()) {
val key = itr.next()
val value = params[key]
if (first) first = false else result.append("&")
result.append(URLEncoder.encode(key, "UTF-8"))
result.append("=")
result.append(URLEncoder.encode(value.toString(), "UTF-8"))
}
return result.toString()
}
}
Using the above object class you can do your GET and POST requests as shown below:
//As this is network call it should be done in a separate thread
Thread(Runnable {
RequestHandler.requestGET(url)
RequestHandler.requestPOST(url, postJSONObject)
}).start()
Instead of using thread you can also use AsyncTask as followed:
class NetworkAsyncCall(private val context: Context, private val url: String, private val requestType:
String, private val postJSONObject: JSONObject = JSONObject()
) : AsyncTask<String?, String?, String?>() {
override fun doInBackground(vararg p0: String?): String? {
return when (requestType) {
RequestHandler.GET -> RequestHandler.requestGET(url)
RequestHandler.GET -> RequestHandler.requestPOST(url, postJSONObject)
else -> ""
}
}
override fun onPostExecute(s: String?) {
if (s != null) {
Toast.makeText(context, s, Toast.LENGTH_LONG).show()
}
}
}
You can create the asyncTask as a inner class of Activity or a seperate indipendent class.
Now to call the newtwork call via the AsyncTask NetworkAsyncCall in your onCreate() or any function from which you want call the api you can write:
NOTE: The mentioned url will not work so, you have to replace it with your own.
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
// Change the url with your own GET URL request
val urlGET = "http://my-json-feed"
//GET Request
NetworkAsyncCall(this#MainActivity, urlGET, RequestHandler.GET).execute();
// POST Request
// doPost()
}
For POST request you can call:
private fun doPost() {
// Change the url with your own POST URL request
val urlPOST = "http://my-json-feed"
val postDataParams = JSONObject()
postDataParams.put("name", "Ajay")
postDataParams.put("email", "aj****ri#gmail.com")
postDataParams.put("phone", "+91 78******25")
NetworkAsyncCall(this#MainActivity, urlPOST, RequestHandler.POST, postDataParams).execute()
}
you can check the complete code in github here.
For a good explanation you can check this link.
the advantage of using the NetworkAsyncCall as seperate indipendent class is you don't have to write the AsyncTask code again, just call the same AsyncTask NetworkAsyncCall with a new object from different activitys/functions, however with this you have to implement a listener interface that you will require for the callback on onPostExecute() after getting the response from the api and to return back the response to the activity you have to perform the callback using that interface.
You can adaptaion your code; Don't forget user Runnable thread.
Thread(Runnable {
try {
val url = URL("www.android.com")
val con = url.openConnection() as HttpURLConnection
val datas = con.inputStream.bufferedReader().readText()
val json = JSONObject(datas)
val blockList = json.getJSONObject("blockList")
val warning = json.get("warnMessage").toString()
val keys = blockList.keys()
var permission = HashMap<String, Array<String?>>()
while (keys.hasNext()) {
val key = keys.next()
val kods = blockList.getJSONArray(key)
val permissonArray = arrayOfNulls<String>(kods.length())
for (i in permissonArray.indices) {
permissonArray[i] = kods.getString(i)
}
permission[key] = permissonArray;
}
} catch (ex: Exception) {
Log.d("Exception", ex.toString())
}
}).start()