JsonUnwrapped to deserialize GET request parameters - spring

I have the following:
#RestController
public class MyController {
#PostMapping
MyDto test(#RequestBody MyDto myDto) {
return myDto;
}
#GetMapping
MyDto test2(MyDto myDto) {
return myDto;
}
#Data
static class MyDto {
private String a;
#JsonUnwrapped
private MySecondDto secondDto;
#Data
static class MySecondDto {
private String b;
}
}
}
However:
GET http://localhost:8080?a=a&b=b
returns
{
"a": "a"
}
while
POST http://localhost:8080
{
"a": "a",
"b": "b"
}
returns
{
"a": "a",
"b": "b"
}
so it looks like #JsonUnwrapped and GET mapped Pojos don't work together as expexted.
Any hint on how to use complex nested Pojos to accomodate GET request params ?

Related

Overloading SpringBoot #PostMapping controller method not working

I have faced some challenge and to describe shortly I created test application. Code you can see and error you can see below.
#RestController
public class TestController {
#PostMapping(value = "/test",params = { "a", "b" })
public String test(#RequestPart MultipartFile a, #RequestPart(required = false) MultipartFile b) {
return "test1";
}
#PostMapping(value = "/test", params = { "b" })
public String test(#RequestPart MultipartFile b) {
return "test2";
}
}
I`m trying to execute this request from postman:
And I`m getting such error in logs:
Resolved [org.springframework.web.bind.UnsatisfiedServletRequestParameterException:
Parameter conditions "a, b" OR "b" not met for actual request parameters: ]
The thing is, if I will put parameters also in postman (not in body, in request url: localhost:8080/test?b=anything) it will work fine, but I don`t need request params in url.
Is there some possible way to make it work?
I am able to override #PostMapping. But the type of the parameter should be different.
#PostMapping(value="/test" )
public String testApi(#ModelAttribute MultipartDTO multipartDTO) {
return "test1";
}
#PostMapping(value="/test" ,params = { "b" })
public String test(#RequestParam String b) {
return "test2";
}
/** DTO **/
#Data
public class MultipartDTO{
private MultipartFile a;
private MultipartFile b;
}
you can not map the same signature twice which contains the same Http methods then below error will occur.
java.lang.IllegalStateException: Ambiguous handler methods
try this one
#RestController
public class TestController {
#PostMapping("/test")
public String test(#RequestParam MultipartFile a, #RequestParam(required = false) MultipartFile b) {
return "test1";
}
#PostMapping("/test2")
public String test(#RequestParam MultipartFile b) {
return "test2";
}
}
You should try something like below.
#RestController
public class TestController {
#PostMapping(value = "/test")
public String test(#RequestParam MultipartFile a, #RequestParam(required = false) MultipartFile b) {
return "test1";
}
#PostMapping(value = "/test")
public String test(#RequestParam MultipartFile b) {
return "test2";
}
}

Get the value of multiple map (Map inside of map) from postman

How can get the value of multiple key values (Map inside of map) from the postman
{
"message_key": {
"device_id": "12548652",
"message": "Y5482lsdfkOjEyNDUysdfsdfMTc1sdfOTM3MjU=",
"messageType": "Text"
}
}
Actually I want to bind value of message_key with domain to validate every properties.
I have found the answer e.g:
DTO:
public #Data class MessageKey {
#JsonProperty("device_id")
private String deviceId;
#JsonProperty("message")
private String message;
#JsonProperty("messageType")
private String messageType;
}
Controller:
public void test(#RequestBody Map<String, MessageKey> bodyParameters) {
MessageKey messageKey = bodyParameters.get("message_key");
System.out.println(messageKey);
}

Spring RepositoryRestController with excerptProjection

I have defined a #Projection for my Spring Data entity as described here
For the same reasons as described there. When I do GET request, everything is returned as expected. But when I do a POST request, the projection won't work. Following the example provided above, "Address" is shown as a URL under Links and is not exposed the way it is with GET request.
How to get it exposed the same way?
I created a class with #RepositoryRestController where I can catch the POST method. If I simply return the entity, it is without links. If I return it as a resource, the links are there, but "Address" is also a link. If I remove the GET method from my controller, the default behavior is as described above.
UPDATE
My entities are same as described here A, B and SuperClass except I don't have fetch defined in my #ManyToOne
My controller looks like this:
#RepositoryRestController
public class BRepositoryRestController {
private final BRepository bRepository;
public BRepositoryRestController(BRepository bRepository) {
this.bRepository = bRepository;
}
#RequestMapping(method = RequestMethod.POST, value = "/bs")
public
ResponseEntity<?> post(#RequestBody Resource<B> bResource) {
B b= bRepository.save(bResource.getContent());
BProjection result = bRepository.findById(b.getId());
return ResponseEntity.ok(new Resource<>(result));
}
}
And my repository looks like this:
#RepositoryRestResource(excerptProjection = BProjection.class)
public interface BRepository extends BaseRepository<B, Long> {
#EntityGraph(attributePaths = {"a"})
BProjection findById(Long id);
}
And my projection looks like this:
#Projection(types = B.class)
public interface BProjection extends SuperClassProjection {
A getA();
String getSomeData();
String getOtherData();
}
And SuperClassProjection looks like this:
#Projection(types = SuperClass.class)
public interface SuperClassProjection {
Long getId();
}
In the custom #RepositoryRestController POST method you should also return the projection. For example:
#Projection(name = "inlineAddress", types = { Person.class })
public interface InlineAddress {
String getFirstName();
String getLastName();
#Value("#{target.address}")
Address getAddress();
}
public interface PersonRepo extends JpaRepository<Person, Long> {
InlineAddress findById(Long personId);
}
#PostMapping
public ResponseEntity<?> post(...) {
//... posting a person
InlineAddress inlineAddress = bookRepo.findById(person.getId());
return ResponseEntity.ok(new Resource<>(inlineAddress));
}
UPDATE
I've corrected my code above and the code from the question:
#RepositoryRestResource(excerptProjection = BProjection.class)
public interface BRepository extends CrudRepository<B, Long> {
BProjection findById(Long id);
}
#Projection(types = B.class)
public interface BProjection {
#Value("#{target.a}")
A getA();
String getSomeData();
String getOtherData();
}
Then all works fine.
POST request body:
{
"name": "b1",
"someData": "someData1",
"otherData": "otherData",
"a": {
"name": "a1"
}
}
Response body:
{
"a": {
"name": "a1"
},
"someData": "someData1",
"otherData": "otherData",
"_links": {
"self": {
"href": "http://localhost:8080/api/bs/1{?projection}",
"templated": true
}
}
}
See working example

Spring MVC - Jackson deserialize braces

I have REST POST method which accepts #RequestBody String name.
When I send there from Postman json like this:
{
"name": "some_name"
}
I get a String variable which contains: '{ "name": "some_name" }'.
But when I send from postman just some_name I get variable which contains 'some_name'.
How it's possible?
You are telling Jackson to parse the payload into a String, so it does. If you want Jackson to parse a JSON payload into a POJO, do something like:
#RestController
public class MyController {
#PostMapping("my/endpoint")
public NameDto post(#RequestBody NameDto dto) {
// do stuff
return dto;
}
class NameDto {
private String name;
public NameDto() { }
public String getName() {
return this.name;
}
publid void setName(String s) {
this.name = s;
}
}
}
}
Depending on your use case, you may want to refactor NameDto into it's own file and modify the access to public.

Spring rest controller giving unsupported content type

Hello all here is what i have:
StockController.java
#RestController
public class StockController {
#Autowired
private StockRepository repository;
#RequestMapping(value = "stockmanagement/stock")
public ResponseEntity<?> addStock(#RequestBody String stock
) {
System.out.println(stock);
return new ResponseEntity<>(HttpStatus.OK);
}
when I make a request like so using chrome advanced rest extension :
Raw Headers
Content-Type: application/json
Raw Payload
{"stock": {"productId": 2, "expiryAndQuantity" : {}, "id": 0}}
It works fine in that out comes a string of json
However when i try to replace String stock with Stock stock where stock looks like this:
public class Stock {
#Id
private String id;
private String productId;
private Map<LocalDateTime, Integer> expiryAndQuantity;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public Map<LocalDateTime, Integer> getExpiryAndQuantity() {
return expiryAndQuantity;
}
public void setExpiryAndQuantity(Map<LocalDateTime, Integer> expiryAndQuantity) {
this.expiryAndQuantity = expiryAndQuantity;
}
#Override
public String toString() {
return String.format(
""
);
}
}
I get an error where by the following is fed back to me:
"status": 415
"error": "Unsupported Media Type"
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException"
"message": "Content type 'application/json;charset=UTF-8' not supported"
"path": "/stockmanagement/stock"
My question is; how do i create a request which maps to my Stock object.
You can try with #JsonRootName annotation, by default Spring serialize using no root name value. like this:
{"productId": 2, "expiryAndQuantity" : {}, "id": 0}
But if you want that your serialization has a rootname you need to use #JsonRootName annotation.
#JsonRootName(value = "Stock")
And it'll produce something like this
{"Stock": {"productId": 2, "expiryAndQuantity" : {}, "id": 0}}
You can see more here
http://www.baeldung.com/jackson-annotations
instead of accepting a String Accept a Stock object.and accept it from a post request than having a get request
#RequestMapping(value = "stockmanagement/stock",method=RequestMethod.POST)
public ResponseEntity<?> addStock(#RequestBody Stock stock){
}
and your request should be sent like this
{
"productId": 2
,"expiryAndQuantity" : null
,"id": 0
}
all parameter names should be equal to the objects filed names,since spring has jackson binders on class path and object will be created inside the controller method. if you are planning on passing different parameters from the post request you can use
#JsonProperty("pid")
private String productId;
on the field name.

Resources