Dropwizard: How to add a custom validation for GET / PUT - java

I have a dropwizard service in which I am trying to implement request validation, and below is the code for same.
import com.google.common.collect.ImmutableMap;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/system-info")
#Produces(MediaType.APPLICATION_JSON)
public class SystemInfo {
#GET
#Path("/get")
public Response testValidation(#QueryParam("name") String name,
#QueryParam("phoneNo") Long phoneNo,
#QueryParam("email") String email,
#QueryParam("password") String password) {
if(email == null) {
return Response.ok(ImmutableMap.of("status", "email missing")).build();
}
//bunch of other validations
return Response.ok(ImmutableMap.of("status", "ok")).build();
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("/post")
public Response testPostValidation(final Person person) {
if(person.getEmail() == null) {
return Response.ok(ImmutableMap.of("status", "email missing")).build();
}
return Response.ok(ImmutableMap.of("status", "ok")).build();
}
}
class Person {
#JsonProperty
private String name;
#JsonProperty
private String email;
#JsonProperty
private long phoneNo;
#JsonProperty
private String password;
public String getEmail(){
return email;
}
public Person(){};
}
In both the GET and POST method I have the QueryParams and the Person object which I would like to be validated.
I have the validation logic inside the Resource class itself, I can create a separate class and replace the if statements with
//if(email == null) {
// return Response.ok(ImmutableMap.of("status", "email missing")).build();
//}
if(!CustomValidater.validate(email, name, phone, password)) {
return Response.ok(ImmutableMap.of("status", "data missing")).build();
}
and do the same for POST as well this way the validation logic is abstracted in different class.
Is this the correct way of doing the validation or should I create custom annotation that will do this validation?

You should create custom validator, or add constraints validation to model.
Person should have properties like:
#NotNull
private String name;
#NotEmpty
private String surname;
...
And should be validated as method parameter if needed:
#Valid Person person

Related

How to get rid of automatic installation of a new table

Problem: a new table is created once when I make a post request through The bash console. The rest of the queries go to the new table.
Than he does not like those databases which are available. As I understand - they just don't know, but I don't know how to direct it in the right. Although all variables are also named.
A problem was found created due to an Entity annotation in the Message class. Please tell me how to make it added to an existing table, tried #Table(name = "ApiTable") to an existing one, and it generates a new api_table.. Also don't quite understand what needs to be added/changed to accept json post requests.
Application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories("com.example.api")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MainController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping(path="/demo") /
public class MainController {
#Autowired
private UserRepository TestApi;
#PostMapping(path="/add")
public #ResponseBody String addNewUser (#RequestParam String name
, #RequestParam String email) {
Message n = new Message();
n.setName(name);
n.setEmail(email);
TestApi.save(n);
return "Saved";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<Message> getAllUsers() {
return TestApi.findAll();
}
}
Message
import javax.persistence.*;
#Entity
public class Message {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserRepository
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<Message, Integer> {
}
application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost/Test?useUnicode=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
The problem seems to be Spring Boot's default naming strategy which you'd have to replace.
Spring Boot's default naming strategy now seems to include converting camelCase to snake_case so you need to choose a different one (or implement your own).
Here's some more info on the topic: Hibernate naming strategy changing table names

Validation not performed in a Spring controller

I want to use annotations in classes. I use javax.validation.constrants.* for annotations.
public final class EmailCredential implements Serializable {
private static final long serialVersionUID = -1246534146345274432L;
#NotBlank(message = "Sender must not be empty.")
#Email
private final String sender;
#NotBlank(message = "Subject must not be empty.")
private final String subject;
/// getters setters
}
None of them are working as expected. Meaning that when a below API gets called, annotations should throw error if annotated field is invalid. It looks like there is no annotation to check fields. How properly can I use annotations in a normal class?
controller:
#PostMapping(value = "/email/credentials", consumes = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> emailCredentials(#RequestBody EmailCredential emailCredential) {
return emailService.setCredentials(emailCredential);
}
In your case the validation has to be specified to be triggered.
So add the #Valid annotation on the parameter(s) that you want to validate such as :
import javax.validation.Valid;
// ...
#PostMapping(value = "/email/credentials", consumes = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> emailCredentials(#RequestBody #Valid EmailCredential emailCredential) {
return emailService.setCredentials(emailCredential);
}
According to Spring Boot official documentation : Validating Form Input
You should indicate that your EmailCredential need to be validated using the annotation #Valid
Here's an example from documentation :
package hello;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Controller
public class WebController implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/results").setViewName("results");
}
#GetMapping("/")
public String showForm(PersonForm personForm) {
return "form";
}
#PostMapping("/")
public String checkPersonInfo(#Valid PersonForm personForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
}

Why this REST service is not working?

package model;
import java.net.URI;
import java.util.Collection;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
#Path("/item")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Stateless
public class InfoRestService {
// the PersistenceContext annotation is a shortcut that hides the fact
// that, an entity manager is always obtained from an EntityManagerFactory.
// The peristitence.xml file defines persistence units which is supplied by
// name
// to the EntityManagerFactory, thus dictating settings and classes used by
// the
// entity manager
#PersistenceContext(unitName = "Task")
private EntityManager em;
// Inject UriInfo to build the uri used in the POST response
#Context
private UriInfo uriInfo;
#POST
public Response createItem(PersonInfo item) {
if (item == null) {
throw new BadRequestException();
}
em.persist(item);
// Build a uri with the Item id appended to the absolute path
// This is so the client gets the Item id and also has the path to the
// resource created
URI itemUri = uriInfo.getAbsolutePathBuilder().path(item.getId()).build();
// The created response will not have a body. The itemUri will be in the
// Header
return Response.created(itemUri).build();
}
#GET
#Path("{id}")
public Response getItem(#PathParam("id") String id) {
PersonInfo item = em.find(PersonInfo.class, id);
if (item == null) {
throw new NotFoundException();
}
return Response.ok(item).build();
}
// Response.ok() does not accept collections
// But we return a collection and JAX-RS will generate header 200 OK and
// will handle converting the collection to xml or json as the body
#GET
public Collection<PersonInfo> getItems() {
TypedQuery<PersonInfo> query = em.createNamedQuery("PersonInfo.findAll",
PersonInfo.class);
return query.getResultList();
}
#PUT
#Path("{id}")
public Response updateItem(PersonInfo item, #PathParam("id") String id) {
if (id == null) {
throw new BadRequestException();
}
// Ideally we should check the id is a valid UUID. Not implementing for
// now
item.setId(id);
em.merge(item);
return Response.ok().build();
}
#DELETE
#Path("{id}")
public Response deleteItem(#PathParam("id") String id) {
PersonInfo item = em.find(PersonInfo.class, id);
if (item == null) {
throw new NotFoundException();
}
em.remove(item);
return Response.noContent().build();
}
}
package model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
/**
* The persistent class for the person_info database table.
*
*/
#Entity
#XmlRootElement
#Table(name="person_info")
#NamedQuery(name="PersonInfo.findAll", query="SELECT p FROM PersonInfo p")
public class PersonInfo implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
private String email;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
public PersonInfo() {
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="Task">
<jta-data-source>jdbc/DBtest</jta-data-source>
<class>model.PersonInfo</class>
</persistence-unit>
</persistence>
and the other class is Application
package model;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("rest")
public class ApplicationConfig extends Application{
}
I really have no idea, the connections are made ok .. I'm using Glassfish 4 server and MySQL database... code is deploying but when I want to access the localhost:8080/Task/.. (my app) the only thing it says is this:
"HTTP Status 404 - Not Found / Type Status report
messageNot Found
descriptionThe requested resource is not available."
The code you supplied is working (when commenting out the persistence related stuff), I guess you are just confusing something.
The #ApplicationPath annotation sets the root context which comes after your project name.
If you project name really is Task you have to use this URL: http://localhost:8080/Task/rest/item
Otherwise: http://localhost:8080/YOUR_PROJECT_NAME/rest/item
See also:
How to set up JAX-RS Application using annotations only (no web.xml)?

Jersey - Rest API (Json Format)

I am trying to create an Jersey application and I would like to pass the output to web browser in Json format. Below are the codes. I can only pass the data in xml format and when I change to "application/json" format, "HTTP Status 500 - Internal Server Error" came out. Any advice? thanks.
import java.net.URISyntaxException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/emp")
public class EmployeeService {
#GET
#Path("/get/{empID}")
#Produces({"application/json"})
public Employee getEmployee(#PathParam("empID") String empID){
Employee employee = new com.rest.employee.model.Employee();
employee.setEmpID(empID);
employee.setName("George");
employee.setEmail("george#gmail.com");
return employee;
}
}
and another java file
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name="employee")
public class Employee {
public String empID;
public String name;
public String email;
#XmlElement(required=true)
public String getEmpID() {
return empID;
}
public void setEmpID(String empID) {
this.empID = empID;
}
#XmlElement(required=true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement(required=true)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
The code looks 'nearly' fine and works for me as expected, but your Employee should implement Serializable. I can't see any other problem in there for now, so the problem is somewhere else i guess.
You said, that its not working anymore, since you changed from XML to JSON. So i reckon, that you miss a dependency for JSON. Check this ...
But in fact, the missing dependency should not result in an 500. Can it be true, that you have added a ExceptionMapper and that you throw your 500 by yourself?
You need to integrate Jersey with Jackson. Look at this tutorial:
http://examples.javacodegeeks.com/enterprise-java/rest/jersey/json-example-with-jersey-jackson/

BindingResult isn't detecting JSR:303 annotation based errors

I am new to Spring MVC. I am trying a simple application to validate form values. I am use Spring BindingResult and JSR303 for field validation. But for some reason the validation errors don't show up in the error tag. In fact, the binding result doesn't return any errors.
My bean is as follows:
package com.app.ebl.bean.login;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class LoginBean {
#NotNull(message = "User Name field can not be blank")
#Size(max = 10, message = "User Name should not be more than 10 characters")
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
My Controller Class Look like below,
package com.app.ebl.controller.login;
import javax.validation.Valid;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.app.ebl.bean.login.LoginBean;
#Controller
public class LoginController {
private static final Log log = LogFactory.getLog(LoginController.class);
#RequestMapping(value="/login", method=RequestMethod.GET)
public String toLogin(Model model)
{
model.addAttribute("login",new LoginBean());
return "login";
}
#RequestMapping(value="/authenticate", method=RequestMethod.POST)
public String authenticate(#ModelAttribute(value = "login") #Valid LoginBean login,
BindingResult bindingResult, Model model)
{
if(bindingResult.hasErrors()) {
return "login";
}
model.addAttribute("userName",login.getUserName());
return "home";
}
}
Now If I provide no value in the user name field, system is not validating the same, and allowing me to the next view.
Can someone please help me.
Thanks in Advance,
The #Size annotation accepts both min and max parameter. When min is not provided, it uses default:
/**
* #return size the element must be higher or equal to
*/
int min() default 0;
You didn't provided any, and my guess is that your controller do not transform empty strings to null. Firstly, I'd switch to #NotBlank annotation from org.hibernate.validator. In addition to #NotNull, it removes trailing whtespaces:
public class LoginBean {
#NotBlank(message = "User Name field can not be blank")
#Size(max = 10, message = "User Name should not be more than 10 characters")
private String userName;
....
Additionally, define init binder in your controller, that will change empty strings to null with help of StringTrimmerEditor from org.springframework.beans.propertyeditors.StringTrimmerEditor
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
Took me a while to figure this out, I hope this helps someone.
I had the same problem. The bean just wasn't getting validated.
What I had done was created a library via the "buildpath" menu and added the hibernate-validator.jar to my library. I could use the hibernate annotations and I wasn't getting any compile errors but when I ran it, the beans never got validated.
The reason was that the application needed the hibernate-validator.jar in WEB-INF/lib folder, so that it could use it for validation.

Categories

Resources