throwing "Unsupported Media Type" for rest end point - spring

Team,
i am facing "unsupported media type" issue, while invoking the below end point in spring boot application. And Template class is annotated with #XmlRootElement
#RequestMapping(value = "/template", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_JSON},
headers={"Accept=application/json"})
public ResponseEntity<Response> create ( #RequestPart("file") #Valid #NotNull #NotBlank MultipartFile file,
#RequestPart("templatedata") #Valid #NotNull #NotBlank Template template)```

Related

Spring boot rest requestbody and #valid not working when object is null/empty

I am trying to apply not null validation on an attribute of my request which is instructedAmount but it is not working. I have a Spring Boot (V2.3.0.RELEASE) application with the following endpoints:
#Validated
public class TestController {
#PostMapping(value = "/test/pay")
public ResponseEntity<IPSPaymentResponse> validatePayt(#Valid #RequestBody InstantPaymentRequest instantPaymentRequest) {
log.debug("start validatePayment method {}", instantPaymentRequest);
....
The InstantPaymentRequest is as follows:
#Data
#Validated
public class InstantPaymentRequest {
#Valid
private PaymentIdentificationRequest paymentIdentification;
#NotBlank(message = "transactionTypeCode.required")
private String transactionTypeCode;
#Valid
private InstructedAmount instructedAmount;
#Valid
private CustomerRequest debtor;
The instructed amount is as follows:
#Data
public class InstructedAmount {
#NotBlank(message = "currency.required")
private String currency;
#NotBlank(message = "value.required")
private String value;
}
Basically when the instructedAmount is provided in the payload but for example I miss currency attribute in payload, the validation is working fine, the non null error message is displayed correctly and my rest controller endpoint is not called.
However when instructedAmount is not provided in the payload, no mandatory error message is displayed and my rest endpoint is called, it this the correct way or I am missing something?
I thought since attribute of InstructedAmount cannot be null then InstructedAmount also cannot be null/empty.
How to add InstructedAmount not null validation in the above scenario with annotation?
Use #NotNull together with #Valid:
#NotNull
#Valid
private InstructedAmount instructedAmount;
From https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-object-graph-validation:
Note that null values are getting ignored during cascaded validation.

Spring Validating requests params

I want to add validation annotations to my requests params in a Spring annotation. I have the required javax dependencies and code looks something like this
#RestController
public class Controller {
#RequestMapping(value = "/api", method = RequestMethod.GET)
public ExternalUserStatusCollection getUser(
#RequestParam(value = "userId", required = false) #Validated #Size(min = 5) #NotNull UserId userId,
{.....
}
However when I run the application, it doesnt validate the required fields. I dont want to modify the DTO of the object since the intention is to validate some DTO fields coming from external dependencies as well. How can I achieve this using annotations?
Annotating controller with #Validated leads to a bunch of other errors & doesnt help either

Request body Object Not being Validated

I have one REST API which is using a POST Call to create a record, I'm expecting certain Object to be passed in post call, if anything is missing i have to reject straight away from their only,
#RequestMapping(value="/saveEssentialDetails",produces={"application/json"},method=RequestMethod.POST)
ResponseEntity<?> saveEssentialDetails(#ApiParam(value="Body Parameters")#RequestBody #Validated EssentialDetails essentialDetails, BindingResult bindingResult)throws Exception;
and the Essential Model class is as follow
#Data
#NoArgsConstructor
#Document(collection="essentialDetails")
public class EssentialDetails {
#NotNull
Integer dpId;
#Id
#NotEmpty
String tpId;
#NotEmpty
List<FamousFor> famousFor;
#NotEmpty
List<OpenHours> openHours;
#NotEmpty
Pictures uploadedImages;
#NotEmpty
List<FloorDescription> floorDescriptions;
#NotEmpty
List<Outlets> mallOutlets;
}
But while making a Post Call with Missing attributes i'm allowed to make an entry in MongoDB, which i don't want to persist as it's not a proper request,#Validation is not working for me, i'm using spring boot 2.0.6 with MongoDb 4.0.4,
any help would be highly appreciated. Thanks well in advance
#Validated can be used to validate a object with a custom validation object. Example usage:
#RequestMapping(value = "/")
public String request(#Validated(Account.ValidationStepOne.class) Account account)
Instead of using #Validated use #Valid which does check for the validation annotations that you are using in your entity.

Customize endpoints with Spring Data REST

I've a project with Spring Boot 1.5.7, Spring Data REST, Hibernate, Spring JPA, Swagger2.
I've two beans like these:
#Entity
public class TicketBundle extends AbstractEntity {
private static final long serialVersionUID = 404514926837058071L;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Note> notes = new ArrayList<>();
.....
}
and
#Entity
public class Note extends AbstractEntity {
private static final long serialVersionUID = -5062313842902549565L;
#Lob
private String text;
...
}
I'm exposing my methods via Repository:
#Transactional
#RepositoryRestResource(excerptProjection = TicketBundleProjection.class)
#PreAuthorize("isAuthenticated()")
public interface TicketBundleRepository extends PagingAndSortingRepository<TicketBundle, Long> {
....
}
so in swagger I see the endpoint in which I'm interested that is needed to load the collection of notes from a specific ticket bundle:
Now, I want to override the default GET /api/v1/ticketBundles/{id}/notes and replace that with my custom method I put in TicketBundleRepository:
#Transactional(readOnly = true)
#RestResource(rel = "ticketBundleNotes", path = "/ticketBundles/{id}/notes")
#RequestMapping(method = RequestMethod.GET, path = "/ticketBundles/{id}/notes")
#Query("SELECT n FROM TicketBundle tb JOIN tb.notes n WHERE tb.id=:id ORDER BY n.createdDate DESC,n.id DESC")
public Page<Note> getNotes(#Param("id") long id, Pageable pageable);
It's very convenient create the query in this way because I need to use Pageable and return a Page. Unfortunately I've two problems at this point.
First problem
The method is mapped on the endpoint /api/v1/ticketBundles/search/ticketBundles/{id}/notes instad of /api/v1/ticketBundles/ticketBundles/{id}/notes
Second problem
When I call the method from swagger I receive an HTTP 404:
The request seems wrong. Seems the path variable is not understood:
curl -X GET --header 'Accept: application/json' 'http://localhost:8080/api/v1/ticketBundles/search/ticketBundles/{id}/notes?id=1'
This is the response from the server:
{
"timestamp": "2017-10-05T14:00:35.563+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/v1/ticketBundles/search/ticketBundles/%7Bid%7D/notes"
}
without any error on the server side.
Is there a way to override the endpoint GET/api/v1/ticketBundles/{id}/notes exposing it through Repository without using a custom controller (using that I would loose the facilities to manage the Pageable)?
Furthermore, what am I doing wrong to get a HTTP 404 in the call I shown above?
I believe you are using incorrect annotations. You would need to annotate your class with #RestController and use #PathVariable on your method instead of #Param. Here is a working sample, you may want to tailor it according to your needs.
#org.springframework.data.rest.webmvc.RepositoryRestController
#org.springframework.web.bind.annotation.RestController
public interface PersonRepository extends org.springframework.data.repository.PagingAndSortingRepository<Person, Long> {
#org.springframework.web.bind.annotation.GetMapping(path = "/people/{id}")
Person findById(#org.springframework.web.bind.annotation.PathVariable("id") Long id);
}

Change #RequestBody object based on #PathVariable

Below is my application code
#RequestMapping(value = "app/{version}/register", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public RegisterMemberResponse registerMember(#Valid #RequestBody RegisterMemberRequest request,#PathVariable Integer version){
return registerservice.register(request);
}
for app version 1 my RegisterMemberRequest class is
public class RegisterMemberRequest{
#NotNull
private String firstName;
#NotNull
private String lastName;
//gatter & setter...
}
and for app version 2 my request class is
public class LatestRegisterMemberRequest extends RegisterMemberRequest{
#NotNull
private String middleName;
//getter & setter...
}
so how i can i change my registerMember() method in such a way that i can serve both version 1 & 2 request uri.
If you want to hardcode the version number into the url (which I would not recommend), you could easily use two different controller methods with hardcoded paths like app/v1/register and app/v2/register.
Another way would be to use content negotiation and route the requests according to a given content-type, e.g. application/vnd+<YOURCOMPANY>.<DATATYPE_AND_VERSION>+json, also using separate rest controller methods annotated with #Consumes.
I think you can make use of Requestmapping-uri-templates-regex , version number will be the #PathVariable write your business logic to decide what should be the response body depends on pathvariable version.

Resources