How to POST/PUT on a Map<String,Entity> collection association - java

Maybe a dumb question but how to update the content of a typed Map collection association of an exposed entity with Spring Data Rest using POST/PUT requests ?
I know how to POST/PUT on List or Set associations using Content-Type: text/uri-list but i don't with key/value Map<>
EDIT
I tried to send a ressource URI on the body of a PUT request with Content-Type: text/uri-list on the association endpoint. I gave no error, but it replaced all the previous content of the Map<> by this :
{
self: { ENTITY content}
}
So it seems to be supported (a side effect ?). It named the key self.
I don't know how to specify the key.

#WebService
#Path("/test")
public class TestResource {
#GET
#Produces(APPLICATION_JSON)
public HashMap<String, String> findAll() {
HashMap<String, String> test = new HashMap<>();
test.put("key1", "value1");
test.put("key2", "value2");
return test;
}
This produces JSON at the front end:
{"key2":"value2","key1":"value1"}
However this does use application/json content type which may not be what you are after?

Related

How to pass a list of integers as json data to a Spring Boot rest API without a custom intermediate object?

How to pass a list of integers as json data to a Spring Boot rest API without creating a custom intermediate object? I know you can create a DTO sort of object to map the request and I've done that successfully but is there a way to receive it as a collection/array of IDs without having to create a class just for this purpose? I tried to do it without any luck. Also tried to map it to a collection of the Entity the IDs refer to also without any luck. A way to do this, or alternatives that are more idiomatic to Spring Boot would be welcomed.
Sample of request body that's being sent to the endpoint:
{
"products": [
"1",
"2"
]
}
It seems answer of fps should be suitable, but here one caveat
Even if your class is annotated as #RestController, you need to use #RequestBody when using Map as input (with POJO this annotation can be avoided)
Bellow code works fine:
#PostMapping("/products")
#ResponseStatus(value = HttpStatus.OK)
public Object createProducts(#RequestBody Map<String, List<String>> input) {
return input;
}
More agile way is using Jackson classes.
It allows to use not only Strings, but all "native" json structures.
You can check JsonNode.isNumber(), JsonNode.isTextual(), JsonNode.isArray(), JsonNode.isObject() and so on.
#PostMapping("/products")
#ResponseStatus(value = HttpStatus.OK)
public Object createProducts(#RequestBody Map<String, JsonNode> input) {
return input;
}

How to map dynamic query parameters in Spring Boot RestController

Is it possible to map query parameters with dynamic names using Spring Boot? I would like to map parameters such as these:
/products?filter[name]=foo
/products?filter[length]=10
/products?filter[width]=5
I could do something like this, but it would involve having to know every possible filter, and I would like it to be dynamic:
#RestController
public class ProductsController {
#GetMapping("/products")
public String products(
#RequestParam(name = "filter[name]") String name,
#RequestParam(name = "filter[length]") String length,
#RequestParam(name = "filter[width]") String width
) {
//
}
}
If possible, I'm looking for something that will allow the user to define any number of possible filter values, and for those to be mapped as a HashMap by Spring Boot.
#RestController
public class ProductsController {
#GetMapping("/products")
public String products(
#RequestParam(name = "filter[*]") HashMap<String, String> filters
) {
filters.get("name");
filters.get("length");
filters.get("width");
}
}
An answer posted on this question suggests using #RequestParam Map<String, String> parameters, however this will capture all query parameters, not only those matching filter[*].
You can map multiple parameters without defining their names in #RequestParam using a map:
#GetMapping("/api/lala")
public String searchByQueryParams(#RequestParam Map<String,String> searchParams) {
...
}
Does matrix variables work for you? If I understand you correctly, can be like this:
// GET /products/filters;name=foo;length=100
#GetMapping("/products/filters")
public void products(
#MatrixVariable MultiValueMap matrixVars) {
// matrixVars: ["name" : "foo", "length" : 100]
}
This seems like a solvable problem. The solutions are not ideal far as I know, but there are ways.
A previous attempt seemed bent on finding a perfect solution where the entire composition of the filter was known in-transit.
Spring MVC populate
The entirety of the dynamic criteria that user defines can be transmitted with some basic scheme you define, as one key=value parameter from the client, then decomposed into its elements once it is received.
You could also send two parameters: "fields" and "values", where the lists of each are encoded in there respectively, with some cautious delimiter of your choosing (could be an encoded special character that the user cannot physically type, perhaps).
You still need, as with everything other approach where the client side is submitting criteria (like filter criteria), full protection from any malicious use of the parameters, just as the client trying to embed SQL criteria in them (SQL Injection).
But so long as the client code follows the agreed syntax, you can receive any number of dynamic parameters from them in one shot.
Client:
/products?filter=field1--value1||field2--value2||field3--value3...
That is a simplified example showing delimiters that are too easy to "break", but the idea is some simple, even fully readable (no harm in doing so) scheme just for the purpose of packing your field names and values together for easy transit.
Server:
#RequestMapping(value = "/products", method = RequestMethod.GET)
public String doTheStuff(#RequestParam(value = "filter") String encodedFilter) {
.. decompose the filter here, filter everything they've sent for disallowed characters etc.

Define Enunciate response of Map<String, String>

The closest response data type description I can get is
map of object (JSON)
with a response example of
...
when I use the annotation
#TypeHint(Map.class)
Ideally I need to specify a response type of Map<String, String>, HashMap<String, String>, or something that would provide a response data type that makes sense and a response example similar to
{
"...": "...",
"...": "..."
}
Found a solution using the #requestExample JavaDoc Tag detailed in the Enunciate documentation.
For example
#responseExample application/json {"..." : "..."}
Gives a proper response example of
{
"...": "..."
}
Using the #TypeHint(Object.class) annotation produces a response data type of object (JSON), which technically makes sense, so this solution is sufficient.

Read Map entity in JAX-RS client

I have a webservice which returns a "Map", I am trying to read this object from the Response(javax.ws.rs.core).
something like this:
ex : Map<String, Object> temp = response.readEntity(Map.class)
but this doesn't seem to work.
My question is how do I read a Map entity from a response object?
Found a way to read the Map entity from response.I guess I needed to provide the implementation class for Map.
response.readEntity(new GenericType<HashMap<String, Object>>() { });

Jersey, JSR 303 validation - custom "path" and "invalidValue" in ValidationError

I am using Jersey (JAX-RS) and I'm trying to implement a validation. I have a problem with a response returned by my application when a validation error occurs. Now the response looks like this:
[{
"message": "Custom message",
"messageTemplate": "{custom.message.template}",
"path": "SomeJerseyResource.resourceMethod.arg0.names[0]",
"invalidValue":"[value1, value2]"
}]
where "SomeJerseyResourceClass.resourceMethod" is a JAX-RS resource:
public class SomeJerseyResource {
#POST
#Path("/path")
public Response resourceMethod(#Valid RequestModel request) {
/** method body **/
}
}
and validation constraint is assigned to a getter in RequestModel:
public class RequestModel {
private List<String> names = new ArrayList<>();
#MyConstraint
public List<String> getNames() {
return tags;
}
}
I have a custom ConstraintValidator, where I validate each element of that List.
Problem
I don't want to include resource and method name in "path" field of the response. Instead of
SomeJerseyResource.resourceMethod.arg0.names[0] I want arg0.names[0] only. Client doesn't know about server classes and methods, and he wouldn't be able to properly assign errors to fields when he receives response like that.
I want to customize "invalidValue" field of a response. More specifically, to have only invalid element value, not the whole list in that field.
I didn't find any easy way to do that. Do you have any ideas?
You can just write an ExceptionMapper<ConstraintViolationException> to return the Response of your liking. Jersey uses an ExceptionMapper<ViolationException>. ConstraintViolationException extends from ViolationException, so you're mapper is more specific, and would take precedence in the choosing of the mapper. Jersey's mapper, returns the response as a ValidationError, that's why the body is how it is. But you can make it whatever you want.
If you just want the invalidValue list, then just iterate through the ConstraintViolations from ContraintViolationException.getConstraintViolations(), and get the invalidValue from the ConstraintViolation.

Categories

Resources