Spring RestController POST 400 Bad Request - java

I have a Spring RestController that any attempt to post to it returns 400 Bad Request despite seeing the correct data being sent in Chrome Developer Tools. The #Valid annotation is kicking it out because the ParameterDTO object is not being populated at all.
My Controller
#RestController
#RequestMapping(path = "/api/parameters", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
public class ParameterResource {
private final ParameterService parameterService;
#Autowired
public ParameterResource(ParameterService parameterService) {
this.parameterService = parameterService;
}
#GetMapping
public ResponseEntity<?> getParameters(#RequestParam(value = "subGroupId", required = false) Integer subGroupId) {
if (subGroupId != null) {
return ResponseEntity.ok(parameterService.getParameters(subGroupId));
}
return ResponseEntity.ok(parameterService.getParameters());
}
#PostMapping
public ResponseEntity<?> createParameter(#Valid ParameterDTO parameterData) {
int id = parameterService.saveParameter(parameterData);
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(id).toUri();
return ResponseEntity.created(uri).build();
}
#GetMapping(path = "/levels")
public ResponseEntity<?> getParameterLevels() {
return ResponseEntity.ok(ParameterLevels.getParameterLevelMap());
}
#GetMapping(path = "/levels/{id}/values")
public ResponseEntity<?> getLevelValues(#PathVariable("id") int levelId) {
return ResponseEntity.ok(parameterService.getParameterLevelValues(levelId));
}
#GetMapping(path = "/types")
public ResponseEntity<?> getParameterTypes() {
return ResponseEntity.ok(parameterService.getParameterTypes());
}
}
I was using axios from JavaScript and though my problem might be there but I have the same issue using Postman. I am setting the Content-Type and Accept header. It seems like Spring is not deserializing the data at all.

You need to add #RequestBody annotation before ParameterDTO parameterData declaration, like below:
#PostMapping
public ResponseEntity<?> createParameter(#RequestBody #Valid ParameterDTO parameterData) {
int id = parameterService.saveParameter(parameterData);
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(id).toUri();
return ResponseEntity.created(uri).build();
}

Related

How to add content-type header application/json to void postmapping endpoint?

I have an endpoint like this:
#RestController
#RequiredArgsConstructor
#RequestMapping(path = "/api/notifications", produces = APPLICATION_JSON_VALUE)
public class NotificationController {
#PostMapping
#ResponseStatus(value = HttpStatus.CREATED)
public void sendNotification(...) {
...
}
}
While testing with pact tests I noticed that the api does not return "application/json" as Content-Type header. What should I do so it does?
Try this one instead
#PostMapping
public ResponseEntity<Void> sendNotification(...) {
...
return ResponseEntity.status(HttpStatus.CREATED).build();
}
If this does not return by default Application/Json then you can do
return ResponseEntity.status(HttpStatus.CREATED).contentType(MediaType.APPLICATION_JSON).build();

Java Spring 5 Get and findById mongo MonoOnErrorResume

Spring newbie here, trying to make a GET http query in a mongo db via findById(id, Object).
But it doesn't seem to be working. I can POST and PUT but when calling a query via ID i get this err MonoOnErrorResume
I'm using EmbeddedMongoDB
Controller
public class ContentController {
public static final String CONTENT_V_1_CONT = "/contents/v1/cont/";
private final ContentService contentService;
#Autowired
public ContentController(ContentService contentService) {
this.contentService = contentService;
}
#GetMapping(path = "{id}", produces =
MediaType.APPLICATION_JSON_UTF8_VALUE)
public Mono<Content> getContent(#PathVariable String id) {
System.out.println(contentService.getContent(id)); //
MonoOnErrorResume
return contentService.getContent(id);
}
#PostMapping(path = "", produces =
MediaType.APPLICATION_JSON_UTF8_VALUE, consumes =
MediaType.APPLICATION_JSON_UTF8_VALUE)
public Mono<Content> createContent(#RequestBody Mono<Content> content){
return contentService.createContent(content);
}
Service Implmentation
public final ReactiveMongoOperations reactiveMongoOperations;
#Autowired
public ContentServiceImplementation(ReactiveMongoOperations reactiveMongoOperations) {
this.reactiveMongoOperations = reactiveMongoOperations;
}
#Override
public Mono<Content> getContent(String id) {
return reactiveMongoOperations.findById(id, Content.class);
}
#Override
public Mono<Content> createContent(Mono<Content> contentMono) {
return reactiveMongoOperations.save(contentMono);
}
Data Config Dont know is this is useful
#Bean
public ReactiveMongoDatabaseFactory mongoDatabaseFactory(MongoClient mongoClient){
return new SimpleReactiveMongoDatabaseFactory(mongoClient, DATABASE_NAME);
}
#Bean
public ReactiveMongoOperations reactiveMongoTemplate(ReactiveMongoDatabaseFactory mongoDatabaseFactory){
return new ReactiveMongoTemplate(mongoDatabaseFactory);
}
Lmk if i'm missing some critical info
Your problem may come from your controller, you declare your path like so:
#GetMapping(path = "{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
So unless you have a / at the end of your controller class mapping you will have issues because your final URL will look like this :
http://localhost:8080/my/route/get1
instead of :
http://localhost:8080/my/route/get/1
Your #PathVariable looks strange as well, try doing this instead :
#PathVariable("id") String id
To ensure Spring is going to map {id} to your #PathVariable

#GetMapping, ambigious mapping

I'm trying to create several routes for #GetMapping. For example, localhost:8080/tasks and localhost:8080/tasks/?status=...
So I created several methods as below.
Controller
#RestController
#RequestMapping(value = "/tasks", produces = MediaType.APPLICATION_JSON_VALUE)
#ExposesResourceFor(Task.class)
public class TaskRepresentation {
private final TaskResource taskResource;
public TaskRepresentation(TaskResource taskResource) {
this.taskResource = taskResource;
}
#GetMapping
public ResponseEntity<?> getAllTasks() {
return new ResponseEntity<>(this.taskResource.findAll(), HttpStatus.OK);
}
#GetMapping
public ResponseEntity<?> getTasksStatus(#RequestParam("status") int status) {
return new ResponseEntity<>(this.taskResource.getTasksByStatus(status), HttpStatus.OK);
}
}
Resource
#RepositoryRestResource(collectionResourceRel = "task")
public interface TaskResource extends JpaRepository<Task, String> {
#GetMapping
List<Tache> getTasksByStatus(#RequestParam int status);
}
Error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'taskRepresentation' method
public org.springframework.http.ResponseEntity<?> org.miage.tache.boundary.TacheRepresentation.getTasksStatus(int)
to {GET /tasks, produces [application/json]}: There is already 'taskRepresentation' bean method
(The only solution is to create only one route for #GetMapping with optionnal params?)
Can you help me ?
Thanks for help.
Coming from the other answer, as this one more specific.
You can narrow down your endpoint mapping by specifying the needed query parameters.
#GetMapping
public ResponseEntity<?> getAllTasks() {
return ResponseEntity.ok().body(this.taskResource.findAll());
}
#GetMapping(params = "status")
public ResponseEntity<?> getAllTasksWithStatus(#RequestParam("status") final int status) {
return ResponseEntity.ok().body(this.tacheResource.getTachesByEtat(status));
}
Docs link.
Note : As params is an array, you can specify multiple values with
#GetMapping(params = { "status", "date" })
You can do something like this :
#RestController
#RequestMapping(value = "/tasks", produces = MediaType.APPLICATION_JSON_VALUE)
#ExposesResourceFor(Task.class)
public class TaskRepresentation {
private final TaskResource taskResource;
public TaskRepresentation(TaskResource taskResource) {
this.taskResource = taskResource;
}
#GetMapping
public ResponseEntity<?> getTasksStatus(#RequestParam(value="status", required=false) Integer status) {
if(status==null){
return new ResponseEntity<>(this.taskResource.findAll(), HttpStatus.OK);
}
return new ResponseEntity<>(this.taskResource.getTasksByStatus(status.intValue()), HttpStatus.OK);
}
}

Is it possible in Spring MVC to have void handlers for requests?

Is it possible in Spring MVC to have void handler for request?
Suppose I have a simple controller, which doesn't need to interact with any view.
#Controller
#RequestMapping("/cursor")
public class CursorController {
#RequestMapping(value = "/{id}", method = PUT)
public void setter(#PathVariable("id") int id) {
AnswerController.setCursor(id);
}
}
UPD
#Controller
#RequestMapping("/cursor")
public class CursorController {
#RequestMapping(value = "/{id}", method = PUT)
public ResponseEntity<String> update(#PathVariable("id") int id) {
AnswerController.setCursor(id);
return new ResponseEntity<String>(HttpStatus.NO_CONTENT);
}
}
you can return void, then you have to mark the method with
#ResponseStatus(value = HttpStatus.OK) you don't need #ResponseBody
#RequestMapping(value = "/updateSomeData" method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.OK)
public void defaultMethod(...) {
...
}
Only get methods return a 200 status code implicity, all others you have do one of three things:
Return void and mark the method with #ResponseStatus(value = HttpStatus.OK)
Return An object and mark it with #ResponseBody
Return an HttpEntity instance
Also refer this for interesting information.

defining default url for controller spring mvc3

I am new to spring mvc3 development and was facing a minor issue (which I was didn't face with ASP.Net MVC3). I want to know the process of defining a default (or landing) URL for a controller.
I have an accounts controller where I do all account management related stuff. So all my urls are mapped to this controller. I want to know that how can I map my "/accounts" url request to hit openAccountsDashboard method?
Code -
.... imports...
#Controller
#RequestMapping(value = "/accounts/*")
public class AccountController {
#RequestMapping( value = "/", method = RequestMethod.GET)
public ModelAndView openAccountsDashboard(HttpServletRequest request) {
.....
return new ModelAndView("accounts/landing");
}
#RequestMapping( value = "/change-password", method = RequestMethod.GET)
public ModelAndView openPasswordChangePage(HttpServletRequest request) {
.....
return new ModelAndView("accounts/passwordChange");
}
... other actions...
}
Any help would be great!
Thanks
Try something like this:
.... imports...
#Controller
#RequestMapping(value = "/accounts/")
public class AccountController {
#RequestMapping( value = "", method = RequestMethod.GET)
public ModelAndView openAccountsDashboard(HttpServletRequest request) {
.....
return new ModelAndView("accounts/landing");
}
#RequestMapping( value = "/change-password", method = RequestMethod.GET)
public ModelAndView openPasswordChangePage(HttpServletRequest request) {
.....
return new ModelAndView("accounts/passwordChange");
}
... other actions...
}
Then you can use url like this:
http://localhost:8080/yourwebapp/accounts/
to hit openAccountsDashboard method.
Regards,
Arek

Categories

Resources