Use ModelAndView in a Rest API app - java

I'm writing a web app which only consists of Rest API endpoints. There is no jsp, nor any UI written in Java provided by the app. (I'm planning to write the front end in React in future)
Does it make sense to use ModelAndView in my controllers ? I want to do a redirect to another URL ? I see sample codes similar to the following code:
#Controller
#RequestMapping("/")
public class RedirectController {
#GetMapping("/redirectWithRedirectPrefix")
public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) {
model.addAttribute("attribute", "redirectWithRedirectPrefix");
return new ModelAndView("redirect:/redirectedUrl", model);
}
}

If your controller is always going to redirect, you can just return a String e.g.
return "redirect:/redirectedUrl";
Otherwise, you can return a response entity:
final HttpHeaders headers = new HttpHeaders();
headers.add("Location", "/redirectedUrl");
return new ResponseEntity<Foo>(headers, HttpStatus.FOUND);
I don't think it makes sense to return a ModelAndView if there is no view.

Related

Best way of sending REST responses in spring boot

What is the best way to send rest responses in spring boot? Also how should i manage sending status codes to do it properly?
Currently i do it using ResponseEntity but i doubt this is the most elegant way.
Sample code:
#PostMapping()
public ResponseEntity post(#Valid #RequestBody Item item, BindingResult bindingResult){
if (bindingResult.hasErrors()){
return new ResponseEntity<>(new ModelErrors(bindingResult), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(itemService.addItem(item), HttpStatus.CREATED);
}
ModelErrors class extends a HashMap class and just fetches and wraps the BindingResult's error messages.
Personally I think that returning ResponseEntity is going to be the best choice for a lot of cases. A little more readable way of doing it in my opinion is to use the handy status methods on ResponseEntity like this
#PostMapping()
public ResponseEntity post(#Valid #RequestBody Item item, BindingResult bindingResult){
if (bindingResult.hasErrors()){
return ResponseEntity.badRequest().body(new ModelErrors(bindingResult));
}
return ResponseEntity.created().body(itemService.addItem(item));
}
Alternatively, you can use the status method passing a HttpStatus or status code like this
ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ModelErrors(bindingResult));
ResponseEntity.status(201).body(itemService.addItem(item));
Another option is to just return whatever type you'd like without using ResponseEntity, but this gives you a lot less control over the response and requires that you have the proper MessageConverter configuration (you can read up on that here).
A simple example might look like this
#RequestMapping("/hotdog")
public Hotdog hotdog() {
return new Hotdog("mystery meat", "ketchup, mustard");
}
and if everything is configured correctly you'd end up with a response like this
{
"content": "mystery meat",
"condiments": "ketchup, mustard"
}

Routing in Java web-application

I created a web-application using Spring MVC/jsp. To navigate from one view to another I am using spring controller like the following -
#RequestMapping(value = { "/transaction" }, method = RequestMethod.GET)
public ModelAndView defaultPage() {
return new ModelAndView("transaction");
}
#RequestMapping(value = {"/getPartnerList"}, method = RequestMethod.POST)
#ResponseBody
List<PartnerList> viewTransactionReport() {
logger.info("inside getPartnerList");
List<PartnerList> partnerList = CommonQuery.getPartnerList();
logger.info("Partner List Size " + partnerList.size());
return partnerList;
}
I first send the view request to the controller and after that I place the data-driven call to get JSON data.
Had I been using AngularJS, I could have used ng-route for navigation and sent only data-driven calls to the controller.
Is my approach correct in terms of efficiency and best practices? Thanks.

Spring REST service to consume and produce both HTML form POST and AJAX/JSON in a single method

I'm trying to teach myself Spring by creating a very simple web application. I have a class to create "Note" objects:
#Controller
#RequestMapping(value = "/notes")
public class NoteRestController {
#Autowired
private MappingJackson2JsonView jsonView;
[...]
#ResponseStatus(HttpStatus.CREATED)
#RequestMapping(method = RequestMethod.POST, consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_FORM_URLENCODED_VALUE })
public ModelAndView create(final Model model,
#Valid #ModelAttribute final Note note, final BindingResult result) {
ModelAndView mav;
// how can I test the request source?
if (<requesting from HTML FORM>) {
// return jsonView
mav = new ModelAndView(jsonView);
} else {
// return JSP view
mav = new ModelAndView("note", "model", model);
}
model.addAttribute("note", note);
if (result.hasErrors()) {
model.addAttribute("errors", result.getAllErrors());
// on error, redirect back to note page with form
// return new ModelAndView("note/note", "model", model);
return mav;
}
note.setId(daoService.createNote(note));
return mav;
}
}
I would like to be able to use a single method (like the above) to handle requests from both an AJAX post AND a HTML form post. If triggered by AJAX I would like to return JSON (with validation errors if present), and if it is triggered by a HTML form, I would like to return to the JSP using the form taglib
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
and show validation errors next to input fields using e.g.
<form:errors path="title" cssClass="errorMessage"></form:errors>
Is this possible, or should I be creating two controllers; one for the REST/JSON, and one for HTML/form? Maybe there is something I can pass into the method that can be interrogated to determibne the request source, but I can't see it right now.
What would be the "best practice" in this case?
EDIT 1:
Trying answer from #ring-bearer first as it allows for the same URL pattern, but having issues.
Using methods:
// used to handle JSON/XML
#RequestMapping(method = RequestMethod.POST, produces = {
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public #ResponseBody Note create(
#Valid #ModelAttribute final Note note, final BindingResult result) {
[...]
}
// used to handle form view
#RequestMapping(method = RequestMethod.POST)
public ModelAndView createForView(final Model model,
#Valid #ModelAttribute final Note note, final BindingResult result) {
[...]
}
Interestingly, the HTML form submission, still gets handled by create() and not createForView(). After looking at the form submission request headers, I see that this Accept header:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
By adding produces = "text/html" to #RequestMapping on createForView(), all 3 scenarios work (form, AJAX/JSON, AJAX/XML).
Is this normal, or am I still missing something?
This can be achieved using "content negotiation". Spring MVC needs to be enabled for content negotiation using a "contentNegotiationManager" definition. It can be set up using Java or XML configuration. The configuration will centrally manage media type mappings(json, xml etc). Once that is set up, a controller class can be built to cater to both JSON and View(HTML). Below is a generic example(uncompiled), it should be easy to refactor your class to similar structure to avoid violation of DRY.
#Controller
class ReportController{
//1- Method for JSON/marshalling types(XML)
#RequestMapping(value="/report", produces={"application/xml", "application/json"})
#ResponseStatus(HttpStatus.OK)
public #ResponseBody List<ReportPara> generateReport(Principal principal) {
return reportService.generateReport(principal);
}
//2- For View technologies ( Eg:JSP HTML)
#RequestMapping("/report")
public String generateReportForView(Model model, Principal principal) {
model.addAttribute( generateReport(principal) );
// Return the view to use for rendering the response
return ¨reports/main¨;
}
}
Which of the two #RequestMapping methods will execute? It is determined by content negotiation definition. Eg: URLs such as report.xml or report.json map to the first method, any other URLs ending in report.anything map to the second.
The following will be easier to maintain:
#Controller
class NoteController {
#Autowired NoteService service;
#RequestMapping(method = RequestMethod.POST, value = "/note")
public ModelAndView createFromForm(#ModelAttribute #Valid Note note, BindingResult result) {
return new ModelAndView("note", create(note));
}
#RequestMapping(method = RequestMethod.POST, value = "/api/note")
#ResponseBody
public Note createFromApi(#RequestBody Note note) {
return create(note);
}
private Note create(Note note) {
return service.create(note);
}
}

.getJSON 404 Error with ModelAndView

I am trying to retrieve some JSON data in my javascript by making a call to the controller. The controller returns a MappingJacksonJsonView ModelandView, but the .getJSON is always reporting a 404 at .../handhygiene.json.
Is there a problem with the way I am returning the ModelandView from the controller?
Controller
#RequestMapping(value = "/{room}/handhygiene.json", method = RequestMethod.GET)
public ModelAndView getHandHygienePageAsync(
#PathVariable(value = "room") String roomCode) {
ModelAndView mav = new ModelAndView(new MappingJacksonJsonView());
mav.getModelMap().addAttribute(blahblahblah); //adds some attributes
...
return mav;
}
Javascript
var currentURL = window.location;
$.getJSON(currentURL + ".json",
function(data) {
... //does stuff with data
}
If you're trying to get only an JSON object from Ajax request, you need to add #ResponseBody to your method, and make you result object as return from your method.
The #ResponseBody tells to Spring that he need to serialize your object to return to the client as content-type. By default, Spring uses JSON Serialization. ModelAndView will try to return an .JSP page. Maybe you don't have this jsp page on your resources so, the server return 404 error.
I Think this code should work for you:
#RequestMapping(value = "/{room}/handhygiene.json", method = RequestMethod.GET)
public #ResponseBody Room getHandHygienePageAsync(#PathVariable(value = "room") String roomCode) {
Room room = myService.findRoomByRoomCode(roomCode);
return room;
}
I'm assuming you're using the Room as your result object, but it may be another object or ArrayList, for example.
You cant take a look here for Spring example, and here for example and configuration.

Rewriting Java Controller Class with Grails REST

I have implemented a REST application with Spring at Java. An example of GET and DELETE requests are as follows:
#RequestMapping(method = RequestMethod.GET)
public
#ResponseBody
List<Configuration> getAllConfigurationsInJSON() {
return new ArrayList<Configuration>(configurationMap.values());
}
#RequestMapping(value = "{systemId}", method = RequestMethod.DELETE)
public void deleteConfiguration(HttpServletResponse response, #PathVariable long systemId) throws IOException {
if (configurationMap.containsKey(systemId)) {
configurationMap.remove(systemId);
response.setStatus(HttpServletResponse.SC_OK);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
I am searching about Grails and want to rewrite my controller with Grails. I read some articles and it shows that there is no need to write that annotations at Grails. I will just define my clousers and it will render my response to JSON object as like my Spring applicaiton. How can I implement them with closures? (I use IntelliJ IDEA 10.3)
There is nothing in this code that can make use of closures.
In grails it may look the same, or you can put the url mappings in UrlMappings.groovy

Categories