I am getting Bad Request when trying to do POST to createCategory end point. All endpoint works but not for createCategory. Am i doing something wrong?
All my DTO classes using the same annotation but only this one doesn't work. Is it possible that spring doesn't accept single variable response body?
endpoint: http://localhost:8180/api/v1/categories
request body in json:
{
"name": "Category 1"
}
CategoryController:
#RestController
#RequiredArgsConstructor
#RequestMapping("api/v1/categories")
public class CategoryController {
private final CategoryApplicationService categoryApplicationService;
#PostMapping
public ResponseEntity<Data<CategoryIDResponse>> createCategory(#RequestBody CreateCategory createCategory){
return new ResponseEntity<>(new Data<>(categoryApplicationService.createCategory(createCategory)), HttpStatus.CREATED);
}
#PatchMapping
public ResponseEntity<Data<CategoryIDResponse>> updateCategory(#RequestBody UpdateCategory updateCategory){
return new ResponseEntity<>(new Data<>(categoryApplicationService.updateCategory(updateCategory)), HttpStatus.OK);
}
#DeleteMapping("/{categoryID}")
public ResponseEntity<Data<CategoryIDResponse>> deleteCategory(#PathVariable("categoryID") UUID categoryID){
return new ResponseEntity<>(new Data<>(categoryApplicationService.deleteCategory(categoryID)), HttpStatus.OK);
}
#GetMapping("/{categoryID}")
public ResponseEntity<Data<GetCategoryResponse>> getCategory(#PathVariable("categoryID") UUID categoryID){
return new ResponseEntity<>(new Data<>(categoryApplicationService.getCategory(categoryID)), HttpStatus.OK);
}
#GetMapping
public ResponseEntity<Data<List<GetCategoryResponse>>> getAllCategory(){
return new ResponseEntity<>(new Data<>(categoryApplicationService.getAllCategory()), HttpStatus.OK);
}
}
DTO:
CreateCategory:
#Getter
#Builder
#AllArgsConstructor
public class CreateCategory {
#NotNull
private final String name;
}
This is happening because you have not added NoArgsConstructor in your DTO.
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class CreateCategory {
private String name;
}
Change your DTO to above code. Working fine for me.
Hope this helps.
Actually, you need to add #Setter annotation to the CreateCategory class by removing the final keyword, because spring will set the fields to the objects using setter methods, and you cant use setters with final variables.
Related
I'm running into a failure, but I can't figure out why. For a simple test, I want to quickly create a RESTful API to accept a JSON and convert it to an object (POJO).
Restcontroller:
#RestController
public class RESTController {
#Autowired
AuthorService authorService;
#RequestMapping(value = "/setsAuthor", headers = "Accept=application/json", method=RequestMethod.POST)
public void addAuthor(#RequestBody Author author){
authorService.addAuthor(author);
}
}
Data object:
#AllArgsConstructor
#Entity
#NoArgsConstructor
public class Author {
#Id
private Integer id;
private String authorName;
}
Service:
#Service
public class AuthorService {
#Autowired
AuthorRepository authorRepository;
public void addAuthor(Author author) {
try {
authorRepository.save(author);
} catch (IllegalAccessError e) {
System.out.println("cant save Author: " + author + " to Respository");
}
}
Repository interface:
public interface AuthorRepository extends CrudRepository<Author, Integer> {
}
Post Request at
http://localhost:8080/setsAuthor
With JSON:
{
"id": 1,
"authorName": "SomeAuthor"
}
I also tried to wrap the Items in the JSON in a "Author" :{}
The RestController won't map my JSON to the object. It always says id = null, authorName = null when I debug the program.
What am I doing wrong? I remember it always worked this way.
Add #Getter and #Setter to entity
#AllArgsConstructor
#Entity
#NoArgsConstructor
#Getter
#Setter
public class Author {
You can also use
#AllArgsConstructor(onConstructor = #__(#JsonCreator))
instead of #Setter
I am using spring boot's Rest Controller for creating rest end points. Along with swagger 2 for api documentation.
#RestController
#RequestMapping("/api")
public class BatchController extends ControllerConfig {
#PostMapping("/batch")
public GeneralResponse<Boolean> createBatch(#RequestBody Batch batch) throws Exception{
try{
batchService.createBatch(batch);
return new GeneralResponse<>(true,"batch created successfully", true, System.currentTimeMillis(), HttpStatus.OK);
} catch (Exception e){
return new GeneralResponse<>(false,e.getMessage(), false, System.currentTimeMillis(), HttpStatus.BAD_REQUEST);
}
}
#PutMapping("/batch")
public GeneralResponse<Boolean> updateBatch(#RequestBody Batch batch) {
try {
batchService.updateBatch(batch);
return new GeneralResponse<>(true, "batch updated successfully", true, System.currentTimeMillis(), HttpStatus.OK);
} catch (Exception e) {
return new GeneralResponse<>(false, e.getMessage(), false, System.currentTimeMillis(), HttpStatus.BAD_REQUEST);
}
}
}
And Batch Model :
#Entity
#Table
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class Batch {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long qualityId;
private Date date;
private String remark;
}
I am using JPA repository.
Now, For both the rest end points Swagger will show the request model as :
{
id: 0,
qualityId: 0,
date: "2020-10-04T21:18:00.656Z",
remark: "string"
}
but I want to hide "id" field for create batch request as that is autogenerated, but its required for update as that is based on id.
how can that be done?
Entities are not supposed to be exposed in the API layer,
You should create a dedicated DTO classes instead.
For example-
#Data
public class PutBatchDTO {
private Long id;
private Long qualityId;
private Date date;
private String remark;
}
#Data
public class PostBatchDTO {
private Long qualityId;
private Date date;
private String remark;
}
I use spring-test 5.0.7 and run into the next issue:
DTOs:
#Data
#AllArgsConstructor
#NoArgsConstructor
public static class SomeDTO {
private String uid;
private Object child;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
public static class AnotherDTO {
private String someField;
}
Controller:
#RestController
#RequestMapping("/api")
public static class TestController {
#GetMapping(path = "/dto/{uid}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public SomeDTO findSomeObject(#PathVariable String uid) {
return new SomeDTO(uid, new AnotherDTO("value"));
}
}
And a test that is failing:
#Test
public void testControllerResult() throws Exception {
SomeDTO dto = new SomeDTO(UUID.randomUUID().toString(), new AnotherDTO("value"));
mockMvc.perform(MockMvcRequestBuilders.get("/api/dto/{uid}", dto.getUid()))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$").value(dto));
}
Test logs:
java.lang.AssertionError: JSON path "$"
Expected :TestTest.SomeDTO(uid=7817869a-eb9a-4491-a34b-a8006a643b6c, child=TestTest.AnotherDTO(someField=value))
Actual :null
Is there any way to make it working without specifying type of field SomeDTO.child? I guess it does not work because SomeDTO does not provide information about field child(as soon as I make it private AnotherDTO child, test will pass), but it can be calculated at runtime.
I'm trying to implement the server side validation using spring. but its not validating. Here is my code sample.
#RestController
#RequestMapping("/api/v1/note")
public class NoteController {
#Autowired
private final NoteService noteService;
#PostMapping
public ResponseEntity<String> create(#Valid #RequestBody final NoteDto noteDto){
noteService.create(noteDto);
return new ResponseEntity<>("sucess", HttpStatus.CREATED);
}
}
POJO..
#Data
#JsonInclude(value = Include.NON_NULL)
public class NoteDto {
#NotEmpty(message = "Building No can't be empty!")
private String buildingNo;
private String buildingName;
#NotEmpty(message = "Street can't be empty!")
}
What am missing here
#Valid annotation that triggers validations on the NoteDto (in this case #NotNull and #Future). These annotations could come from different JSR-303 providers (e.g, Hibernate, Spring..etc).
Example
static class NoteDto {
#NotNull #Future
private Date date;
}
And Remove final.
I have the following domain object and DTO defined.
Country.java
#Data
#Entity
public class Country extends ResourceSupport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long countryID;
#NotBlank(message = "Country name is a required field")
private String countryName;
private String countryNationality;
}
CountryDTO.java
#Data
public class CountryDTO {
private List<Country> countries;
}
I have overridden the POST method in the RepositoryRestController for the country class.
#RepositoryRestController
public class CountryController {
#Autowired
private CountryRepository repo;
#RequestMapping(method = POST, value = "countries")
public #ResponseBody ResponseEntity<?> createCountry(#RequestBody Resource<CountryDTO> dto,
Pageable page, PersistentEntityResourceAssembler resourceAssembler) {
Country savedCountry = repo.save(dto.getContent().getCountries());
return new ResponseEntity<>(resourceAssembler.toResource(savedCountry), HttpStatus.OK);
}
}
Now I have defined a RepositoryEventHandler to handle validations.
#Component
#RepositoryEventHandler
public class CountryHandler {
#HandleBeforeCreate
public void handleBeforeCreate(Country country) {
System.out.println("testing");
}
But when I send a POST request to the endpoint http://localhost:8080/countries, the eventhandler does not get invoked. Is there anything I am doing wrong?
UPDATE 1:
I am sending the following JSON to the endpoint using Postman.
"countries":[{
"countryName":"Australia",
"countryNationality":"Australian"
}]
It is difficult to give you an exact solution not knowing how you are invoking the request. But possible reason is that you are missing the slash symbol #RequestMapping value attribute:
#RequestMapping(method = POST, value = "countries")
Should be:
#RequestMapping(method = POST, value = "/countries")
Define a Bean in AppConfigration as
#Configuration
#EnableAsync
public class AppConfig {
#Bean
CountryHandler countryHandler (){
return new CountryHandler ();
}
}
It will work then.
Try editing maybe the Controller class annotation from:
#RepositoryRestController
to
#RestController
and mainly the method annotation from:
#RequestMapping(method = POST, value = "countries")
to
#RequestMapping(value = "/countries", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
PS: produces = MediaType.APPLICATION_JSON_VALUE if you are going to return json.
I know this is older but this works as it is supposed to.
The methods defined in a #RepositoryRestController implementation replace the methods in the default RepositoryEntityController which publish #RepositoryEventHandler events.
So your controller needs to publish a create event:
#RepositoryRestController
public class CountryController {
#Autowired
private CountryRepository repo;
private final ApplicationEventPublisher publisher; //This changed
#RequestMapping(method = POST, value = "countries")
public #ResponseBody ResponseEntity<?> createCountry(#RequestBody Resource<CountryDTO> dto,
Pageable page, PersistentEntityResourceAssembler resourceAssembler) {
Country savedCountry = repo.save(dto.getContent().getCountries());
publisher.publishEvent(new BeforeCreateEvent(savedCountry)); //This changed
return new ResponseEntity<>(resourceAssembler.toResource(savedCountry), HttpStatus.OK);
}
}