6

My question is almost 1:1 as this one. The only difference (and struggle) I have is that my "data container" has a collection of objects. It looks like this:

public class A {
    int plainFieldA;
    B fieldB;
    List<B> collectionB = new ArrayList<>();
}

public class B {
    int plainFieldB;
}

@Transactional(readOnly = true)
@GetMapping("")
public Entity getAll(A reqParam) {
    return getAll(reqParam);
}

Is it possible to define collectionB in params of the url http://localhost/api/test?plainFieldA=1 without creating a converter ? @GameSalutes correctly pointed out that since spring 4 we can do fieldB.plainFieldB=2 so the url will be: http://localhost/api/test?plainFieldA=1&fieldB.plainFieldB=2 but the question is can we do soemthing similar with collectionB without creating a converter ?

user3529850
  • 1,632
  • 5
  • 32
  • 51
  • 1
    That's a good question. I would assume it's not possible, as there would be a problem of representation of values in a list. How would you represent them? collectionB[0].plainFieldB? And if you have 100 values? Don't forget that a ``Get`` mapping relies on the http ``GET`` method and there is a limit to the size of a QueryString. – Filippo Possenti Dec 12 '19 at 10:10
  • I would choose to make a `POST` call for such a scenario because of the limit in character size for `GET` method – Naveen Kulkarni Dec 12 '19 at 10:48
  • 1
    Looks like you are trying to implement search to search something based on different parameters. If you want to implement search based on different parameters then I will recommend you to use "Interpreter pattern" without worrying on additional handling. https://en.wikipedia.org/wiki/Interpreter_pattern – Gul Ershad Dec 12 '19 at 10:59

2 Answers2

6

Yes, you can make the request like this:

http://localhost/api/test?plainFieldA=1&fieldB.plainFieldB=2&collectionB[0].plainFieldB=9

Or encoding the request, for postman:

http://localhost/api/test?plainFieldA=1&fieldB.plainFieldB=2&collectionB%5B0%5D.plainFieldB=9

With two objects in request:

http://localhost/api/test?plainFieldA=1&fieldB.plainFieldB=2&collectionB%5B0%5D.plainFieldB=9&collectionB%5B1%5D.plainFieldB=11

The result with breakpoint in the IDE:

enter image description here

Francesc Recio
  • 2,187
  • 2
  • 13
  • 26
  • Oh, well.. so long to my "I would assume it's not possible". I even literally wrote the syntax to use. :D Well deserved bounty, if the answer gets accepted! – Filippo Possenti Dec 12 '19 at 15:20
  • That's perfect. Thank you. I clicked bounty button but it says "You may award your bounty in 14 hours" (and can't be transfered). So I will do that in 14 hours. Thank you. – user3529850 Dec 12 '19 at 19:06
  • Do you recomend `POST` over `GET` when it comes to searching by given criteria ? – user3529850 Dec 13 '19 at 10:06
  • Thank you! Yes, I recommend in this case POST over GET, in case you send more complex objects than a simple key/value. If one day you have a list with many elements, GET will bring you problems and also at design level you are sending an object. – Francesc Recio Dec 13 '19 at 10:18
0

Accepted answer works like a charm but I'm adding this answer as an alternative if someone reaches url size limit or some other reason.

Following this thread advice, here is how I've done.

  • Frontend: stringify your object than encode it in base64 for submission.
  • Backend: decode base64 string then convert the string json into desired object.

It isn't the best for debugging your API with postman but it is working as expected for me.

Original object: { page: 1, size: 5, filters: [{ field: "id", value: 1, comparison: "EQ" }

Encoded object: eyJwYWdlIjoxLCJzaXplIjo1LCJmaWx0ZXJzIjpbeyJmaWVsZCI6ImlkUGFyZW50IiwiY29tcGFyaXNvbiI6Ik5VTEwifV19

@GetMapping
fun list(@RequestParam search: String?): ResponseEntity<ListDTO> {
    val filter: SearchFilterDTO = decodeSearchFieldDTO(search)
    ...
}

private fun decodeSearchFieldDTO(search: String?): SearchFilterDTO {
    if (search.isNullOrEmpty()) return SearchFilterDTO()
    return Gson().fromJson(String(Base64.getDecoder().decode(search)), SearchFilterDTO::class.java)
}

And here the SearchFilterDTO and FilterDTO

class SearchFilterDTO(
    var currentPage: Int = 1,
    var pageSize: Int = 10,
    var sort: Sort? = null,
    var column: String? = null,
    var filters: List<FilterDTO> = ArrayList<FilterDTO>(),
    var paged: Boolean = true
)

class FilterDTO(
    var field: String,
    var value: Any,
    var comparison: Comparison
)
Gabriel Brito
  • 1,003
  • 2
  • 16
  • 26