Spring Boot Validations with annotations - spring

I am trying to validate the RequestBody using #Valid. Here is an article I read about this: https://dimitr.im/validating-the-input-of-your-rest-api-with-spring. Here is my code:
#PutMapping("/update")
public ResponseEntity<?> update(#RequestBody #Valid ProfileDTO medicationDTO) {
try {
profileService.update(medicationDTO);
} catch (Exception e) {
return ResponseEntity
.badRequest()
.body(new MessageResponseDTO("Error: User not found!"));
}
return ResponseEntity.ok(new MessageResponseDTO("User updated successfully!"));
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class ProfileDTO {
private Integer userId;
private String username;
private String email;
#NotBlank(message = "First name cannot be empty.")
#Min(1)
private String firstName;
#NotBlank(message = "Last name cannot be empty.")
#Min(1)
private String lastName;
#NotBlank(message = "Registration plate cannot be empty.")
#Min(1)
private String registrationPlate;
}
However, when I try sending this from Postman status 200 is returned:
{
"userId": "2",
"firstName": "",
"lastName": "Smith",
"registrationPlate": "AB20CDE"
}
Why isn't the validation working?

You are missing the BindingResult right after the medicationDTO, like:
public ResponseEntity<?> update(#RequestBody #Valid ProfileDTO medicationDTO, BindingResult bindingResult)
And you need to check if bindingResult.hasErrors() is true and then throw the exception you want.

Related

Error locating String field in Spring Boot

I'm trying to find a company by its CNPJ(Brazilian corporate tax payer registry number) in a DB (H2), but it's returning an error
{
"timestamp": "2022-03-30T19:30:23.823+00:00",
"status": 404,
"error": "Not Found",
"path": "/companies/cnpj/30101554000146"
}
I've tried other alternatives using:
http://localhost:8080/companies/cnpj/'30.101.554/0001-46', http://localhost:8080/companies/cnpj/"30.101.554/0001-46",
but the error persists. I implemented like this :
#Entity
#Table(name = "company")
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#CNPJ
private String cnpj;
//skipped
}
public interface CompanyRepository extends JpaRepository<Company,Long> {
Optional<Company> findByCnpj(String cnpj);
}
public class CompanyDTO {
private Long id;
private String name;
private String cnpj;
//skipped
}
#Service
#Transactionalpublic class CompanyService {
#Autowired
private CompanyRepository companyRepository;
#Transactional(readOnly = true)
public CompanyDTO findById(Long id) {
Company resultado = companyRepository.findById(id).get();
CompanyDTO dto = new CompanyDTO(resultado);
return dto;
}
#Transactional(readOnly = true)
public CompanyDTO findByCnpj(String cnpf) {
Optional<Company> resultado = companyRepository.findByCnpj(cnpf);
CompanyDTO dto = new CompanyDTO(resultado.get());
return dto;
}
}
#RestController
#RequestMapping(value = "/companies")public class CompanyController {
#Autowired
private CompanyService companyService;
#GetMapping(value = "/{id}")
public CompanyDTO findById(#PathVariable Long id) {
return companyService.findById(id);
}
#GetMapping(value = "/cnpj/{cnpj}")
public CompanyDTO findByCnpj(#PathVariable String cnpj) {
return companyService.findByCnpj(cnpj);
}
}
The expected output would be:
[
{"id": 1,
"nome": "Company 123",
"cnpj": "30.101.554/0001-46"
}
]
UPDATE:
I changed #GetMapping(value = "/cnpj/{cnpj}") to #GetMapping(value = "/cnpj/**") and:
#GetMapping(value = "/cnpj/**")
public CompanyDTO findByCnpj(HttpServletRequest request) {
return companyService.findByCnpj(request.getRequestURI().split(request.getContextPath() + "/cnpj/")[1]);
}
Works for me! Thanks
As explained here, pathParams with slashes can be realy tricky while using spring-boot. This article explains pretty well what to do to avoid getting an error 404 when your pathVariable has a slash.

How can I add properties to the mutation successful response

I am new to graphql-spqr so I hope this is an easy question, however I couldn't find a solution for this, even after a long search.
Hint: In my app, I use the code-first/schema-last approach, which I like about the graphql-spqr, so there is no schema.graphqls loaded from a file.
My User.java starts with this
#Table(name = "users")
#Entity
#Setter
#Getter
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GraphQLQuery(name = "id", description = "A user's id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, updatable = false)
public Long id;
#GraphQLQuery(name = "firstName", description = "User's first name")
#Column(name = "first_name")
public String firstName;
#GraphQLQuery(name = "lastName", description = "User's last name")
#Column(name = "last_name")
public String lastName;
#GraphQLQuery(name = "email", description = "User's email")
public String email;
#GraphQLQuery(name = "uuid", description = "User's uuid")
//#Type(type = "char")
public String uuid;
//#Type(type = "char")
#Transient
public Company company;
#Column(name = "company")
public Long companyId;
#Transient
public Role role;
#Column(name = "role")
public Long roleId;
#Column(name = "pw")
public String password;
#GraphQLQuery(name = "terms", description = "User accepted terms")
public Boolean terms;
#Transient
public String token;
#CreatedDate
public Instant created;
public String getUuid() {
return this.uuid;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public String getEmail() {
return this.email;
}
public String getPassword() {
return this.password;
}
}
A user is created by a mutation:
#GraphQLMutation(name = "createUser")
public User createUser (
#GraphQLArgument(name = "firstName") String firstName,
#GraphQLArgument(name = "lastName") String lastName,
#GraphQLArgument(name = "email") String email,
#GraphQLArgument(name = "password") String password,
#GraphQLArgument(name = "company") String company,
#GraphQLArgument(name = "terms") Boolean terms) throws UserExistsException {
... some business logic
... and finally I use the JpaRepository<User, String> to save the user
return userRepository.save(user);
}
This is the query I am sending to the server
{"operationName":"CreateUser","variables":{"firstName":"Chris","lastName":"Rowing","email":"foo54#bar.com","password":"dada","company":"Test 5","terms":true,"source":"start","invitationId":null},"query":"mutation CreateUser($firstName: String!, $lastName: String!, $email: String!, $password: String!, $terms: Boolean!, $company: String) {\n createUser(\n firstName: $firstName\n lastName: $lastName\n email: $email\n password: $password\n terms: $terms\n company: $company\n ) {\n id\n __typename\n }\n}\n"}
The new user gets saved in the DB, everything works fine, and in my Angular client I listen to the success event, and in the inspector there is the following output
{"data":{"createUser":{"id":4,"__typename":"User"}}}
My question
How can I customize the response? For example I need to respond also a JWT token, and maybe hide the id. I have not found a way to do this up to now and any help would be appreciated! Thanks!
For anyone who is experiencing the same newbie problem: This is how I solved it:
I added the token property to the GraphQL query, removed the id property, and added this to the UserService
// Attach a new field called token to the User GraphQL type
#GraphQLQuery
public String token(#GraphQLContext User user) {
return authService.createToken(user.email, user.uuid);
}
It is possible to add external fields to the response without changing the original User.class by using #GraphQLContext

Json content for One post in Many to one rs request in spring boot

So i have two classes,
class User:
#Data
#NoArgsConstructor
#Entity
public class User {
#Id
#GeneratedValue
private int id;
private String displayName;
private String email;
private String gender;
private String Nationality;
private int age;
#OneToMany(mappedBy = "id", cascade = CascadeType.ALL)
private List<Event> createdEvents;
#OneToMany(mappedBy = "id", cascade = CascadeType.ALL)
private List<Reservation> clientReservations;
}
and class Event:
#Data
#Entity
public class Event {
#Id
#GeneratedValue
private int id;
private String eventName;
private Date eventDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at", nullable = false, updatable = false)
#CreatedDate
private Date createdAt;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
}
Contoller class:
#RestController
#RequestMapping("/event")
public class EventController {
#Autowired
private EventRepository eventRepository;
#PostMapping("/create")
public Event addEvent(#RequestBody Event event) {
return eventRepository.save(event);
}
}
Json:
"eventName": "theatre",
"eventDate": "2020-04-22",
"user": 3
im new to spring boot and what I've tried doesn't work.
now i want to add a single Event, and i need to pass a user id to reference the user who created the event, How can i do it ?
1.
In your Event class, add a constructor like this:
public Event(String eventName, Date eventDate, User user) {
this.createdAt = new DateTime();
this.eventName = eventName;
this.eventDate = eventDate;
this.user = user;
}
2.
Instead of passing Event as #RequestBody, consider creating a dto that handles submitted data on Post requests
public class EventDto {
private String eventName;
private String eventDateString;
private Long userId;
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
public String getEventDateString() {
return eventDateString;
}
public void setEventDateString(String eventDateString) {
this.eventDateString = eventDateString;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
Then you must replace #RequestBody Event event with #RequestBody EventDto eventDto
3.
Inject UserRepository, handle data, check that submitted user id exists and save your Event
#PostMapping("/create")
public Event addEvent(#RequestBody EventDto eventDto) throws ParseException {
var user = userRepository.findById(eventDto.getUserId());
if (user.isPresent()) {
Event event = new Event(eventDto.eventName.trim(), new SimpleDateFormat("yyyy-MM-dd").parse(eventDto.eventDateString), user.get());
eventRepository.save(event);
}
}

Null Foreign Key (Springboot, Hibernate, Postman)

I am using Springboot with Hibernate and I would like to save a new “post” using a POST request to my database. One thing that I would like to highlight is that I am using the dependency “spring-boot-starter-data-rest”.
Schema of the database (MySQL):
Class User:
#Entity
#Table(name="user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id", nullable = false)
public int id;
#OneToMany(mappedBy = "user_id_fk")
public Set<Post> posts;
#Column(name="email")
private String email;
#Column(name="username")
private String username;
#Column(name="password")
private String password;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="create_time")
protected Date createTime;
#Column(name="type")
private String accountType;
public User() {
this.createTime = new java.util.Date();
}
public User(String email, String username, String password, String firstName, String lastName, Date createTime, String accountType) {
this.email = email;
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.createTime = createTime;
this.accountType = accountType;
this.createTime = new java.util.Date();
}
public User(int id, String email, String username, String password, String firstName, String lastName, Date createTime, String accountType) {
this.id = id;
this.email = email;
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.createTime = createTime;
this.accountType = accountType;
this.createTime = new java.util.Date();
}
Plus the Getters & Setters & toString().
Class Post:
#Entity
#Table(name="post")
public class Post implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
public int id;
#ManyToOne(optional = false)
#JoinColumn(name = "user_id_fk", nullable = false)
public User user_id_fk;
#Column(name="comment")
private String comment;
#Column(name="likes")
private int likes;
#Column(name="dislike")
private int dislike;
#Column(name="create_time")
protected Date createTime;
public Post() {
this.createTime = new java.util.Date();
}
public Post(String comment, int likes, int dislike, User user_id_fk) {
this.user_id_fk = user_id_fk;
this.comment = comment;
this.likes = likes;
this.dislike = dislike;
this.createTime = new java.util.Date();
}
public Post(int id, User user_id_fk, String comment, int likes, int dislike) {
this.id = id;
this.user_id_fk = user_id_fk;
this.comment = comment;
this.likes = likes;
this.dislike = dislike;
this.createTime = new java.util.Date();
}
Plus the Getters & Setters & toString().
Post request (I'm using Postman to send the request):
{
"comment" : "This is a comment",
"likes" : 123,
"dislike" : 1,
"user_id_fk" :
[
{
"id" : 1
}
]
}
In the request at the "user_id_fk" I tried with [ {"id" : 1 } ] and with { "id" : 1 } but the result was the same.
Issue:
When I am executing exactly the same code from my controller everything works are excepted. Bear in mind that I am using the dependency “spring-boot-starter-data-rest”.
Also, when I am executing the code without the “optional = false” and “nullable = false” is inserting the data into the database but the “user_id_fk” is null :(.
The error that I am getting:
not-null property references a null or transient value : com.citizen.citizen.entity.Post.user_id_fk;
nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : com.citizen.citizen.entity.Post.user_id_fk]
That means that the foreign key ("user_id_fk") is null but should not be null.
Any help will be greatly appreciated.
I just remove the dependency "spring-boot-starter-data-rest" and I solved the issue by creating my custom rest and everything works. Kisses!
According to this article, you should make user_id_fk nullable and then:
Send POST to create User
Send second POST to create Post
Send PUT to create a relation between the two.
This article states the same.
And the documentation only mentions handling associations via association links.

Spring Boot Data Rest JPA - Entity custom create (User)

I am trying to learn Spring. I created a project with Spring Boot using the following tools:
Spring Data JPA
Spring Data REST
Spring HATEOAS
Spring Security
I am trying to create a User entity. I want the user to have an encrypted password (+ salt).
When i do POST to /api/users i successfully create a new user.
{
"firstname":"John",
"lastname":"Doe",
"email":"johndoe#example.com",
"password":"12345678"
}
But i have 2 problems:
the password is saved in clear-text
the salt is null
+----+---------------------+-----------+----------+----------+------+
| id | email | firstname | lastname | password | salt |
+----+---------------------+-----------+----------+----------+------+
| 1 | johndoe#example.com | John | Doe | 12345678 | NULL |
+----+---------------------+-----------+----------+----------+------+
The problem i think is that the default constructor is used and not the other one i have created. I am new to Spring and JPA so i must be missing something. Here is my code.
User.java
#Entity
#Table(name = "users")
public class User{
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
public String firstname;
#Column(nullable = false)
public String lastname;
#Column(nullable = false, unique = true)
public String email;
#JsonIgnore
#Column(nullable = false)
public String password;
#JsonIgnore
#Column
private String salt;
public User() {}
public User(String email, String firstname, String lastname, String password) {
this.email = email;
this.firstname = firstname;
this.lastname = lastname;
this.salt = UUID.randomUUID().toString();
this.password = new BCryptPasswordEncoder().encode(password + this.salt);
}
#JsonIgnore
public String getSalt() {
return salt;
}
#JsonProperty
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty
public void setPassword(String password) {
this.password = password;
}
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
public User findByEmail(String email);
public User findByEmailAndPassword(String email, String password);
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
}
Also if someone finds what i did wrong, i would like to point me where/how i should put the user login code (decryption).
Thanks.
So, here is how i solved my problem: i created a Controller as my custom endpoint and then i created a service in which i placed the logic i wanted for the creation of the user. Here is the code:
UserController.java
#Controller
public class UserController {
#Autowired
private UserService userService;
#RequestMapping("/api/register")
#ResponseBody
public Long register(#RequestBody User user) {
return userService.registerUser(user);
}
...
}
UserService .java
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public Long registerUser(User user) {
user.setPassword(new BCryptPasswordEncoder().encode(password));
userRepository.save(user);
return user.getId();
}
...
}
so by doing a POST with
{
"firstname":"John",
"lastname":"Doe",
"email":"johndoe#example.com",
"password":"12345678"
}
in /api/register, i can now create a user with a hashed password.
If you want Spring to use your constructor, you need to
remove the no-argument constructor
annotate every parameter in the other constructor with #JsonProperty like this
public User(#JsonProperty("email") String email,
#JsonProperty("firstname") String firstname,
#JsonProperty("lastname") String lastname,
#JsonProperty("password") String password) {
this.email = email;
this.firstname = firstname;
this.lastname = lastname;
this.password = new BCryptPasswordEncoder().encode(password);
}
You don't need to provide a salt value to the BCryptPasswordEncoder because it already salts passwords by itself.

Resources