Hi I have a REST controller in a SpringBoot application with a number of methods. My GET methods are working fine but my POST method does not work and instead returns an error 405 that the method is not allowed. Any ideas will help; thanks in advance.
#GetMapping("orders")
public List<OrderDto> getOrders() {
List<OrderDto> orderDtos = new ArrayList<>();
// get all the orders using the getOrders method in the OrderService class
List<Order> orders = orderService.getOrders();
// convert each of the returned orders into orderDto
orders.forEach(o->{
orderDtos.add(OrderDto.from(o));
});
return orderDtos;
}
#GetMapping("lineitems")
public List<OrderLineItemDto> getOrderLineItems(){
List<OrderLineItemDto> orderLineItemDtos = new ArrayList<>();
// get line items using the getOrderLineItems service
List<OrderLineItem> orderLineItems = orderLineItemService.getOrderLineItems();
// convert each of the returned order line items into an orderLineItemDto so that the DTO is the one that gets returned to the client
orderLineItems.forEach(ol->{
orderLineItemDtos.add(OrderLineItemDto.from(ol));
});
// return the list of lineItemDtos to the calling client.
return orderLineItemDtos;
}
#GetMapping("lineitems/{id}")
public OrderLineItem getOrderLineItem(#PathVariable Long id) {
return orderLineItemService.getOrderLineItem(id);
}
#PostMapping("order/add")
// #RequestMapping(value = "order",
// consumes = "application/json",
// method = RequestMethod.POST)
public void addOrder(#RequestBody OrderDto orderDto) {
Order order = new Order();
// convert the orderDto into order
order = Order.from(orderDto);
// use the order in the order creation service
orderService.addOrder(order);
}
Response when I invoke the REST service via postman
Related
I have this method Mapping
#PostMapping("**/filtrarprodutospreco")
public void preprocessamentoprodutos(Pageable pageable, FiltroProdutosDto filtro) {
filtro.setFiltrarPor("1");
filtro.setItensPorPag("12");
filtrarProdutos(pageable, filtro);
}
After processing the data I want to call this other method with these objects (pageable and filter) of parameter for the second method:
#PostMapping("**/filtrarprodutos")
public ModelAndView filtrarProdutos(Pageable pageable, FiltroProdutosDto filtro) {
ModelAndView model = new ModelAndView("product");
Categoria categoria = categoriaRepository.findById(filtro.getCategoriaId()).get();
if(filtro.getPrecoDe() == null) {
filtro.setPrecoDe(0D);
}
if(filtro.getPrecoAte() == null) {
filtro.setPrecoAte(1000000000D);
}
if(filtro.getFiltrarPor().contains("1")){
model.addObject("produtos", produtoRepository.filtroProdutos(categoria, filtro.getPrecoDe(), filtro.getPrecoAte(), PageRequest.of(0, Integer.parseInt(filtro.getItensPorPag()), Sort.by("nome"))));
}else {
model.addObject("produtos", produtoRepository.filtroProdutos(categoria, filtro.getPrecoDe(), filtro.getPrecoAte(), PageRequest.of(0, Integer.parseInt(filtro.getItensPorPag()), Sort.by("precoNovo"))));
}
model.addObject("id", filtro.getCategoriaId());
model.addObject("categorias", categoriaRepository.findAll());
model.addObject("filtro", filtro);
return model;
}
This second method uses ModelAndView to redirect the pages, so I want to be called by the first method. Spring calls the second method with the objects and the second method takes those objects and returns to the page configured in (ModelAndView model = new ModelAndView("product")).
How do I make the first method call the second and redirect to a view?
Have the first method return a String and return a redirect to the second method like so:
#PostMapping("**/filtrarprodutospreco")
public String preprocessamentoprodutos(Pageable pageable, FiltroProdutosDto filtro) {
filtro.setFiltrarPor("1");
filtro.setItensPorPag("12");
filtrarProdutos(pageable, filtro);
return "redirect:/filtrarprodutos";
}
I am calling a service in an orders controller which receives a multipart file and processes it and saving it into a database. I am trying to create a Spring Rest Doc for it but it is not even hitting the endpoint. I am creating a list of orders which is what the service expects. It receives the order as a stream as shown and converts into a stream of orders before saving it into a database. I have shown the main part of the controller and my code for generating the rest docs. When I run the code I get the following exception, it never even hits the endpoint when I set a breakpoint. I also used fileupload() but that did not work either.
Exception is:
Content type = application/json
Body = {"path":"/orders/order_reception","exceptionName":
"MissingServletRequestPartException","message":"Required request part 'uploadFile' is not
present",
"rootExceptionName":"MissingServletRequestPartException",
"rootMessage":"MissingServletRequestPartException: Required request part 'uploadFile' is not present"}
#RestController
#RequestMapping(value = "/orders")
#Validated
class OrderController{
#PostMapping(path = "/order_reception")
public ResponseEntity receiveData(#RequestPart MultipartFile uploadFile,
HttpServletRequest request,
HttpServletResponse response) {
if (!uploadFile.isEmpty()) {
try {
Reader reader = new InputStreamReader(request.getInputStream()));
... save file
return new ResponseEntity<>(HttpStatus.HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
#Test
public void sendData() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Order order = repository.getOrder("1233333");
List<Order> orderList = new ArrayList<>():
resourceList.add(order);
MockMultipartFile orderFile = new MockMultipartFile("order-data", "order.json", "application/json",
mapper.writeValueAsString(orderList).getBytes(Charset.defaultCharset()));
mockMvc.perform(multipart("/orders/order_reception")
.file(orderFile))
.andExpect(status().isCreated())
.andDo(document("send-order",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())));
}
Thank you Marten Deinum, your suggestion that the file name was wrong fixed it.
I simply changed name in the MockMultipartFile( "uploadsFile", ...)
I am working with spring boot with a h2 database. I would like to return a 201 message when the register is inserted succesfully and a 400 when is duplicated. I am using ResponseEntity to achieve this, fot example , the next is my create method from the Service:
#Override
public ResponseEntity<Object> createEvent(EventDTO eventDTO) {
if (eventRepository.findOne(eventDTO.getId()) != null) {
//THis is a test, I am looking for the correct message
return new ResponseEntity(HttpStatus.IM_USED);
}
Actor actor = actorService.createActor(eventDTO.getActor());
Repo repo = repoService.createRepo(eventDTO.getRepo());
Event event = new Event(eventDTO.getId(), eventDTO.getType(), actor, repo, createdAt(eventDTO));
eventRepository.save(event);
return new ResponseEntity(HttpStatus.CREATED);
}
This is my controller:
#PostMapping(value = "/events")
public ResponseEntity addEvent(#RequestBody EventDTO body) {
return eventService.createEvent(body);
}
But I'm not getting any message in the browser, I am doing different tests with postman and when I consult for all the events, the result is correct, but each time that I make a post I dont get any message in the browser, I am not pretty sure what is the cause of this issue. Any ideas?
The ideal way to send Response to the client is to create DTO/DAO with ResponseEntity in Controller
Controller.java
#PostMapping("/test")
public ResponseEntity<Object> testApi(#RequestBody User user)
{
System.out.println("User: "+user.toString());
return assetService.testApi(user);
}
Service.java
public ResponseEntity testApi(User user) {
if(user.getId()==1)
return new ResponseEntity("Created",HttpStatus.CREATED);
else
return new ResponseEntity("Used",HttpStatus.IM_USED);
// for BAD_REQUEST(400) return new ResponseEntity("Bad Request",HttpStatus.BAD_REQUEST);
}
Tested using Postman
Status 201 Created
Status 226 IM Used
Okay, I really don't feel good that service sending the ResponseEntity but not Controller.You could use #ResponseStatus and ExceptionHandler classes for these cases, like below.
Create a class in exception package
GlobalExceptionHandler.java
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(DataIntegrityViolationException.class) // NOTE : You could create a custom exception class to handle duplications
public void handleConflict() {
}
}
Controller.java
#PostMapping(value = "/events")
#ResponseStatus(HttpStatus.CREATED) // You don't have to return any object this will take care of the status
public void addEvent(#RequestBody EventDTO body) {
eventService.createEvent(body);
}
Now changing the service would look like,
Service.java
#Override
public void createEvent(EventDTO eventDTO) { // No need to return
if (eventRepository.findOne(eventDTO.getId()) != null) {
throw new DataIntegrityViolationException("Already exists"); // you have to throw the same exception which you have marked in Handler class
}
Actor actor = actorService.createActor(eventDTO.getActor());
Repo repo = repoService.createRepo(eventDTO.getRepo());
Event event = new Event(eventDTO.getId(), eventDTO.getType(), actor, repo, createdAt(eventDTO));
eventRepository.save(event);
}
I have controller:
#PostMapping(value = "/value/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String updateSettings(final Dto dto) {
System.out.println(">>> " + dto);
return "template";
}
Controller works if I send request across chrome window. But when I write test for this method I get problem. Not converted object, value not inserted.
Test:
#Test
#WithMockUser(username = FAKE_VALID_USER, password = FAKE_VALID_PASSWORD)
public void test_B_CreateDtoWithValidForm() throws Exception {
final Dto dto = new Dto();
dto.setId("value");
dto.setEnabled("true");
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.content(dto.toString()))
.andDo(print());
}
Output is >>> Dto{id=null, enabled=false}
How test Post request with custom object in content type application/x-www-form-urlencoded?
In this case you don't need to use content, but instead you need to use param in this way:
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "value")
.param("enabled", "true"))
.andDo(print());
I have a working example of posting only one object, but i dont know how to post a list of object. Here's how im trying to do this :
Client
protected List<EventStudent> doInBackground(Object... params) {
RestTemplate template = new RestTemplate();
template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
EventStudent[] array = new EventStudent[event.size()];
event.toArray(array);
template.postForObject(URL.GET_EVENT_INFO ,array, EventStudent[].class);
return event;
}
this is how im trying to get them on server:
Server
#RequestMapping(value = "/eventstudent", method = RequestMethod.POST)
#ResponseBody
public List<EventStudent> saveRemider(#RequestBody List<EventStudent>event) {
return service.save(event);
}
But it won't work
the problem is generic and type erasure for List , this would be equivalent of List< ? > in controller method.
Create a custom list class just to wrap List into List that can be handled by spring mvc
public class EventStudentList extends ArrayList<EventStudent> {
}
and use it as
#RequestMapping(value = "/eventstudent", method = RequestMethod.POST)
#ResponseBody
public List<EventStudent> saveRemider(#RequestBody EventStudentList event) {
return service.save(event);
}