I work on developing an android app and I would like to make a generic function of volley post request, I write my function as bellow:
public fun <T> push(context: Context, url: String, myObject: T, completion: (response: String) -> Unit) {
val queue = Volley.newRequestQueue(context)
val sr = object : StringRequest(
Method.POST, url,
Response.Listener { response ->
println(response)
completion(response)
},
Response.ErrorListener { volleyError ->
Common.showVolleyError(volleyError, context)
}) {
override fun getParams(): Map<String, String> {
val params = myObject as HashMap<String, String>
return params
}
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val params = HashMap<String, String>()
params["Content-Type"] = "application/x-www-form-urlencoded"
params["X-Requested-With"] = "XMLHttpRequest"
return params
}
}
sr.retryPolicy = DefaultRetryPolicy(
0,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
)
queue.add(sr)
}
What I enforce is How to convert my serializable object to a HashMap<String, String>(), i.e. How to bind myObject to getParams() function,
Make a base class includes an abstract method returns Map<String, String> named for example getConvertedParams. This method should convert itself to Map<String, String> like:
val params = HashMap<String, String>()
params["attribute1"] = attribute1
params["attribute2"] = attribute2
...
return params
Every request object should extends that base class and override that method. In getParams where you send request, call getConvertedParams for your generic request object.
override fun getParams(): Map<String, String> {
val params = myObject.getConvertedParams()
return params
}
Also do not forget to change the method signature
public fun <BaseClassName> push(context: Context, url: String, myObject: BaseClassName, completion: (response: String) -> Unit)
Finally for any one may like to use this way, I rewrite function as below:
public fun <T> push(context: Context, url: String, myObject: T,myObjectType : Array<Field>, completion: (response: String) -> Unit) {
val myObjectAsDict = HashMap<String, String>()
val allFields = myObjectType //:Array<Field> = myObjectType!!::class.java.declaredFields
for ( field in allFields) {
if (!field.isAccessible) {
field.isAccessible = true
}
val value = field.get(myObject)
if (value != null)
{
if( field.name != "serialVersionUID") {
myObjectAsDict[field.name] = value.toString()
}
}
}
println(myObjectAsDict)
val queue = Volley.newRequestQueue(context)
val sr = object : StringRequest(
Method.POST, url,
Response.Listener { response ->
println(response)
completion(response)
},
Response.ErrorListener { volleyError ->
Common.showVolleyError(volleyError, context)
}) {
override fun getParams(): Map<String, String> {
val params = myObjectAsDict
return params
}
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val params = HashMap<String, String>()
params["Content-Type"] = "application/x-www-form-urlencoded"
params["X-Requested-With"] = "XMLHttpRequest"
return params
}
}
sr.retryPolicy = DefaultRetryPolicy(
0,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
)
queue.add(sr)
}
And using of it As below:
var myClass = MyClass()
VolleyFunctions.push(this,"URL",myClass, MyClass::class.java.declaredFields)
{
response->
myClass = Gson().fromJson(response, MyClass::class.java)
println("myClass.Name${myClass.name}")
}
Thanks faranjit for your answer and yours comments.
Related
I'm new in JUnit with Mock. This is actual working but I notice receiving null value in TEST using Map<String, Object>
The reason for this what if their is condition must not null. Someday I'm going to need this.
What I did was since Map<String, Object> can't be implemented in headers.setAll() because it need Map<String, String> so i convert it.
#RestController
public class TestController {
#GetMapping("/test")
public String testAuth(#RequestParam String name, #RequestHeader Map<String, Object> head) {
try {
String conv = String.valueOf( head.get("head"));
return name + " "+ conv ;
} catch (Exception e) {
return e.getMessage();
}
}
}
Test
#WebMvcTest(controllers = TestController.class)
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testControllerHeader() throws Exception {
Map<String, Object> headerMap = new HashMap<String, Object>();
Map<String, String> headObjectToString = headerMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));
HttpHeaders headers = new HttpHeaders();
headers.setAll(headObjectToString);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders
.get("/test?name=justin")
.headers(headers)
.accept(MediaType.APPLICATION_JSON)
).andExpect(status().isOk()).andReturn();
//i make it error just to see the body
assertEquals(result.getResponse().getStatus(), 201);
//this the original
//assertEquals(result.getResponse().getStatus(), 200);
}
}
Body = justin null
MockHttpServletRequest:
HTTP Method = GET
Request URI = /test
Parameters = {name=[justin]}
Headers = [Accept:"application/json"]
Body = null
Session Attrs = {}
Handler:
Type = com.mock.TestingMokito.Controller.TestController
Method = com.mock.TestingMokito.Controller.TestController#testAuth(String, Map)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json", Content-Length:"10"]
Content type = application/json
Body = justin null
Forwarded URL = null
Redirected URL = null
Cookies = []
You're passing null in the header, that's why it is returning null.
In your test class, add some data in the headerMap before converting it to a string.
#WebMvcTest(controllers = TestController.class)
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testControllerHeader() throws Exception {
Map<String, Object> headerMap = new HashMap<String, Object>();
headerMap.put("head",new String("This was the bug"));
Map<String, String> headObjectToString = headerMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));
HttpHeaders headers = new HttpHeaders();
headers.setAll(headObjectToString);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders
.get("/test?name=justin")
.headers(headers)
.accept(MediaType.APPLICATION_JSON)
).andExpect(status().isOk()).andReturn();
//i make it error just to see the body
assertEquals(result.getResponse().getStatus(), 201);
//this the original
//assertEquals(result.getResponse().getStatus(), 200);
}
}
I’m busting my head with spring webflux comprehension.
I'd like, to retrieve the body error (String format)
to fill a pojo declared out of the scope of the webflux method.
How can we do that ?
ExtraScope extraScope = new ExtraScope;
Map<String, Object> vars = new HashMap<>();
vars.put("instrumentId", instrument);
RequestHeadersSpec<?> request = mdiWebclient
.get()
.uri(mdiEndpointsGetQuotations, vars)
.header(HttpHeaders.AUTHORIZATION, token)
.accept(MediaType.APPLICATION_JSON);
InstrumentDetailsDto block = request.exchangeToMono(r -> {
if (r.statusCode() != HttpStatus.OK) {
r.bodyToMono(String.class).subscribe(errorBody -> {
logger.debug("errorBody: [{}] ", errorBody);
extraScope.setBody(errorBody);
});
return Mono.empty();
}
return r.bodyToMono(InstrumentDetailsDto.class);
})
.block(Duration.ofMinutes(5));
System.out.println("content of ExtraScope pojo: " + extraScope.getBody());
can you see in the else statement ?
how to do this ?
Today I'm dealing with an issue creating a client to consume a restful service with a Bearer JWT. After appying the swagger codegen maven plugin I got the following GET operation what I need to consume in my new service:
public String getRelationByCucoIdUsingGET(Integer cucoId, String GS_AUTH_TOKEN) throws RestClientException {
Object postBody = null;
// verify the required parameter 'cucoId' is set
if (cucoId == null) {
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'cucoId' when calling getRelationByCucoIdUsingGET");
}
// create path and map variables
final Map<String, Object> uriVariables = new HashMap<String, Object>();
uriVariables.put("cucoId", cucoId);
String path = UriComponentsBuilder.fromPath("/getRelationByCucoId/{cucoId}").buildAndExpand(uriVariables).toUriString();
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
if (GS_AUTH_TOKEN != null)
headerParams.add("GS-AUTH-TOKEN", apiClient.parameterToString(GS_AUTH_TOKEN));
final String[] accepts = {
"application/json"
};
final List<MediaType> accept = apiClient.selectHeaderAccept(accepts);
final String[] contentTypes = { };
final MediaType contentType = apiClient.selectHeaderContentType(contentTypes);
String[] authNames = new String[] { };
ParameterizedTypeReference<String> returnType = new ParameterizedTypeReference<String>() {};
return apiClient.invokeAPI(path, HttpMethod.GET, queryParams, postBody, headerParams, formParams, accept, contentType, authNames, returnType);
}
The model class is as follow:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class CuCoPerson {
private Integer cucoId;
private List<CustomerRelation> customers;
}
And last, I have done this service but it doesnt work and I don't know how to create the controller to use this service.
#Service
public class CustomerRelationService {
RestControllerApi restControllerApi = new RestControllerApi();
public void getCustomers(Integer cudoId, Principal auth) {
restControllerApi.getRelationByCucoIdUsingGET(cudoId, auth.getName());
}
How can I pass a generic class to GsonRequest class? Here is my api's response model.
class APIResponse<T> {
var status:Boolean
var data:T?
var collection: ArrayList<T>?
var message:String?
constructor(status:Boolean, data:T?, collection: ArrayList<T>?, message:String?){
this.status = status
this.data = data
this.collection = collection
this.message = message
}
}
And these are the kotlin models of tables in my database. There are used for converting json to models.
class ControlCategory {
var id:Int = 0
var name:String? = null
var control_points:ArrayList<ControlPoint> = ArrayList()
constructor(id:Int, name:String, control_points:ArrayList<ControlPoint>) {
this.id = id
this.name = name
this.control_points = control_points
}
}
class ControlPoint {
var id:Int
var name:String
var control_category: ControlCategory
constructor(id:Int, name:String, control_category:ControlCategory) {
this.id = id
this.name = name
this.control_category = control_category
}
}
Here is my GsonRequest class.
open class GsonRequest<T>(
url: String,
private val clazz: Class<T>,
private val headers: MutableMap<String, String>?,
private val listener: Response.Listener<T>,
errorListener: Response.ErrorListener
) : Request<T>(Method.GET, url, errorListener) {
protected val gson = Gson()
override fun getHeaders(): MutableMap<String, String> = headers ?: super.getHeaders()
override fun deliverResponse(response: T) = listener.onResponse(response)
override fun parseNetworkResponse(response: NetworkResponse?): Response<T> {
return try {
val json = String(
response?.data ?: ByteArray(0),
Charset.forName(HttpHeaderParser.parseCharset(response?.headers)))
Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response))
} catch (e: UnsupportedEncodingException) {
Response.error(ParseError(e))
} catch (e: JsonSyntaxException) {
Response.error(ParseError(e))
}
}
}
How can I pass APIResponse<ControlCategory> to GsonRequest class' :
val controlPointsRequest = GsonRequest<APIResponse<ControlCategory>>(
getString(R.string.apiUrl)+"control-categories",
object: TypeToken<APIResponse<ControlCategory>>(){}.type,
headers,
listener,
error
);
When I pass object: TypeToken<APIResponse<ControlCategory>>(){}.type it gives error.
I want to get data from php file after putting the value m (String) in JSONObject and give result in TextView i.e. mResultTextView. However, this is giving me the following error.
com.android.volley.parseerror org.json.jsonexception value yes of type
java.lang.String cannot be converted to JSONObject
var m = barcode.displayValue.toString()
val jsonobj = JSONObject()
jsonobj.put("email", m)
val url = "http://192.168.1.106/verf1.php"
val que = Volley.newRequestQueue(this#MainActivity)
val req = JsonObjectRequest(Request.Method.POST, url, jsonobj, Response.Listener {
response -> mResultTextView.text = response.toString()
},Response.ErrorListener {
response -> mResultTextView.text = response.toString()
})
que.add(req)
You need to set the content-type to application/json in the header for your POST request. For adding a custom header along with your POST request you might consider taking a look at this tutorial.
I am copying the code example from the tutorial from the link above.
class ServiceVolley : ServiceInterface {
val TAG = ServiceVolley::class.java.simpleName
val basePath = "https://your/backend/api/"
override fun post(path: String, params: JSONObject, completionHandler: (response: JSONObject?) -> Unit) {
val jsonObjReq = object : JsonObjectRequest(Method.POST, basePath + path, params,
Response.Listener<JSONObject> { response ->
Log.d(TAG, "/post request OK! Response: $response")
completionHandler(response)
},
Response.ErrorListener { error ->
VolleyLog.e(TAG, "/post request fail! Error: ${error.message}")
completionHandler(null)
}) {
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers.put("Content-Type", "application/json")
return headers
}
}
BackendVolley.instance?.addToRequestQueue(jsonObjReq, TAG)
}
}