How to send a List<Map<String, String>> as parameter for a GET API - 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 :)

Related

Extracting Nested POJO Object with Rest-Assured

I'm writing some tests using rest-assured and its Kotlin extensions to test some simple Spring MVC endpoints. I'm trying to understand how to extract values.
One endpoint returns a BookDetailsView POJO, the other returns a Page<BookDetailsView> (where Page is an interface provided by Spring for doing paging).
BookDetailsView is a really simple Kotlin data class with a single field:
data class BookDetailsView(val id: UUID)
For the single object endpoint, I have:
#Test
fun `single object`() {
val details = BookDetailsView(UUID.randomUUID())
whenever(bookDetailsService.getBookDetails(details.id)).thenReturn(details)
val result: BookDetailsView = Given {
mockMvc(mockMvc)
} When {
get("/book_details/${details.id}")
} Then {
statusCode(HttpStatus.SC_OK)
} Extract {
`as`(BookDetailsView::class.java)
}
assertEquals(details.id, result.id)
}
This works as expected, but trying to apply the same technique for the Page<BookDetailsView> runs afoul of all sorts of parsing challenges since Page is an interface, and even trying to use PageImpl isn't entirely straightforward. In the end, I don't even really care about the Page object, I just care about the nested list of POJOs inside it.
I've tried various permutations like the code below to just grab the bit I care about:
#Test
fun `extract nested`() {
val page = PageImpl(listOf(
BookDetailsView(UUID.randomUUID())
))
whenever(bookDetailsService.getBookDetailsPaged(any())).thenReturn(page)
val response = Given {
mockMvc(mockMvc)
} When {
get("/book_details")
} Then {
statusCode(HttpStatus.SC_OK)
body("content.size()", `is`(1))
body("content[0].id", equalTo(page.first().id.toString()))
} Extract {
path<List<BookDetailsView>>("content")
}
println(response[0].javaClass)
}
The final println spits out class java.util.LinkedHashMap. If instead I try to actually use the object, I get class java.util.LinkedHashMap cannot be cast to class BookDetailsView. There are lots of questions and answers related to this, and I understand it's ultimately an issue of the underlying JSON parser not knowing what to do, but I'm not clear on:
Why does the "simple" case parse without issue?
Shouldn't the type param passed to the path() function tell it what type to use?
What needs configuring to make the second case work, OR
Is there some other approach for grabbing a nested object that would make more sense?
Digging a bit into the code, it appears that the two cases may actually be using different json parsers/configurations (the former seems to stick to rest-assured JSON parsing, while the latter ends up in JsonPath's?)
I don't know kotlin but here is the thing:
path() doesn't know the Element in your List, so it'll be LinkedHashMap by default instead of BookDetailsView.class
to overcome it, you can provide TypeReference for this.
java example
List<BookDetailsView> response = ....then()
.extract().jsonPath()
.getObject("content", new TypeRef<List<BookDetailsView>>() {});
kotlin example
#Test
fun `extract nested`() {
var response = RestAssured.given().get("http://localhost:8000/req1")
.then()
.extract()
.jsonPath()
.getObject("content", object : TypeRef<List<BookDetailsView?>?>() {});
println(response)
//[{id=1}, {id=2}]
}

Ktor Client, how to specify body parameters

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

C# removes duplicates in JSON request body

I've this endpoint:
[HttpPost]
[ODataRoute("some\odata\route")]
public async Task<HttpResponseMessage> Func_Name(Request_Type request)
{
...
}
request is IDictionary< string, string>. If user calls this endpoint with a JSON containing duplicates:
{
"Bob": "Doctor",
"Tim": "Engineer",
"Bob": "Sailor"
}
What I see in C# is:
{
"Tim": "Engineer",
"Bob": "Sailor"
}
Means, it always takes the last. How can I stop this automatic removal and see the duplicates after deserialization? Or making the endpoint fail on calls like these?
If you remove your parameter of your Post method you can try the following. Otherwise C# will try to automatically map the entity which will lead to an error.
var rawJson = await this.Request.Content.ReadAsStringAsync();
That will get you the raw JSON which will not be validated and hold the three properties: two of which have the "Bob" key. However I would consider this a bad idea and I'm puzzled by your use case.
C# cannot possibly convert your JSON to a Dictionary<string, string> seeing as the Key property of a Dictionary must always be unique. Needless to say that the JSON is also considered invalid when run through a validator.
{
"Bob": "Doctor",
"Tim": "Engineer",
"Bob": "Sailor"
}
I ended up switching to List instead of Dictionary

JAX-RS Option path param is not working

I am trying to use following construct
#ApplicationPath("app")
#Path("api/{userid}/model")
public class ModelService
{
#Get
#Path("{modelid: (.*)?}")
public Response removePreProcessor(#PathParam("userid") String sUserId, #PathParam("preprocessorid") String sPreProcessorId)
{
return Response.build();
}
}
I can not access both following REST URL
GET http://localhost:8080/XXXX/app/api/xyz/model
GET http://localhost:8080/XXXX/app/api/xyz/model/123
Let me know what is a wrong I am doing
-Thanks in advance
As I read your question, several things look strange to me but they may be context related.
One thing though seems wrong :
You are using a #PathParam("preprocessorid") but I can't see this param in your path.
Do you have any logs ?

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.