I am studying the Spring MVC Showcase example downlodable from the STS dashboard.
Now I am trying to understand the Message Converters section.
In my view I have the following link:
<li>
<a id="writeString" class="textLink" href="<c:url value="/messageconverters/string" />">Write a String</a>
</li>
This link generate an HTTP Request towards the URL: /messageconverters/string
This request is handled by the following method of my controller class:
#RequestMapping(value="/string", method=RequestMethod.GET)
public #ResponseBody String writeString() {
return "Wrote a string";
}
This method simply return a String inside the body field of the HTTP Response...this is very simple
The problem is that I am not understanding why this example is inside the Message Converter section...
What connects this to the message converts topics?
The main thing to note in the example is #ResponseBody method parameter annotation.
This annotation can be put on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name). Spring converts the returned object to a response body by using an HttpMessageConverter
Related
I have a spring boot controller endpoint as follows.
#PutMapping("/manage/{id}")
public ResponseEntity<Boolean> manage(#PathVariable Long id, #RequestBody Type type) {
...
}
Where Type is an Enum as follows.
public enum Type {
ONE,
TWO
}
ISSUE 1: When I test this controller, I have to send the content as "ONE" instead of ONE for a successful invocation. i.e. it works with the following code.
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("\"" + Type.ONE + '\"'))
.andExpect(status().isOk());
It does not work with
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(Type.ONE.name()))
.andExpect(status().isOk());
ISSUE 2: I am not able to invoke this method from the Angular service.
this.http.put<string>('/api/manage/' + id, type)
gives me
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
Everything works when I add the Enum to a Dto and send an object from the client. But due to some business requirements, I want to use the current structure itself. i.e the Enum as a RequestBody.
UPDATE
I also tried to change the controller method structure to
#PutMapping(value = "/manage/{id}", consumes = MediaType.TEXT_PLAIN_VALUE)
I get the following error.
Content type 'text/plain' not supported
Both issues stem from trying to use a JSON endpoint as a plain text endpoint.
Ad 1, ONE is invalid JSON ("ONE" is valid)
Ad 2, when you just post a string, it is sent as text/plain and the endpoint complains.
Probably adding consumes="text/plain" to your #PutMapping will solve the problem, but frankly - I am not sure if string/enum mappings work out-of-the-box in the hodge-podge that is spring boot.
I am trying to pass clusterId=1 as parameter from
<a href="http://192.168.11.134:8080/UniconnectConfigurationWeb/nodes?clusterId=1"> and get it into spring mvc controller through #PathParam("clusterId")Integer clusterId. But I'm getting 404 error.
Guide me how to pass parameter through anchor tag and how to hit controller and get the parameter value.
I am sharing my code below,
#RequestMapping(value = "/nodes?clusterId={clusterId}", method = RequestMethod.GET)
public ModelAndView nodes(#RequestParam("clusterId")Integer clusterId,HttpSession session, HttpServletRequest request) {
System.out.println(clusterId);
return dashboard;
}
}
<c:url var="myURL" value="http://192.168.11.134:8080/UniconnectConfigurationWeb/nodes">
<c:param name="clusterId" value="1"/>
</c:url>
Here you are using clusterId as Request Parameter , and passing from client side to server side. but in your server side code you are used ?clusterId={clusterId} in Request Mapping annotation and you are trying to receive that request parameter with #RequestParam Annotation. here #RequestParam is enough for receiving Request Parameter. so, no need to use this ?clusterId={clusterId}`, this is not correct way of writing server side URL.
it may helps you for better understanding #RequestParam vs #PathVariable
I have a spring boot application.
I have a custom error controller, that is mapped to using ErrorPage mappings. The mappings are largely based on HTTP Status codes, and normally just render a HTML view appropriately.
For example, my mapping:
#Configuration
class ErrorConfiguration implements EmbeddedServletContainerCustomizer {
#Override public void customize( ConfigurableEmbeddedServletContainer container ) {
container.addErrorPages( new ErrorPage( HttpStatus.NOT_FOUND, "/error/404.html" ) )
}
And my error controller:
#Controller
#RequestMapping
public class ErrorController {
#RequestMapping( value = "/error/404.html" )
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public String pageNotFound( HttpServletRequest request ) {
"errors/404"
}
This works fine - If I just enter a random non-existent URL then it renders the 404 page.
Now, I want a section of my site, lets say /api/.. that is dedicated to my JSON api to serve the errors as JSON, so if I enter a random non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to have a #ControllerAdvice that specifically caught a class of custom API exceptions I had defined and returned JSON, and in my standard ErrorController checking the URL and throwing an apprpriate API exception if under that API URL space (but that didn't work, as the ExceptionHandler method could not be invoked because it was a different return type from the original controller method).
Is this something that has been solved?
The problem was my own fault. I was trying to work out why my #ExceptionHandler was not able to catch my exception and return JSON - As I suggested at the end of my question, I thought I was having problems because of conflicting return types - this was incorrect.
The error I was getting trying to have my exception handler return JSON was along the lines of:
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation"
I did some more digging/experimenting to try to narrow down the problem (thinking that the issue was because I was in the Spring error handling flow and in an ErrorController that was causing the problem), however the problem was just because of the content negotiation stuff Spring does.
Because my errorPage mapping in the web.xml was mapping to /error/404.html, Spring was using the suffix to resolve the appropriate view - so it then failed when I tried to return json.
I have been able to resolve the issue by changing my web.xml to /error/404 or by turning off the content negotiation suffix option.
Now, I want a section of my site, lets say /api/.. that is dedicated
to my JSON api to serve the errors as JSON, so if I enter a random
non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to
have a #ControllerAdvice that specifically caught a class of custom
API exceptions I had defined and returned JSON, and in my standard
ErrorController checking the URL and throwing an apprpriate API
exception if under that API URL space (but that didn't work, as the
ExceptionHandler method could not be invoked because it was a
different return type from the original controller method).
I think you need to rethink what you are trying to do here. According to HTTP response codes here
The 404 or Not Found error message is an HTTP standard response code
indicating that the client was able to communicate with a given
server, but the server could not find what was requested.
So when typing a random URL you may not want to throw 404 all the time. If you are trying to handle a bad request you can do something like this
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ErrorResponse> noRequestHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.debug("noRequestHandlerFound: stacktrace={}", ExceptionUtils.getStackTrace(e));
String errorCode = "400 - Bad Request";
String errorMsg = "Requested URL doesn't exist";
return new ResponseEntity<>(new ErrorResponse(errorCode, errorMsg), HttpStatus.BAD_REQUEST);
}
}
Construct ResponseEntity that suites your need.
I am unable to get the ModelAttribute for second request.
My first request is initForm() method I prepared Command object and able to display the command in jsp.
Through initForm() I am populating command and that command I want in editForm when I will do ajax call.
Here is my spring form
<form:form method="POST" action="addstudentdetails.htm" commandName="command">
Ignore what is inside this
Name: Shoaib Age:23 edit
</form:form>
My ajax request:
function editStudentDetails(studentId,index){
$.ajax(
{url:"editstudentdetails.htm",
method:"GET",
data:{"action":"edit","id":studentId,"index":index},
success: function(data) {
jQuery("#studentDetailsDiv").html(data)
}
}
)
}
In editStudentDetails() method I have method ajax call to go editForm() of the controller.
Here is my controller:
#Controller
public class StudentDetailsController {
#Autowired
private StudentDetailsDAO studentDetailsDAO;
#RequestMapping(value="/studentdetails.htm",method = RequestMethod.GET)
public String initForm(HttpServletRequest request,ModelMap map){
String action=request.getParameter("action");
StudentDetailsCommand command=new StudentDetailsCommand();
System.out.println("in controller"+action);
command.setStudents(studentDetailsDAO.findAll());
map.addAttribute("command", command);
return "studentdetails";
}
#RequestMapping(value="/editstudentdetails.htm",method = RequestMethod.GET)
public String editForm(ModelMap map,HttpServletRequest request){
map.addObject("index", request.getParameter("index"));
StudentDetailsCommand command=(StudentDetailsCommand)map.get("command");
System.out.println(command);
System.out.println(command.getStudents());//NullPointerException here.
map.addObject("command", command);
return "studentdetails";
}
}
Even tried #ModelAttribute("studentDetailsCommand") but didn't worked.
I am new to Spring 3.0 and I followed all solutions which are given here but nothing worked.Can anyone help me out please?
Model attributes only exist during the life cycle of one HttpServletRequest. Consider reading my answer here.
In your initForm method, you do the following
map.addAttribute("command", command);
this add an attribute named command to the model attributes. This attribute will eventually find its way into the HttpServletRequest attributes and be available to your JSP. In here
<form:form [...] modelAttribute="studentDetailsCommand" commandName="command">
first of all, modelAttribute and commandName have the same purpose, ie. to find an attribute in the model. If you remove commandName you will get an Exception because there is no model attribute named studentDetailsCommand. Here your commandName's value is overwriting your modelAttribute's value.
When the Servlet container is finished rendering your JSP, the rendered content is sent as the body of the HTTP response. At this point, the request has been handled and the HttpServletRequest and the model attributes are garbage collected.
When you send your new request through AJAX, there is no longer any model attribute named studentDetailsCommand (there actually never was).
Consider using Flash Attributes.
Related:
How to read flash attributes after redirection in Spring MVC 3.1?
Spring RedirectAttributes: addAttribute vs addFlashAttribute
Use of getFlashAttributes() in Spring's RedirectAttributes
I have a method to which I want to post some json data, that looks like this
#RequestMapping(value = "/m1", method = RequestMethod.POST)
public Object m1(#RequestBody Map<String, ?> body) {
// do something
}
This works great when I set the content-type header to application/json when I post, but fails with an error if I don't (it cannot deserialize the post body into the map because it doesn't know how)
What would I have to configure in spring to make it use application/json as a default when no header is specified?
The class that converts the JSON to your object is called an HttpMessageConverter. I assume you are using the default Jackson one that comes with Spring. You can write a custom MessageConverter, that will always return true in it's supports method with your response object type and then just call the Jackson httpconverter in your readInternal and writeInternal methods. If you do this however, be careful, as once it's registered in your requesthandler, it will be asked on all #ResponseBody and #RequestBody requests.