Background:
Using AJAX JQuery form to upload file asynchronously and receive JSON response back from Controller, extract the data and update the view without page refresh.
Tools and Version:
IBM WebSphere Portal Server v8, Spring + Spring MVC Portlet v4.1.5, Commons-fileupload v1.3.1, Jackson-core/databind v2.5.1, json-20131018.jar.
What I've tried before:
Used portlet:resourceURL and handled that in Controller as ResourceRequest and using @ResponseBody which was fine i.e. able to return as JSON response back to the view and extract the data and update the view but the problem with this approach is that the mulitpart file never submitted to the Controller using ResourceRequest. So I had to find a way to get this file.
Later learned that @ResponseBody is still not supported in Spring MVC Portlet per https://jira.spring.io/browse/SPR-7344 and suggested to use ActionRequest instead of ResourceRequest per Handling Multipart request that is not an Action request? and https://www.liferay.com/community/forums/-/message_boards/message/50151319 -
What I am doing now:
When I tried to use ActionRequest as suggested above, the render phase follows the action phase always return as text/html but what i wanted is application/json. So tried custom view resolver as described(pls refer AjaxView.java) below but that doesn't help - still getting text/html page, any help?
JSP:
<TR>
<TD align="left" id="fileUploadWrapper">
<form:input id="file" path="msgList[${status.index}].file" type="file"/>
<form:hidden path="msgList[${status.index}].file"/>
<input name="uploadBtn" id="uploadBtn" type="button" value="Attach" onclick="disabledConfirm_exit=true;uploadJqueryForm()";/>
</TD>
</TR>
JS:
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"> </script>
<script src="http://malsup.github.com/jquery.form.js"></script>
function uploadJqueryForm(){
var url = '<portlet:actionURL><portlet:param name="action" value="upload"/></portlet:actionURL>';
$("#replyMessageForm").ajaxForm({
type: "POST",
url: url,
success:function(responce) {
$.each(responce,function(key, attachment) {
var htmlrowStart = '<TR name="DataContainer">';
var htmlcol1 = '<TD name="Document_PageAutoDelete_ColumnData" valign="top" class="tableCell"><a href=<portlet:actionURL><portlet:param name="action" value="upload"/><portlet:param name="attachmentID" value='+attachment.attachmentID+'/></portlet:actionURL> onclick="disabledConfirm_exit=true; javascript:submitMsgForm(replyMessageForm);">'+"[Remove]"+'</a> </TD>';
var htmlrowEnd = '</TR>';
$('#fileListTblID').append(htmlrowStart);
$('#fileListTblID').append(htmlcol1);
$('#fileListTblID').append(htmlrowEnd);
});
},
}).submit();
}
Controller:
@ActionMapping(params = "action=upload")
public void onUpload(ActionRequest actionRequest,ActionResponse actionResponse, Model model,@Valid @ModelAttribute("messageForm") MessageForm messageForm, BindingResult result) throws Exception {
if (result.hasErrors()) {
actionResponse..setRenderParameter("action","send");
} else {
CommonsMultipartFile multipartFile = messageForm.getMessageList().get(0).getFile();
String orgName = multipartFile.getOriginalFilename();
Attachment attachment = new Attachment();
attachment.setAttachmentID((message.getId() != null ? message.getId() + genUniRandInt():genUniRandInt()));
attachment.setAttachmentName(orgName);
attachment.setAttachmentSize(toString(new Long(multipartFile.getSize()/1000).toString()));
byte[] bytes = multipartFile.getBytes();
String encFile = encoder.encode(bytes);
attachment.setAttachmentData(encFile);
model.addAttribute("attachment", attachment);
actionResponse.setRenderParameter("action","fileUpload");
}
}
@RenderMapping(params = "action=fileUpload")
public ModelAndView process(RenderRequest request, Model model) throws IOException {
Map map = new HashMap();
Attachment attachment = (Attachment)model.asMap().get("attachment");
map.put("attachment", attachment);
return new ModelAndView("ajaxView", map);
}
VIEW:
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;
@Component("ajaxView")
public class AjaxView extends AbstractView {
public AjaxView() {
super();
setContentType("application/json");
}
@Override
protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest request, HttpServletResponse response)
throws Exception {
JSONObject jsonObj = new JSONObject(map);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(jsonObj.toString());
response.getWriter().flush();
}
}
applicationContext.xml
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"> <value>org.springframework.web.servlet.view.JstlView</value> </property>
<property name="prefix"> <value>/WEB-INF/jsp/</value> </property>
<property name="suffix"> <value>.jsp</value> </property>
<property name="order"> <value>2</value> </property>
</bean>
<bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="1" />
</bean>
Error:
Caused by: java.lang.IllegalArgumentException: application/json not valid compared to [text/html]
at com.ibm.ws.portletcontainer.core.impl.PortletResponseImpl.setContentType(PortletResponseImpl.java:579)
at com.ibm.ws.portletcontainer.core.impl.RenderResponseImpl.setContentType(RenderResponseImpl.java:113)
at javax.portlet.filter.RenderResponseWrapper.setContentType(RenderResponseWrapper.java:139)
at javax.portlet.filter.RenderResponseWrapper.setContentType(RenderResponseWrapper.java:139)
at org.springframework.web.portlet.DispatcherPortlet.render(DispatcherPortlet.java:1126)
at org.springframework.web.portlet.DispatcherPortlet.doRenderService(DispatcherPortlet.java:789)
at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:536)