#valid annotation sends exception in response to rest service - java

I am using custom validation in my rest web services.
#PUT
#Path("/{accountId}")
#Consumes({MediaType.APPLICATION_JSON})
public Response update(
#NotNull #ValidUUID #PathParam("accountId") UUID accUUID,
#NotNull #Valid Account changedAcc) {
synchronized (LOCK_MANAGER.getLock(accUUID)) {
return accHelper.update(this.getCurrentUser(), accUUID, changedAcc);
}
}
here is a glimpse at my Account class
#Table(keyspace = "Randomss", name = "accounts")
public class Account {
#PartitionKey
#Column(name = "id")
#JsonIgnore
private UUID id;
#Column(name = "acc_type")
#NotNull
#ValidString
#JsonIgnore
private String accType;
Now I send JSON data to this web service to update account,
but if I send some wrong json data
(e.g acc_type is expected as string and I send numeric data)
then it throws an exception.
How do I get it to send an error message instead of throwing an exception
(specifically, I want to send the error message)?

You need to write a javax.ws.rs.ext.Provider that implements an javax.ws.rs.ext.ExceptionMapper.
For example a generic ValidationExceptionMapper might look like:
#Provider
public class ValidationExceptionMapper
implements ExceptionMapper<ValidationException> {
public Response toResponse(ValidationException e) {
return Response.status(BAD_REQUEST).build();
}
}
You can choose a more appropriate response to return.

Related

JAX RS 2.0 Not Converting UUIDs

I have been given an OpenAPI yaml file and have used the maven openapi generator plugin to turn this into Java. This is being deployed to Weblogic 12.2.1.4 which should have Jersey 2.22 intalled.
This is largely working except for some UUID columns which are just coming through as null.
There is an API class
#Path("/jobtest")
public class JobTestAPI {
#POST
#Consumes({ "application/json" })
public Response jobTestNotification(#Valid #NotNull JobTestNotification jobTetNotification) {
return Response.ok().entity("OK").build();
}
}
and a data class
#JsonTypeName ("JobTestNotification")
public class JobTestNotification {
private #Valid UUID jobId;
private #Valid Date jobTimestamp;
private #Valid String jobDescription;
#JsonProperty("jobId")
#NotNull
public UUID getJobId() {
return jobId;
}
#JsonProperty("jobId")
public void setJobId(UUID jobId) {
this.jobId = jobId;
}
// Other getters / setters with same annotations
}
Sending
{
"jobId": "abcdef12-abcd-ef12-1234-abcdef123456",
"jobTimestamp": "2022-11-10T09:00:00.000Z",
"jobDescription": "Some description"
}
I am getting a bad request and adding in an ExceptionMapper I see it is complaining that the UUID is null. Indeed if I take out the #Valid annotation on the API method and print out the object I can see the other two values are there but the UUID field is null.
If I change the datatype of jobId to String it works.
My understanding is that as UUID implements a static fromString method there is no need for a custom converter (and indeed trying to implement one didn't seem to work).
Why are these fields not being mapped through (I have treble checked they are being passed with correct names and are definitely there)? Other than just changing the values to String what can I do to get this work

Spring Boot REST API unsupported media type

I have two API:s , CarRental-API on port 8080 and CarRental-CRUD on port 8081.
CarRental-CRUD uses JpaRepository to access a h2 memory DB.
I want to use CarRental-API to make requests to CarRental-CRUD, using webclient.
In CarRental-CRUD , I can make post requests and add cars to the db using this service:
public String addCar(Car car) {
carRepository.save(car);
return loggerService.writeLoggerMsg("CREATED CAR AND ADDED TO DB");
}
And then in the controller :
#RestController
#RequestMapping("/crud/v1")
public class AdminCarController {
#Autowired
private AdminCarService adminCarService;
#PostMapping(path = "/addcar", consumes = "application/json")
public String addCar(#RequestBody Car car) {
return adminCarService.addCar(car);
}
}
I tried to post a request with webclient in CarRental-API with :
#Service
public class AdminCarService {
#Autowired
LoggerService loggerService;
#Autowired
private WebClient.Builder webClientBuilder;
public String addCar(Car car) {
webClientBuilder
.build()
.post()
.uri("localhost:8081/crud/v1/addcar")
.retrieve()
.bodyToFlux(Car.class);
return loggerService.writeLoggerMsg("ADDED CAR TO DB");
}
}
However, using the carRental-API , I get this error in postman when I try to post a request :
"status": 500,
"error": "Internal Server Error",
"trace": "org.springframework.web.reactive.function.client.WebClientResponseException: 200 OK from POST localhost:8081/crud/v1/addcar; nested exception is org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'text/plain;charset=UTF-8' not supported for bodyType=com.backend.carrentalapi.entity.Car\n\tat
This is the Car Entity :
#Getter
#Setter
#RequiredArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "TBL_CAR")
public class Car {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long carId;
#Column(name = "NAME")
private String carName;
#Column(name = "MODEL")
private String carModel;
#Column(name = "DAILY_PRICE")
private double dailyPrice;
}
I can't seem to find where in the code I am producing text/plain. I made sure in postman that I'm posting a raw JSON body request, and the headers say content type : application/json.
In your WebClient you are not adding the request body, but instead expecting a Car back from the API you are calling (and this API returns a simple String instead). The following should work.
#Service
public class AdminCarService {
#Autowired
LoggerService loggerService;
#Autowired
private WebClient.Builder webClientBuilder;
public String addCar(Car car) {
webClientBuilder
.build()
.post()
.uri("localhost:8081/crud/v1/addcar")
.body(BodyInserters.fromValue(car))
.retrieve()
.toBodilessEntity();
return loggerService.writeLoggerMsg("ADDED CAR TO DB");
}
}
Using .toBodilessEntity() since you don't really do anything with the response.

Ignore a property in Dto from RequestBody base on endpoint (use one Dto for two purpose)

I have a Coupon Table in the database so there are CouponEntity and CouponDto in the application. My question is about handling two endpoints with one Dto. for example, I have create and update endpoints, both of them use CouponDto. There is a business role that I can't update code property of the coupon entity. How to ignore it if the client passes this property in the request?
If I use #JsonIgnore, the property will be ignored in both the endpoints that I need to get it in create API. I used groups for validations and separate validations from each other, but #JsonIgnore can not be set sometimes.
For create a coupon:
#PostMapping("/create")
public ResponseEntity<?> createCoupon(#RequestBody CouponCodeDTO couponCodeDTO) {
return ResponseEntity.ok(couponService.create(couponCodeDTO));
}
For update a coupon:
#PutMapping("/update/{id}")
public ResponseEntity<?> updateCoupon(
#PathVariable Long id, #RequestBody CouponCodeDTO couponCodeDTO
) {
return ResponseEntity.ok(couponService.update(id, couponCodeDTO));
}
And CouponDto:
public class CouponCodeDto extends BaseDto<Integer> {
#NotBlank
private String code;
private Integer availableCount;
#NotNull
private LocalDate startDate;
...
}
You can use #JsonView to specify the field you want to serialize/deserialize per view and specify view on the endpoint.
public class Views {
interface Update {}
interface Create extends Update {}
}
public class CouponCodeDto extends BaseDto<Integer> {
#NotBlank
#JsonView(Views.Create.class)
private String code;
#JsonView(Views.Update.class)
private Integer availableCount;
#NotNull
#JsonView(Views.Update.class)
private LocalDate startDate;
...
}
And use on request body of the endpoint
#PutMapping("/update/{id}")
public ResponseEntity<?> updateCoupon(#PathVariable Long id,
#JsonView(Views.Update.class) #RequestBody CouponCodeDTO couponCodeDTO) {
#PostMapping("/create")
public ResponseEntity<?> createCoupon(
#JsonView(Views.Create.class) #RequestBody CouponCodeDTO couponCodeDTO) {
Here you find details
https://www.baeldung.com/jackson-json-view-annotation
https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

Quarkus: #Valid doesn’t work with PanacheMongo

I have a problem with validation of my bean.
I'm using Quarkus with MongoDB and when I try to run a create REST API, with #Valid annotation before request bean, I expect an exception if I want to create a document with null field (obviously I use #NotNull in entity), but document is created without field.
Here is my code:
Car.java:
#MongoEntity(collection="cars")
public class Car extends PanacheMongoEntityBase {
#BsonId
private long id;
#NotNull
private String carName;
#NotNull
#Size(min = 1, max = 3)
private String code;
// get and set
}
CarResource.java:
#Path("/cars")
#Consumes("application/json")
#Produces("application/json")
public class CarResource {
#GET
public List<Car> list() {
return Car.listAll();
}
#GET
#Path("/{id}")
public Car get(long id) {
return Car.findById(id);
}
#POST
public Response create(#Valid Car car) {
car.persist();
return Response.status(201).build();
}
I have same problem with #Size annotation, because I can create a code field with more characters than 3.
UPDATE
Validation works with quarkus-hibernate-validator.
Now, I have to find a solution for unique field.
And besides from the main question: is there an annotation like #Indexed(unique = true)? I want an unique field for my app.
You need to use #Validated annotation on CarResource Class as shown below.
#Path("/cars")
#Consumes("application/json")
#Produces("application/json")
#Validated
public class CarResource {
#GET
public List<Car> list() {
return Car.listAll();
}
#GET
#Path("/{id}")
public Car get(long id) {
return Car.findById(id);
}
#POST
public Response create(#Valid Car car) {
car.persist();
return Response.status(201).build();
}
}

Passing data from a form to a REST endpoint

I'm kinda new to writing REST endpoints thus this question.
I'm writing a REST endpoint that should support a form submission in iOS that registers an User.
This is my User class.
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long Id;
private String username;
private String email;
private String password;
}
This is the controller user signup signature that I've been asked to work with,
#RestController
public class RegistrationController {
#RequestMapping(value = "/user/signup",
method = RequestMethod.POST,
consumes = {"application/json"})
public ResponseEntity<?> showRegistrationForm(#RequestBody User user) {
try{
//persist the User
return new ResponseEntity(HttpStatus.OK);
}catch(Exception e){
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
}
The user object is provided to me using the following JSON payload,
{
"username": "Something",
"email": "Something",
"password": "password"
}
What I don't understand is how do I grab the fields posted from the form and convert them to the user in a POST request. Wouldn't I need to change the parameters to string, validate them and then compose the User object. Or just what is the right way of doing this.
Any help appreciated.

Categories