Ajax invocation from Spring MVC Liferay 6.1 portal - ajax

We are using SpringMVC portlet framework with Liferay 6.1 for developing the portal. The current issue that we are facing is that we are not able to invoke the servResouce method in the controller class through the ajax calls from our jsp page.
Can anyone point to the solution.

In your Spring controller, define the resource method as
Public ModelAndView serveResource(ResourceRequest req, ResourceResponse res)
{
...
res.setContentType("application/json; charset=...");
return new ModelAndView("res_page", model);
}
And setup your res_page.jsp response page, with:
<% page contentType="text/html; charset=..." %>
<%-- text/html has no effective effect on your response contentType --%>
<% (your taglibs) %>
{
(your data)
}

Related

HTTP 405 with embedded VAADIN UI in a SPRING-BOOT JSP based webapp

I have integrated Vaadin 7 embedding the UI, as explained in Vaadin Book, in some JSP pages of a Spring Boot based application.
Despite the UI named "v-my-vaadin-ui" is correctly displayed when I call the path in which it is embedded (through a spring MVC controller) I got an HTTP 405 error when interacting with the UI.
Error is on the URL:
http://localhost:8080/v-my-vaadin-ui/UIDL/?v-uiId=4
It seems that because in Spring Boot all controllers have only GET method allowed by default (POST has to be explicitly allowed as explained here)
Already tried disabling the CSRF and configuring Vaadin ralated paths in my Spring Security Configuration:
http.authorizeRequests()
.antMatchers("/vaadinServlet/**",
"/UIDL/**",
"/v-my-vaadin-ui/UIDL/**",
"/v-my-vaadin-ui/PUSH/**",
"/HEARTBEAT/**", "/VAADIN/**").permitAll();
http.csrf().disable();
because VAADIN integration is requiring no CSRF managed by Spring but this doesn't solve the problem.
The VAADIN application is very basic:
#SpringUI(path = "v-my-vaadin-ui")
public class MyVaadinUI extends UI {
private static final long serialVersionUID = -8129815147461786549L;
#Override
protected void init(VaadinRequest vaadinRequest) {
final TextField name = new TextField("Name");
final Button greetButton = new Button("Greet");
greetButton.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Notification.show("Hi " + name.getValue(),
Notification.Type.HUMANIZED_MESSAGE);
}
});
VerticalLayout vl = new VerticalLayout(name, greetButton);
vl.setSpacing(true);
setContent(vl);
}
}
But clicking on the Vaadin button the response code is 405 and the exception in detail is:
.w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/], class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver#4a8756c3]]]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
Unfortunately I didn't find any way to configure the POST method for the UI as for a simple controller i.e. via
#RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
because this annotation cannot be used for Vaadin UI.
Moreover if I call directly the UI (i.e. not passing through a Spring controller) with the URL:
http://localhost:8080/v-my-vaadin-ui
UI displays and works perfectly.
Any idea what is causing this problem? How can I allow the POST method?
Have you tried with this add-on? https://vaadin.com/directory#!addon/jsp-integration
SOLVED!!
The problem was caused by an error I made in the JS script I was using to embed the VAADIN app (I've followed the documentation here: https://vaadin.com/docs/-/part/framework/advanced/advanced-embedding.html) in particular the code fragment for "serviceUrl" parameter:
"serviceUrl": "helloworld/",
that I've understood to be set with the name of the application, in my case "v-my-vaadin-ui".
But the correct "serviceUrl" in my case, is still the default one "vaadinServlet/" so changing from:
"serviceUrl": "v-my-vaadin-ui/",
to
"serviceUrl": "vaadinServlet/",
everything works.
Here below the complete code of the JSP tag to embed the vaadin app that works for me:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%#tag description="ajax widget tag" pageEncoding="UTF-8"%>
<%#attribute name="v_app_url"%>
<%-- Javascript as described here: https://vaadin.com/docs/-/part/framework/advanced/advanced-embedding.html --%>
<div>
<div id="${v_app_url}" class="v-app myvaadinui" >
<div class="v-app-loading"></div>
<noscript>
You have to enable javascript in your browser to use an application built with Vaadin.
</noscript>
</div>
<script type="text/javascript">//<![CDATA[
if (!window.vaadin)
alert("Failed to load the bootstrap JavaScript: vaadinBootstrap.js");
/* The UI Configuration */
vaadin.initApplication("${v_app_url}", {
"browserDetailsUrl": "${v_app_url}/",
"serviceUrl": "vaadinServlet/",
"widgetset": "com.vaadin.DefaultWidgetSet",
"theme": "valo",
"versionInfo": {"vaadinVersion": "7.4.4"},
"vaadinDir": "/VAADIN/",
"heartbeatInterval": 300,
"debug": false,
"standalone": false,
"authErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Authentication problem"
},
"comErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Communication problem"
},
"sessExpMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Session Expired"
}
});//]] >
</script>
</div>

How to get the session id in FTL

I want the current session id in the ftl page.
I tried many things with the Session (HttpSessionHashModel) available but not able to get the id?
Getting a request header also do my job.
Is there any way to get any of these item in my FTL?
you can put the parameter in your FTL file with java servlet.
I think your project work like this:
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try
{
Map dataModel = new HashMap();
dataModel.put("session_id", req.getSession().getId());
Configuration cfg = new Configuration();
Template tmpl = cfg.getTemplate("test.ftl");
tmpl.process(dataModel, resp.getWriter());
}
catch (TemplateException ex) {
throw new IOException(ex);
}
}
file test.ftl as
<html>
<head></head>
<body>
<p> ${session_id!""} </p>
</body>
</html>
FreeMarker doesn't expose anything by default. It doesn't even know what servlets are. So it depends on the web application framework your are using. (Unless, you are using freemarker.ext.servlet.FreeMarkerServlet.) Or, you can put these into the data-model directly.

Handling MultipartException with Spring Boot and display error page

I have a very simple file upload set up with Spring Boot.
I was wondering if there was an easy way to display an error page when the maximum file size is exceeded.
I have uploaded a very simple example of what I'm trying to achieve on github.
Basically, the idea is to catch the MultipartException in a global Spring exception handler:
#ControllerAdvice
public class UploadExceptionHandler {
#ExceptionHandler(MultipartException.class)
public ModelAndView handleError(MultipartException exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("error", exception.getMessage());
modelAndView.setViewName("uploadPage");
return modelAndView;
}
}
The controller which handles the file upload is really simple:
#RequestMapping("/")
public String uploadPage() {
return "uploadPage";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String onUpload(#RequestParam MultipartFile file) {
System.out.println(file.getOriginalFilename());
return "uploadPage";
}
And the uploadPage.html thymeleaf template associated with it too:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<title>Upload</title>
</head>
<body>
<div style="color: red" th:text="${error}" th:if="${error}">
Error during upload
</div>
<form th:action="#{/}" method="post" enctype="multipart/form-data">
<input type="file" id="file" name="file"/>
<button type="submit" name="save">Submit</button>
</form>
</body>
</html>
The idea is to display an error message in the same upload page when the file is too big.
It was my understanding that one would configure Spring's MultipartResolver to resolve exceptions lazily and be able to catch those exceptions at Spring's level (MVC exception handlers) but this code does not seem to help:
#Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(true);
return multipartResolver;
}
So before I resort to extreme measures like a filter or extending the MultipartResolver...
Do you know a clean way to handle those exceptions with Spring MVC?
Answer
Thanks to #rossen-stoyanchev.
Here is what I ended up doing:
#RequestMapping("uploadError")
public ModelAndView onUploadError(HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView("uploadPage");
modelAndView.addObject("error", request.getAttribute(WebUtils.ERROR_MESSAGE_ATTRIBUTE));
return modelAndView;
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> container.addErrorPages(new ErrorPage(MultipartException.class, "/uploadError"));
}
Works like a charm and feels like an elegant solution.
I updated the project on github if someone is interested.
Many thanks!
Multipart request parsing happens before a handler is selected and hence there is no #Controller and therefore no #ControllerAdvice to speak of yet. You can use an ErrorController (see http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-error-handling) for that.
BTW you don't need #RequestParam. It's enough that the argument type is MultipartFile.

Call servlet from Spring controller

I have a controller class with #ResponseBody annotation to server ajax request.
My requirement is to redirect to a servlet(post method) if a check fails. If check does not fails then i need to respond back with Json data
Class Controller {
#ResponseBody redirectTest() {
if(true) {
//send json data
} else {
//Redirect to a servlet
}
}
Is there a possible way to redirect to post method of servlet.
Is there any way to initialize the servlet on need basic, without creating any servlet mapping in web.xml.
Is there a possible way to redirect to post method of servlet.
Use the forward prefix
#ResponseBody
public String redirectTest(HttpServletRequest request) {
if (true) {
//send json data
} else {
request.setAttribute("someObj", someObj);
return "forward:/someServlet";
}
}
Is there any way to initialize the servlet on need basic, without creating any servlet mapping in web.xml.
For Servlet 3.0 or above, use the #WebServlet annotation

Getting error when submitting form using SpringMVC and REST

Created an online form in JSP using SpringMVC tag libraries. The controller for my form is a RESTful web service.
The RESTful web service has two calls:
(1) http://localhost:8080/myapp/applications/new
This brings up the online form in the browser (this works).
(2) http://localhost:8080/myapp/applications/create
This saves the form data to a database (handles submit). This is where it breaks.
Followed the conventions from the sample demo petclinic app which comes with the Spring Framework.
Online form:
<%# page contentType="text/html;charset=UTF-8" language="java"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<body>
<form:form modelAttribute="application" method="POST" action="create">
<table>
<tr>
<td>Name:</td>
<td><form:input path="name" size="30" maxlength="80"/></td>
</tr>
<tr>
<td>Description:</td>
<td><form:input path="description" size="30" maxlength="80"/></td>
</tr>
<tr>
<td>Image URL:</td>
<td><form:input path="imgUrl" size="30" maxlength="80"/></td>
</tr>
</table>
<input type="submit" value="Save" />
</form:form>
</body>
</html>
The RESTful web service which serves as form controller:
#Controller
#Path(ApplicationsResource.APPLICATION_URL)
public class ApplicationsResource
{
private final Logger log =
LoggerFactory.getLogger(ApplicationsResource.class);
public static final String APPLICATION_URL = "/applications";
#Autowired
private ApplicationManager applicationManager;
#Autowired
private ProfileManager profileManager;
#POST
#Path("create")
#Produces(MediaType.TEXT_HTML)
public Model getNewApplication(#Context HttpServletRequest request,
#RequestAttribute Model model)
{
Application app = new Application();
model.addAttribute("application", app);
try
{
if ("POST".equalsIgnoreCase(request.getMethod()))
{
if (app != null)
{
applicationManager.save(app);
log.info("Added application: " + app.getName());
}
else
{
log.info("Application not added");
}
}
}
catch (Exception e)
{
log.info("Exception: ", e);
throw new
WebApplicationException(Response.status(
RestError.SERVER_ERROR_HTTP_RESP).
type("application/json;charset=utf-8").
entity(new ErrorOutput(RestError.SERVER_ERROR_CODE, RestError.SERVER_ERROR_MSG, e.toString())).build());
}
return model;
}
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder)
{
dataBinder.setDisallowedFields(new String[] {"id"});
}
#GET
#Path("new")
#Produces( { MediaType.TEXT_HTML })
public ModelAndView getNewApplicationForm()
{
log.info("ApplicationsResource - Inside getNewApplicationForm");
ModelAndView mv = new ModelAndView("/applications/applications_new");
mv.addObject("application", new Application());
return mv;
}
}
Exception thrown when I click on submit:
Failed executing POST /applications/create
org.jboss.resteasy.spi.BadRequestException:
Could not find message body reader for type:
interface org.springframework.ui.Model of content type:
application/x-www-form-urlencoded at
org.jboss.resteasy.core.MessageBodyParameterInjector$1
createReaderNotFound(MessageBodyParameterInjector.java:73)
Does anyone know why I am getting this exception?
Would really appreciate it if someone could help me with this issue...
Happy programming and thank you for taking the time to read this.
It was a RESTEasy problem... The fix was putting the #Form Application App inside the parameter list and prepending the domain model object's setters with #FormParam("name").
See: Integrating RESTEasy with SpringMVC
You haven't told your controller what mime types it accepts and how to map them, it needs a Reader associated with that mime type to map it and send it into your method.

Resources