Java RestController REST parameter dependency injection or request - java

I am trying inject a dependency or at least filtrate the ID parameter that come into a RestControler in Spring. I am very new in Spring. How can i be sure that the parameter that comes passed in the API is valid and/or how can i inject its dependency related to Customer Entity?
This is my rest controller CustomerController method
#PatchMapping("/{id}")
public Customer updateCustomer(#PathVariable Long id, #RequestBody Customer customer) {
return customerService.updateCustomer(id, customer);
}
This is the request that at the moment filtrates only the firstname and last name
package com.appsdeveloperblock.app.ws.requests.customer;
import javax.validation.constraints.NotNull;
public class CreateCustomerRequest {
#NotNull
private String firstname;
#NotNull
private String lastname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
Thank you!

You need the Bean Validation API (which you probably already have) and it's reference implementation (e.g. hibernate-validator). Check here Java Bean Validation Basics
Summarizing
Add the respective dependencies to your pom.xml (or gradle):
<dependencies>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>6.1.2.Final</version>
</dependency>
</dependencies>
Use #Valid annotation on your Customer entity to have the payload validated automatically:
#PatchMapping("/{id}")
public Customer updateCustomer(#PathVariable Long id, #RequestBody #Valid Customer customer) {
return customerService.updateCustomer(id, customer);
}
You can decorate the fields of your Customer or CreateCustomerRequest class with further annotations, e.g. #Size, #Max, #Email etc. Check the tutorial for more information.

Related

#Valid Not working #PostMapping spring boot

I am learning spring boot, I am adding Validation for #PostMapping, but somehow it always create a object even with non-valid value.
Hospital.java
public class Hospital {
private Integer id;
#Size(min = 2)
private String name;
#Size(min = 2)
private String city;
public Hospital(Integer id, String name, String city) {
this.id = id;
this.name = name;
this.city = city;
}
Controller
#Autowired
HospitalData data;
...
#PostMapping("/hospital")
public ResponseEntity<Hospital> addHospital(#Valid #RequestBody Hospital hospital){
Hospital newHospital = data.addHospital(hospital);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(newHospital.getId()).toUri();
return ResponseEntity.created(location).build();
}
pom.xml
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
And previously I have added below dependency as I am using Spring 2.3.10 RELEASE, but it doesn't work, so I have added above dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
I created a Test Application reproducing the state of your code. As stated under the comments, the code you provided should definitely work. You definitely don't neet to provide a BindingResult to the method. Spring Boot throws a MethodArgumentNotValidException and therefore returns a bad request http status if the validation fails.
I created a project with following content:
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
DemoEntity:
public class DemoEntity {
#NotNull
public String name;
#Size(min = 3)
public String greeting;
}
DemoController:
#Controller
public class DemoController {
#PostMapping("/post")
public ResponseEntity<DemoEntity> put(#Valid #RequestBody DemoEntity demoEntity) {
return ResponseEntity.ok(demoEntity);
}
}
Now, that's what happens with my requests:
Name
Greeting
Result
Peter
Mr
400
Stephen
Monsieur
200
Clara
null
200
Jenny
Madamme
200
As you see from the table above, when greeting is null the result is an ok status. If you want to guarantee that the string is at least min characters long and not null, you need to declare this explicitely.
That's, for example, if you want to validate optional fields like a mobile number. You know, it should be n numbers long and contain only numbers, but you don't want to make it mandatory. Like I showed with the greeting above.
You need to inject the BindingResult object as the param immediately following your form object and then manually check if there were any errors.
See https://spring.io/guides/gs/validating-form-input/ Create a Web Controller for a good example of how this should be done in your controller.

How to fix MessageBodyProviderNotFoundException with Jersey Client using Jackson?

I have set up a REST client consuming JSON with Jersey. Firstly according to Baeldung's tutorial with MOXy and secondly according to Vaadin's tutorial with Jackson.
The JSON response from the REST service looks as follows:
{
"DisplayName": "Sixpack, Joe",
"UserID": "joe.sixpack",
"StdLog": "Some text"
}
So I have set up a simple JAXB bean ...
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class DisplayName {
private String displayName;
private String userID;
private String stdLog;
public DisplayName() {
}
public DisplayName(String DisplayName, String UserID, String StdLog) {
this.displayName = DisplayName;
this.userID = UserID;
this.stdLog = StdLog;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public String getStdLog() {
return stdLog;
}
public void setStdLog(String stdLog) {
this.stdLog = stdLog;
}
}
... added the Maven dependencies ...
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
... and implemented a REST client:
Client client = ClientBuilder.newClient();
URI uri = UriBuilder.fromUri("http://server:2000").path("/path/to/service/" + UriComponent.encode(input_parameter, UriComponent.Type.QUERY_PARAM_SPACE_ENCODED)).build();
WebTarget target = client.target(uri);
DisplayName responseDN = target.request(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).get(DisplayName.class);
However, all the POJO fields are always null when using the MOXy media converter regardless of the annotations and constructors (e.g. no-args constructor) used according to similar issues (e.g. JAX-RS Response Object displaying Object fields as NULL values, Java REST service accepts POJO, but fields are always null).
So I want to use Jackson and adapted the JAXB class ...
import com.fasterxml.jackson.annotation.*;
#JsonInclude(JsonInclude.Include.ALWAYS)
#JsonPropertyOrder({"DisplayName","UserID","StdLog"})
public class DisplayName {
#JsonProperty("DisplayName")
private String displayName;
#JsonProperty("UserID")
private String userID;
#JsonProperty("StdLog")
private String stdLog;
public DisplayName() {
}
#JsonCreator
public DisplayName(#JsonProperty("DisplayName") String DisplayName, #JsonProperty("UserID") String UserID, #JsonProperty("StdLog") String StdLog) {
this.displayName = DisplayName;
this.userID = UserID;
this.stdLog = StdLog;
}
public String getDisplayName() {
return displayName;
}
#JsonProperty("DisplayName")
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getUserID() {
return userID;
}
#JsonProperty("UserID")
public void setUserID(String userID) {
this.userID = userID;
}
public String getStdLog() {
return stdLog;
}
#JsonProperty("StdLog")
public void setStdLog(String stdLog) {
this.stdLog = stdLog;
}
}
... and removed the MOXy dependency and added a dependency for JAXB:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
However, now I run into the error
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json
and none of the solutions I found did help (e.g. MessageBodyReader not found for media type=application/json, Glassfish :MessageBodyProviderNotFoundException in Jersy Client).
Why? And what is the solution?
Shortly after I had posted the question, I found the explanation and solution in the Jersey User Guide which says:
In order to use Jackson as your JSON (JAXB/POJO) provider you need to register JacksonFeature and a ContextResolver for ObjectMapper, if needed, in your Configurable (client/server).
Since MOXy is the default media library and registered automatically with the WebClient, it will be used unless another media library is registered. Hence, one has to register the Jackson media converter ...
Client client = ClientBuilder.newClient().register(JacksonFeature.class);
... and add the respective Maven dependency:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25.1</version>
</dependency>
I'm just surprised that neither the tutorials nor the other answers to similar questions mentioned it.

Spring Boot Automatic JSON to Object at Controller

I have SpringBoot application with that dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
I have a method at my controller as follows:
#RequestMapping(value = "/liamo", method = RequestMethod.POST)
#ResponseBody
public XResponse liamo(XRequest xRequest) {
...
return something;
}
I send a JSON object from my HTML via AJAX with some fields of XRequest type object (it is a plain POJO without any annotations). However my JSON is not constructed into object at my controller method and its fields are null.
What I miss for an automatic deserialisation at my controller?
Spring boot comes with Jackson out-of-the-box which will take care of un-marshaling JSON request body to Java objects
You can use #RequestBody Spring MVC annotation to deserialize/un-marshall JSON string to Java object... For example.
Example
#RestController
public class CustomerController {
//#Autowired CustomerService customerService;
#RequestMapping(path="/customers", method= RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public Customer postCustomer(#RequestBody Customer customer){
//return customerService.createCustomer(customer);
}
}
Annotate your entities member elements with #JsonProperty with corresponding json field names.
public class Customer {
#JsonProperty("customer_id")
private long customerId;
#JsonProperty("first_name")
private String firstName;
#JsonProperty("last_name")
private String lastName;
#JsonProperty("town")
private String town;
}
SpringBoot by default comes with this functionality. You just have to use #RequestBody annotation in parameter declaration of your controller method but in contrast to #so-random-dude's answer you don't have to annotate fields with #JsonProperty, that is not required.
You just have to provide getters and setters for your custom XML object class. I am posting an example below for simplicity.
Example:
Controller method declaration:-
#PostMapping("/create")
public ResponseEntity<ApplicationResponse> createNewPost(#RequestBody CreatePostRequestDto createPostRequest){
//do stuff
return response;
}
Your custom XML object class:-
public class CreatePostRequestDto {
String postPath;
String postTitle;
public String getPostPath() {
return postPath;
}
public void setPostPath(String postPath) {
this.postPath = postPath;
}
public String getPostTitle() {
return postTitle;
}
public void setPostTitle(String postTitle) {
this.postTitle = postTitle;
}
}

how can I connect to my database with spring and test it?

I am trying to create a connection for my spring project, soemthing very simple.
I want to create the configuration fields on my application.properties. I tried with the spring.datasource and I dont get any error, but still, I dont get any info, just null pointers... I think the connections have not been made properly.
Here is part of my pom, with this dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
And my properties:
server.port=80
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/database
jdbc.username=user
jdbc.password=password
I dont get any error, but when I query the database, I always get a null pointer, which makes me think is not working properly, any idea how to configure this?
Thanks
EDIT:
My configuration class
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
The controller
#Autowired
private MyService myService;
#RequestMapping("/")
public String index() {
User myUser = myService.findUserById(1L);
System.out.println(myUser);
return myUser.getFirstName();
}
My Service Implementation
#Service
public class MyServiceImpl implements MyService {
#Autowired
UserRepository userRepository;
public User findUserById(Long id){
return userRepository.findOne(id);
};
}
My Entity
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
And my repository
public interface UserRepository extends CrudRepository<User, Long> {
//User findOne(Long id); this is a default method
}
THE ERROR:
I always get null pointer, even if data exist... and if I change the database password for a wrong one, I dont get any error.. like if it would be ok.
1) Please remove below dependency from pom, because of it might let SpringBoot use h2 instead of mysql, which cause you don't see any error even with wrong mysql password.
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
2) In application.properties, please use:
spring.datasource.url=jdbc:mysql://localhost:3306/database
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
These property names can be found from SpringBoot document: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html:
The correct data source url is:
jdbc:mysql://HostName:Port(3306 is the default)/DBName
You are missing the #Repository annotation on your Repository. Add that and see if that works for you.
If not, 1. share the complete stack trace that you are getting.
2. Also, are you able to reach to the controller endpoint?
I noticed your entity does not implements Serializable interface.
If this is not the issue please take a look at the spring boot example from there github repository for reference.
https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-data-jpa/

How do I make Spring call a validator on a REST service?

I have studied many, many answers and I still can't see what I'm doing wrong.
I have a REST service controller which takes a bean as a parameter and I would like to validate that the fields on the bean are properly set.
The following is (a portion of) my configuration object.
#Configuration
#ComponentScan({...})
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public Validator validator() {
LocalValidatorFactoryBean validatorBean = new LocalValidatorFactoryBean();
return validatorBean;
}
#Override
public Validator getValidator() {
return validator();
}
}
I can't tell if those are necessary, but I can tell they're never called, so maybe they're in the wrong place?
The following is (a portion of) my pom.xml:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>5.1.1.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope>
</dependency>
I don't think el is necessary for what I'm doing, but I included it in case it made a difference.
Here is (a portion of) my bean class that needs to be validated:
public class ContactInfo {
#NotNull
#Size(min=1, max=50)
private String phone;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
And finally, this is (a portion of) the Rest Controller:
#RestController
#RequestMapping("/contact")
public class ContactController {
private static final String USER_ID_ATTRIBUTE = "userId";
#Autowired
private Validator validator;
#Autowired
#Qualifier("contactService")
private ContactService service;
#RequestMapping("/submit")
public void submitContact(
#Valid #RequestBody ContactInfo formData, BindingResult result) throws Exception {
assert(validator != null);
if(result.hasErrors()) {
throw new Exception("I don't like it one bit.");
}
service.submitContact(formData);
}
}
In the debugger, I can see that the validator passed in is an OptionalValidatorFactoryBean, and result.hasErrors() is always false.
The JSON passed in to the controller includes this:
{ "phone":"" }
I think that should trigger a validation error because it doesn't meet the minimum size.

Categories