I am developing REST api using Spring Boot. I've a controller which accepts POST requests.
http://localhost:8085/carride/end-ride
In the above request i want to access the parameter ride_transection_id for finding particular transection object and also some other value as well.
So basically i have 3 way to do that.
1. i can use #PathVariable
#RequestMapping(value = "/end-ride", method = RequestMethod.POST)
public ResponseEntity<?> endRide(#PathVariable("ride_transection_id") long ride_transection_id,#RequestBody
SomeDTORequest someDTORequest ) {
//find transaction using path varibale
}
2.i can use #RequestParam
#RequestMapping(value = "/end-ride", method = RequestMethod.POST
public #ResponseBody item getitem(#RequestParam("ride_transection_id")
long ride_transection_id,#RequestBody SomeDTORequest someDTORequest ){
//find transaction using RequestParam varibale
}
i can use DTO Object SomeDTORequest and accept ride_transection_id into that with other value as well.
#RequestMapping(value = "/end-ride", method = RequestMethod.POST)
public ResponseEntity<?> endRide(#RequestBody SomeDTORequest someDTORequest ) {
//find transaction using path someDTORequest .getID()
}
i am little bit confuses.just want ask which is safest and right way to access the ride_transection_id ?
thanks
You can use any of them but every way is designed for a certain use.
Path variable:
is used when you need to access an entity using a certain field for example i want to access an order and this order is defined by id so to access this order i need the following request Get /order/{id}
Request Parameter:
when you want to send a specific variable or flag for a certain method
for example Get /orders?is_shipped=true, so this will get all shipped orders or you may need orders at certain page Get /orders?page=1
Request body:
when you need to update the entity by the put or patch request as you will update the entity using the entity's json representation which can be send through the request body
for example PUT /orders/{id}
body: {"title": "order_1"}
then the order with id {id} will be updated with the new title
Spring data rest
See also
Basically, all these 3 methods are fine. But if you want to develop or design RESTful services with best practices, I strongly recommend you should provide the querying service with #PathVariable and GET method such as GET /tickets/12. Otherwise, to digest request body with #RequestBody annotation to retrieve querying criteria for POST method is the second suggestion.
Because POST method is usually to be used for creating something. And for querying something, both #PathVariable and #RequestParam annotations are suitable for GET method. More specifically, #RequestParam is often to be used in Filtering, Sorting and Searching results. For example:
Filtering: GET /tickets?state=open - Here, state is a query parameter that implements a filter.
Sorting: GET /tickets?sort=-priority,created_at - Retrieves a list of tickets in descending order of priority. Within a specific priority, older tickets are ordered first.
Searching: GET /tickets?state=closed&sort=-updated_at - Retrieve recently closed tickets.
Please also refer to this article Best Practices for Designing a Pragmatic RESTful API.
Hope this helps you! :)
Related
I am going to implement a POST API in an existing resource class StudentResource, which contains already a POST REST API in order to create a Student.
The new POST API is basically for validating a Student entity, of the following form:
POST /api/student/validate
Request Body:
{
student_id : <>,
validation_type : <>
}
Now, I am not able to understand, should I create a new resource class: StudentValidationResource to put this new validate REST API or should I put this in the existing StudentResource.
EDIT:
The intent of the question is actually whether should I put more than one POST API in a single resource class.
Could anyone please help here?
Have you think about using validator? With javax #valid directly for your controller params / body?
You juste have to set the constraints in the object if the post receive an object, or directly in the controller like
#PostMapping("someurl")
public void myController(#valid #requestParams #size(min=2, Max=10) String aString) {}
I have a doubt if valid come before or after the requestParams.
I'm creating an order service, new to RestServices world.
I need to read the order model into a OrderDTO and persist in the DB.
For that I have a below method:
#PostMapping(produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<OrderDTO> createOrder(#Valid #RequestBody OrderDTO orderDTO) {
Order order = new Order(orderDTO);
Order createdOrder = orderService.createOrder(order);
OrderDTO createdOrderDTO = new OrderDTO(order);
ResponseEntity<OrderDTO> responseEntity = new ResponseEntity<OrderDTO>(createdOrderDTO, null, HttpStatus.CREATED);
return responseEntity;
}
Everything working fine, but I have concerns about the current design:
I'm reading an input into DTO
To Store the object I'm converting into Order object which will be persisted by Hibernate
Again to send the response back I'm converting the actual order object into DTO.
finally I will create 4-5 Objects per a request, if my app got 100 request it may run into memory issue.
How i can read the model data and persist efficiently?
In general, prefer DTO because of single responsibility principle, every object have its own responsibility and It's also clearer to separate View/Controller from Model objects
You can sometimes reduce OrderDTO, use an object that is both DTD and real Object,
It'll include DTD properties and also other properties that you can add using builder for example, I'm using #JsonIgnoreProperties(ignoreUnknown = true) to set only the DTD properties when object is created from request, e.g.:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_NULL)
public class Order
You can also use JsonGetter/JsonProperty/JsonSetter to control what expected/returned
#JsonGetter and #JsonSetter are old alternatives to #JsonProperty.
I prefer a Mapper like Mapstruct:
OrderDtoMapper mapper = new OrderDTOMapper();
Order order = OrderDtoMapper.map(orderDto, Order.class);
and back:
OrderDTO createdOrderDTO = OrderDtoMapper.map(order, OrderDTO.class);
For me the code looks more readable ... and you do not have much to write for, as Mapstruct maps it automatically. Because it looks like you will map quite a lot ;)
Perhaps a mapper is worth a try: http://mapstruct.org/
I don't see any issue with the design.
As Nizet pointed out. Objects created are short lived.
Normally DTO and Entity design is followed to keep the UI and Service Layer separate.
In this way, you have the option to filter out sensitive info from being passed to the world like password, pin.
But if you want you can use Order entity directly in Controller class.
I won't suggest that but it's possible.
I need to implement PATCH functionality at my Spring #RestController.
I saw a lot of questions and the most common approach is to use a plain Java Map to do this. Map that allows null helps to solve the issue with null or absent values because it looks like impossible to implement on POJO.
Is there at Spring any out of the box functionality that helps to reflect values from Map to the existing model object or I have to implement it by myself.. for example using Jackson and so on ?
I can share My implementation of PATCH, hope this helps some one in some way. I have a client which has six fields like ( name, type, address fields , ID, Number, postcode), I can edit the client and change anything.
this is also an elaboration on the question with a partial answer ( or a complete one if there is no other way than the two below) Or perhaps PATCH is supposed to be done differently
clientService is just a service which holds the ClientRepository
#RequestMapping(value = "/{id}", method = RequestMethod.PATCH ,produces = {"application/vnd.api+json"} )
ResponseEntity<Resource<Client>> editClient(#PathVariable("id") Integer id,#RequestBody Client editedClientFromBrowser) {
// the id is the ID of the client that I was editing..
//i can use this to retrieve the one from back end
// editedClientFromBrowser is the changed Client Model Object
//from the browser The only field changed is the one
// that was changed in the browser but the rest of
//the object is the same with the ID as well
logger.info("Edit Client reached");
//retreive the same Client from backend and update and save it
// Client client = clientService.getClient(id);
// if (client == null) {
// throw new EntityNotFoundException("Client not found - id: " + id);
// }else{
// change the field that is different from the
//editedClientFromBrowser and then saveAndFlush client
//}
//OR saveAndFlush the editedClientFromBrowser itself
clientService.saveAndFlush(editedClientFromBrowser);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
now another method I read on (http://www.baeldung.com/http-put-patch-difference-spring) and tried:
#RequestMapping(value = "/{id}", method = RequestMethod.PATCH ,
produces = {"application/vnd.api+json"} )
ResponseEntity<Resource<Client>> editClient(#PathVariable("id") Integer id,
#RequestBody Map<String, Object> updates)
this one does give me a hashMap. but it gives me each and every field. even the ones I did not change. So, is that really beneficial? No idea seriously, may be it is lighter than getting the whole client object back.
I would have liked if I get only the hashmap of one or two fields which I did change. that would have been more in line with PATCH i think. Can I improve my two implementations in some way?
I am pretty new in Spring MVC and I have the following doubt about how correctly achieve the following task.
I am working on a web application that implement a user registration process. This registration process is divided into some consecutive steps.
For example in the first step the user have to insert a identification code (it is a code that identify uniquely a user on some statal administration systems) and in the second step it have to compile a form for his personal data (name, surname, birth date, and so on).
So, actually I have the following controller class that handle these steps:
#Controller
public class RegistrazioneController {
#Autowired
private LoadPlacesService loadPlacesService;
#RequestMapping(value = "/iscrizioneStep1")
public String iscrizioneStep1(Model model) {
return "iscrizioneStep1";
}
#RequestMapping(value = "/iscrizioneStep2", method=RequestMethod.POST)
public String iscrizioneStep2(Model model, HttpServletRequest request, #RequestParam("cf") String codicFiscale) {
System.out.println("INTO iscrizioneStep2()");
//String codicFiscale = request.getParameter("cf");
System.out.println("CODICE FISCALE: " + codicFiscale);
model.addAttribute("codicFiscale", codicFiscale);
return "iscrizioneStep2";
}
#RequestMapping(value = "/iscrizioneStep3", method=RequestMethod.POST)
public String iscrizioneStep3(#ModelAttribute("SpringWeb")Step2FormCommand step2Form, ModelMap model, HttpServletRequest request) {
System.out.println("INTO iscrizioneStep3()");
System.out.println("NOME: " + step2FormCommand.getName());
return "iscrizioneStep3";
}
Into the iscrizioneStep2() it is retrieved the first code (#RequestParam("cf") String codicFiscale).
Into the iscrizioneStep3() it is retrieved a command object containing the data inserted into the form of the view in which this form was submitted, this one:
#ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand
It works fine.
Now my problem is that I have another object named Step3View that have to be initialized with the aggregation of the #RequestParam("cf") String codicFiscale object retrieved into the iscrizioneStep2() method and the #ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand retrieved into the iscrizioneStep3() method.
This Step3View class simply contain the String codicFiscale and all the fields of the Step2FormCommand class.
Now my doubts are: what is the best way to handle this situation? Where have I to declare this Step3View object? at controller level? (so I can use it in all my controller methods?). Have I to annotate this class with #Component (or something like this) to inject it in my controller?
What is the best solution for this situation?
I think in order to get an answer you need to understand the question and ask the right question. I think your question is "how do I pass a parameter from one page to another page in SpringMVC?". You specifically want to know how to pass the "cf" param, but readers here will tend to pass over questions that are too specific because it takes too much time to figure out what you want.
In answer to that, see Spring MVC - passing variables from one page to anther as a possible help.
Also, there are many good answers about this question for JSP in general, which can be worked into the SpringMVC architecture. See How to pass value from one jsp to another jsp page? as a possible help.
Spring documentation says:
Use the #RequestParam annotation to bind request parameters to a
method parameter in your controller.
AFAIK, request parameters are variables retrieved from query strings if the request method is GET. They are also the variables retrieved from the form values when the request method is POST. I've verified this using a simple JSP that displays request parameters through method request.getParameter("key").
But it seems to me that #RequestParam only works on GET method requests. It can only get values from query strings.
Is this a bug in the documentation? Can someone please cite me some documentation that describes exactly what #RequestParam is used for, what it cannot be used for, and how it gets populated?
Can I use #RequestParam for POST methods to get the form values? If I can't use #RequestParam, what else can I use? I'm trying to avoid calling request.getParameter("key").
It works with posts too. Can you post your method body and you html?
Yes it works perfectly with post method too. you can mention the method attribute of #RequestParam as RequestMethod=POST. Here is the code snippet
#RequestMapping(value="/register",method = RequestMethod.POST)
public void doRegister
(
#RequestParam("fname") String firstName,
#RequestParam("lname")String lastName,
#RequestParam("email")String email,
#RequestParam("password")String password
)
Instead of #RequestParam which binds to a single form value, you can use #ModelAttribute annotation and bind to the whole object. But it should be used in conjunction with form or bind Spring's JSTL.
Example:
- controller that calls JSP-page, it should add objects to a Model:
#RequestMapping(value="/uploadForm", method=RequestMethod.GET)
public String showUploadForm(Model model) {
Artist artist = new Artist();
Track track = new Track();
model.addAttribute("artist", artist);
model.addAttribute("track", track);
return "uploadForm";
}
JSP might look something like that:
Track Title *:
Controller that processes form submission;
#RequestMapping(value="/uploadToServer", method=RequestMethod.POST)
public String uploadToServer(#ModelAttribute("artist") Artist artist, #ModelAttribute("track") Track track) { .... }
Here I found a good explanation of using #ModelAttribute annotation - krams915.blogspot.ca