Spring Boot Bad Request while sending post request with inside class on #RequestBody - spring

I get bad request while using a nested class as a #RequestBody using postman. Is it my json? am i missing something? or this is a wrong implementation? This is my first time using a nested class for #RequestBody.
this is my class:
#Getter
#Builder
#AllArgsConstructor
public class CreateOrder {
#NotNull
private final String recipientName;
#NotNull
private final OrderAddress address;
#NotNull
private final List<OrderItem> items;
#Getter
#Builder
#AllArgsConstructor
public static class OrderItem{
#NotNull
private final Product product;
#NotNull
private final int quantity;
}
#Getter
#Builder
#AllArgsConstructor
public static class Product{
#NotNull
private final Integer id;
}
}
This is my controller:
#RestController
#RequiredArgsConstructor
#RequestMapping("api/v1/orders")
public class OrderController {
private final OrderService orderService;
#PostMapping
public ResponseEntity<Data<CreateOrderResponse>> createOrder(#RequestBody CreateOrder createOrder){
return new ResponseEntity<>(new Data<>(orderService.createOrder(createOrder)), HttpStatus.CREATED);
}
#GetMapping("/{orderID}")
public ResponseEntity<Data<GetOrderResponse>> getOrder(#PathVariable("orderID")UUID orderID){
return new ResponseEntity<>(new Data<>(orderService.getOrder(orderID)), HttpStatus.OK);
}
}
This is my json:
{
"recipientName": "TEST",
"address": {
"street": "Street AB",
"city": "City A",
"postalCode": "30001",
"detail": "Block A"
},
"items": [
{
"product": {
"id": 1
},
"quantity": 20
}
]
}

You need default constructors for the CreateOrder Model and its inner classes. You can use the #NoArgsConstructor annotation from Lombok to avoid having to have the boilerplate in your code. However, since your all your fields are final there will be 'Variable might not have been initialized' errors, so you will need to remove the final keyword from each field.

Related

Spring Boot List of Object Bean Validation

I have a Bean,
#Data
#NoArgsConstructor
public final class PersonRequest {
#NotNull
#JsonProperty("nameList")
private List<Person> nameList;
}
and Person POJO,
#Data
public class Sensor {
#NotNull
#JsonProperty("id")
private int id;
#NotNull
#JsonProperty("name")
#Min(1)
private String name;
}
I am sending JSON request and added #Valid in my controller. I am sending request as below,
{
"nameList": [
{
"id": 1,
"name": "John"
},
{
"id": 2,
"name": "Alex"
}
]
}
When i send request without id and name not validating. I tried using #Valid private List<Person> nameList; also but no luck. I use Spring boot 2.3.2.
UPDATED:
when i add one more attribute, this also say bad request when i pass date in request.
#NotNull
#JsonProperty("startTime")
#DateTimeFormat(pattern = "yyyy-MM-dd'T'hh:mm:ss", iso =
DateTimeFormat.ISO.DATE_TIME)
#Valid
private LocalDateTime startTime;
The #Valid annotation in your controller triggers the validation of the PersonRequest object, passed as request body. To validate also the Person objects contained in PersonRequest, you need to annotate that field with #Valid too.
#Data
#NoArgsConstructor
public final class PersonRequest {
#NotNull
#JsonProperty("nameList")
#Valid
private List<Person> nameList;
}

One to many assosiation using #Onetomany #JoinColumn in spring boot?

I am beginner in spring boot and web development and trying to do one-to-many association mapping using Onetomany and joincolumn annotation.
In my application 2 entity classes are there User and ProductDetail and i am trying to do One-to-many association between them.I am expecting user-id as foreign key in ProductDetail table.
There is no error while running my program and when i am passing the JSON statement as input to testcase my application in postman,but when i am seeing my MySql database to see if mapping occured or not, user id is not mapped as a foreign key in ProductDetail table.
I am posting the Entity classes and other classes below.....
Please help i am really stuck please let me know what i am going wrong(I know i am missing very small thing which i am unable to figure out on my own)
User class:-
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Entity
public class User {
#Id
#GeneratedValue
int id;
String name;
String gender;
String emailId;
#OneToMany(targetEntity = ProductDetail.class,cascade = CascadeType.ALL)
#JoinColumn(name="up_fk",referencedColumnName = "id")
private List<ProductDetail> userpurchase;
}
ProductDetail class:-
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Entity
public class ProductDetail
{
#Id
private int productid;
private String productname;
private int cost;
}
Controller class:-
#RestController
#RequestMapping("/Ecommerce")
public class UserController
{
#Autowired
private ProductService productservice;
#Autowired
private ProductRepository productRepository;
#Autowired
private UserRepository userRepository;
#GetMapping("/products")
public List<ProductDetail> ShowProducts()
{
return productservice.getProducts();
}
#PostMapping("/PlaceOrder")
public User placeOrder(#RequestBody OrderRequest request)
{
return userRepository.save(request.getUser());
}
}
OrderRequest:- class which only contains User object from which we extract user object and it will contain list of products..
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class OrderRequest
{
private User user;
}
application.properties file:-
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ecommerceapi
spring.datasource.username=root
spring.datasource.password=deep
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto =update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
Correct name is userpurchase, not ProductDetail.
{
"user": {
"name": "deep",
"gender": "male",
"emailId": "d#gmail.com",
"userpurchase": [
{
"productid": 2,
"productname": "laptop",
"cost": 50000
},
{
"productid": 3,
"productname": "earphone",
"cost": 500
}
]
}
}
I think the structure is wrong, how do you know which order asosiated with which products. If the user cancelled an order which product should not be sent?
Your order should contain a list of products and one user.

How to unwrap custom serialized objects from "content" key with Jackon & Spring Boot

I have customly serialized my object which is represented by this class:
public class Product {
private Long id;
private String name;
private Unit defaultUnit;
private Section section;
}
Serialization:
#Override
public void serialize(Product product, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeNumberField("id", product.getId());
jsonGenerator.writeStringField("name", product.getName());
jsonGenerator.writeStringField("defaultUnit", product.getDefaultUnit().toString());
jsonGenerator.writeObjectField("section", product.getSection());
}
However this produces an error which as I understand means that the default serializer has created a key and I have to provide it with a value:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Can not write a field name, expecting a value; nested exception is com.fasterxml.jackson.core.JsonGenerationException: Can not write a field name, expecting a value]
Now it is obvious that the solution is to wrap the fields adding
jsonGenerator.writeStartObject();
jsonGenerator.writeEndObject();
This however results in generating entity wrapped in "content" object:
"content": {
"id": 1,
"name": "Product",
"defaultUnit": "Unit",
"section": {
"name": "Section"
}
}
My question is whether it is possible to write the entity unwrapped that is without the "content" key.
The following is all the code associated with Product:
Product.java
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#RequiredArgsConstructor
#JsonDeserialize(using = ProductDeserializer.class)
#JsonSerialize(using = ProductSerializer.class)
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NonNull
private String name;
#ManyToOne
#NonNull
private Unit defaultUnit;
#ManyToOne
#NonNull
private Section section;
}
ProductExcerpt.java
#Projection(name = "productExcerpt", types = {Product.class})
public interface ProductExcerpt {
String getName();
#Value("#{target.defaultUnit.toString()}")
String getDefaultUnit();
SectionExcerpt getSection();
}
ProductRepository.java
#RepositoryRestResource(excerptProjection = ProductExcerpt.class)
#CrossOrigin
public interface ProductRepository extends JpaRepository<Product, Long> {
}
ProductSerializer.java
public class ProductSerializer extends StdSerializer<Product> {
ProductSerializer(){
super(Product.class);
}
#Override
public void serialize(Product product, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("id", product.getId());
jsonGenerator.writeStringField("name", product.getName());
jsonGenerator.writeStringField("defaultUnit", product.getDefaultUnit().toString());
jsonGenerator.writeObjectField("section", product.getSection());
jsonGenerator.writeEndObject();
}
}
There is also ProductDeserializer class however I think that it is irrevelant in this case. I do not have a Controller configured beacause as far as I am concerned there is no need while using spring-data-rest.

spring-data-rest not lazy loading thru #RepositoryRestResource embedded objects

Given the following
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#EntityListeners(AuditingEntityListener.class)
#Audited
#Builder
public class User extends BaseEntity {
#NotNull
private String firstName;
#OneToMany(fetch = FetchType.LAZY)
#NotAudited
#JoinColumn(name = "id")
private List<UserAud> audit;
}
#Entity
#Table(name = "USER_AUD")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class UserAud extends BaseAudEntity {
#Column(name = "FIRST_NAME")
private String firstName;
}
and
#Repository
#RepositoryRestResource(path="users")
public interface UserRepository extends JpaRepository<User, Long> {}
I see that when I #Autowire UserRepository userRepository and use userRepository.findAll() audit field is lazy loaded.
However when I access it thru #RepositoryRestResource all child audit are loaded as well.
e.g
{
"_embedded": {
"users": [
{
"id": 1,
"firstName": "cfo"
"_embedded": {
"audit": [
{
"firstName": "cfo"
}
]
}
]
....
I could not find on whether this is possible at all thru #RepositoryRestResource or not.
Off course this eager loading affects performance of controller. I am going to have many audit records.
Ideally I would like to have LAZY loading thru controller endpoint
GET /api/users
and EAGER loading thru
GET /api/users/1/audit
I later realized that most probably serialization is triggering getAudit
I tried the following
#Override
public void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
Hibernate5Module module = new Hibernate5Module(); // or Hibernate4Module ... depends on hibernate version you are using
module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
messageConverters.add(new MappingJackson2HttpMessageConverter(mapper));
}
and
#Bean
public Module hibernate5Module() {
Hibernate5Module hibernate5Module = new Hibernate5Module();
hibernate5Module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
return hibernate5Module;
}
and
#Bean
public Jackson2ObjectMapperBuilder configureObjectMapper() {
return new Jackson2ObjectMapperBuilder()
.modulesToInstall(Hibernate5Module.class);
}
but none of them worked :(

Spring 4.1.7 validate request body

I know this issue has been around there in other post, but after applying the fiux suggested was not working.
I am using spring 4.1.7 version, i want to validate the RequestBody from post rest call. For doing this i tried following set of codes, but it was not working as i expected.
My Request body pojo classes.
ParentPojo.class
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#Validated
public class ParentPojo<T> implements Serializable{
#NotNull
private String cNumber;
#NotNull
private String statusCode;
#NotNull T child;
}
ChildPojo.class
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#Validated
public class ChildPojo{
#NotNull
private String name;
#NotNull
private String address;
#NotNull
private String pin;
}
Controller:
Adding only methods
#Autowired
#Qualifier("validator")
private Validator validator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
#RequestMapping(produces = { "application/json", "application/xml" }, consumes ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, method = { RequestMethod.POST })
#ResponseStatus(HttpStatus.CREATED)
public Messageresponse<ChildPojo> create(#NotNull(groups = {ParentPojo.class, ChildPojo.class})
#Valid #Validated({ParentPojo.class, ChildPojo.class}) #RequestBody final ParentPojo<ChildPojo> ParentPojo, BindingResult bindingResult) {
System.out.println("new version 8="+bindingResult.hasErrors());
validator.validate(ParentPojo, bindingResult);
if(bindingResult.hasErrors()) {
System.out.println("Non formated form stuff.");
}
return service.create(ParentPojo);
}
#RequestMapping(value = "/{create}", produces = { "application/json", "application/xml" }, consumes ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, method = { RequestMethod.POST })
#ResponseStatus(HttpStatus.CREATED)
public Messageresponse<ChildPojo> create1(#NotNull #Valid #RequestBody final ParentPojo<ChildPojo> ParentPojo, BindingResult bindingResult) {
System.out.println("new version 8="+bindingResult.hasErrors());
validator.validate(ParentPojo, bindingResult);
if(bindingResult.hasErrors()) {
System.out.println("Non formated form stuff.");
}
return service.create(ParentPojo);
}
application context xml:
<mvc:annotation-driven validator="validator">
</mvc:annotation-driven>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
jar file tried:
hibernate-validator-4.3.0.Final
hibernate-validator-4.1.0.Final
hibernate-validator-4.0.2.GA
validation-api-1.1.0.Final
spring-context-4.1.7.RELEASE
But nothing was working with all the above combination for the request below:
in below request "pin" is missed and the controller method #Valid #RequestBody expected to handle this request as Bad Request. instead it is accepting the request body and processing further.
{
"cNumber" : "ff",
"statusCode" : "ddd",
"child" : {
"name" : "ll",
"address" : "ll"
}
Look at this question Here
You need to decorate the child pojo as #Valid
public class ParentPojo<T> implements Serializable{
#NotNull
private String cNumber;
#NotNull
private String statusCode;
#Valid
#NotNull
T child;
}

Resources