thunder-103 thunder-103 - 2 months ago 12
Ajax Question

Spring ajax send list of objects from controller to jsp

I am trying to send send list of objects which I get from database to my JSP. I managed to successfully send data from JSP to my controller. Method inside my controller takes that parameter, fills List (I checked it in debug mode) and controller returns that list.

@RequestMapping(value="/test.html", method=RequestMethod.GET, produces="application/json")
public @ResponseBody List<ModelVechicle> fetchListModelById(@RequestParam Integer number) {

System.out.println(number);

List<ModelVechicle> modelList = vechicleService.fetchModelById(number);

return modelList;
}


When I try to get that List on my JSP, I get

HTTP Status 406 -

type Status report

message

description The resource identified by this request is only capable of generating
responses with characteristics not acceptable according to the request "accept" headers.

Apache Tomcat/8.0.32


Here is my JSP with AJAX code

<script type="text/javascript">
$(document).ready(function(){
$("#brand").change(onSelectChange);
});

function onSelectChange() {
var selected = $("#brand option:selected");
var output = "";
var number = parseInt(selected.val());


$.ajax({
type: "GET",
url: "test.html",
dataType : 'json',
data: ({number: number}),
success: function(response){
$('#result').html("");
var obj = JSON.parse(response);
$('#result').html(obj.modelName);
},
error: function(xhr,e){
alert('Error while request..'+xhr.responseText);
}
});

if(selected.val() != 0){
output = "You selected brand " + selected.text();

}
$("#output").html(number);


}




Also here is my ModelVechicle class, that is the class which objects I am adding into List:

@Entity
@Table(name = "CARDEALERSHIP.MODEL")
public class ModelVechicle implements Serializable {

private static final long serialVersionUID = 7420515051961158192L;

@Id
@Column(name = "ID")
private Integer modelId;

@Column(name = "MODELNAME")
private String modelName;

@ManyToOne
@JoinColumn(name = "BRANDID")
private Brand brand;

public ModelVechicle(Integer modelId, String modelName, Brand brand) {
super();
this.modelId = modelId;
this.modelName = modelName;
this.brand = brand;
}

public ModelVechicle() {}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((brand == null) ? 0 : brand.hashCode());
result = prime * result + ((modelId == null) ? 0 : modelId.hashCode());
result = prime * result + ((modelName == null) ? 0 : modelName.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ModelVechicle other = (ModelVechicle) obj;
if (brand == null) {
if (other.brand != null)
return false;
} else if (!brand.equals(other.brand))
return false;
if (modelId == null) {
if (other.modelId != null)
return false;
} else if (!modelId.equals(other.modelId))
return false;
if (modelName == null) {
if (other.modelName != null)
return false;
} else if (!modelName.equals(other.modelName))
return false;
return true;
}

public Integer getModelId() {
return modelId;
}

public void setModelId(Integer modelId) {
this.modelId = modelId;
}

public String getModelName() {
return modelName;
}

public void setModelName(String modelName) {
this.modelName = modelName;
}

public Brand getBrand() {
return brand;
}

public void setBrand(Brand brand) {
this.brand = brand;
}


Can somebody please explain me what to do in order to get the List dynamically to JSP page, and display List members properly?

EDIT: Here is my web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>CarDealership</display-name>
<welcome-file-list>
<welcome-file>addVechicle.html</welcome-file>
</welcome-file-list>

<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatchers.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/app-config.xml
</param-value>
</context-param>

</web-app>

Answer

As of Spring 3.2+, the content negotiation has other facts in account prior to eval Accept header:

From https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc:

Enabling Content Negotiation in Spring MVC

Spring supports a couple of conventions for selecting the format required: URL suffixes and/or a URL parameter. These work alongside the use of Accept headers. As a result, the content-type can be requested in any of three ways. By default they are checked in this order:

  • Add a path extension (suffix) in the URL. So, if the incoming URL is something like http://myserver/myapp/accounts/list.html then HTML is required. For a spreadsheet the URL should be http://myserver/myapp/accounts/list.xls. The suffix to media-type mapping is automatically defined via the JavaBeans Activation Framework or JAF (so activation.jar must be on the class path).
  • A URL parameter like this: http://myserver/myapp/accounts/list?format=xls. The name of the parameter is format by default, but this may be changed. Using a parameter is disabled by default, but when enabled, it is checked second.

  • Finally the Accept HTTP header property is checked. This is how HTTP is > actually defined to work, but, as previously mentioned, it can be problematic to use.

That actually means that if you map a @Controller method with a .htm(l) suffix, it is intended to return html and won't return json nor any other format even if you sent other format as Accept header.

I allways map my controllers as .htm and had to change the way I used to map @ResponseBody annotated methods when I upgraded to Spring 3.2 and newer.

EDIT: After seeing your web.xml, as I supposed, you are mapping every .html suffix request to the dispatcher servlet:

  `<servlet-mapping>
    <servlet-name>springDispatcher</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>`

I figure that now the @RequestMapping in your controller is like this:

@RequestMapping(value="/test", method=RequestMethod.GET, produces="application/json")
public @ResponseBody List<ModelVechicle> fetchListModelById(@RequestParam Integer number) {

As /test does not match .html suffix, request is not arriving to springDispatcher, and that's exactly why you are getting a 404.

Now, options to fix this:

1) Add a new mapping in the web.xml which matches this controller:

<servlet-mapping>
    <servlet-name>springDispatcher</servlet-name>
    <url-pattern>*.html</url-pattern>
    <url-pattern>*/test</url-pattern>
</servlet-mapping>

This way you would be forced to include any new non html returning method. Not seems usable for me.

2) Map all incoming requests to dispatcherServlet

  <servlet-mapping>
    <servlet-name>springDispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

I don't really like this option, I prefer to filter what I really want to reach the dispatcher servlet.

3) Find a new matching pattern for this kind of requests. I allways publish some kind of generic suffix, which will not be catched by JAF, such as *.service:

  <servlet-mapping>
    <servlet-name>springDispatcher</servlet-name>
    <url-pattern>*.html</url-pattern>
    <url-pattern>*.service</url-pattern>
  </servlet-mapping>

So in Controller methods which return XML or JSON (or any other format, depending only of the Accept header), I map like this:

@RequestMapping(value="/test.service", method=RequestMethod.GET, produces="application/json")
public @ResponseBody List<ModelVechicle> fetchListModelById(@RequestParam Integer number) {

4) You could as well publish all this kind of @ResponseBody controller method using a 'http://com.xxx.yyy/myApp/service/resource' pattern and use /service/ as servlet mapping in web.xml

      <servlet-mapping>
        <servlet-name>springDispatcher</servlet-name>
        <url-pattern>*.html</url-pattern>
        <url-pattern>/service/</url-pattern>
      </servlet-mapping>
Comments