Java – spring general rest controller: parsing request body
I have the following controllers:
@RestController @RequestMapping(value = "/{entity}",produces = MediaType.APPLICATION_JSON_VALUE) public class CrudController<T extends SomeSuperEntity> { @RequestMapping(method = GET) public Iterable<T> findAll(@PathVariable String entity) { } @RequestMapping(value = "{id}",method = GET) public T findOne(@PathVariable String entity,@PathVariable String id) { } @RequestMapping(method = POST) public void save(@PathVariable String entity,@RequestBody T body) { } }
The somesuperentity class looks like:
public abstract class SomeSuperEntity extends AbstractEntity { // some logic }
And abstractentity. Its abstract class has some fields:
public abstract class AbstractEntity implements Comparable<AbstractEntity>,Serializable { private Timestamp firstField; private String secondField; public Timestamp getFirstField() { return firstField; } public void setFirstField(Timestamp firstField) { this.firstField = firstField; } public String getSecondField() { return secondField; } public void setSecondField(String secondField) { this.secondField = secondField; } }
All subclasses of somesuperentity - simple JavaBeans If you use the findall () and findone (ID) methods – everything is fine I create an entity in the service layer, which returns to the client in JSON, and all fields are declared in subclasses and abstractentity
However, when I try to get the request body in the save (entity, body), I get the following error:
If I remove the abstraction from somesuperentity, everything is fine, but I request the body. I only get the fields declared in abstractentity
This is my question. Is there a solution to this kind of problem in my case? If not, what would be the best solution without any structural changes (making sub controllers for each entity is not an option)? Is it a good idea to use the search body as plain text? Or would it be better to use a map for this?
I use spring v4 2.1 and Jackson 2.6 3 as a converter
There is some information about the general controller, but I can't find anything covering me So please navigate without repeating the question
Thank you in advance
UPD: at present, its work is as follows: I added additional checks in messageconverter and defined @ requestbody as string
@Override public Object read(Type type,Class<?> contextClass,HttpInputMessage inputMessage) throws IOException,HttpMessageNotReadableException { if (IGenericController.class.isAssignableFrom(contextClass)) { return CharStreams.toString(new InputStreamReader(inputMessage.getBody(),getCharset(inputMessage.getHeaders()))); } return super.read(type,contextClass,inputMessage); }
Then, in the service layer, I define the receiving entity (in ordinary JSON) and transform it:
final EntityMetaData entityMetadata = getEntityMetadataByName(alias); final T parsedEntity = getGlobalGson().fromJson(entity,entityMetadata.getEntityType());
Where entitymetadata is an enumeration with defined relationships between entity aliases and classes Alias from @ pathvariable
Solution
What spring really sees is:
public class CrudController { @RequestMapping(method = GET) public Iterable<Object> findAll(@PathVariable String entity) { } @RequestMapping(value = "{id}",method = GET) public Object findOne(@PathVariable String entity,@PathVariable String id) { } @RequestMapping(method = POST) public void save(@PathVariable String entity,@RequestBody Object body) { } }
For the returned object, it doesn't matter, because Jackson will generate JSON with correct output anyway, but it seems that spring can't handle the incoming object in the same way
You can try to replace generics with somesuperentity and check the spring @ requestbody containing a list of different types (but same interface)