1

What am I using?

  • Tomcat 7.0.56
  • JAVA 1.8
  • Spring MVC
  • jQuery

What is the goal?

I'm implementing a content directory.
The user searches for a specific name or phone number for example. Then a list of contacts shows up on the left side of the page. On the right side there should details be displayed of a contact that gets selected.

Where is the problem?

My problem lays on the part of showing the details.

My Code:

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html >
<%@ include file="includes/header.jsp" %>
<body>
    <div class="pageContainer container-fluid">
        <div class="row">
            <%@ include file="searchBar.jsp" %>
        </div>
        <div class="row">
            <div class="containerOverView col-lg-5 col-md-6 col-sm-12 col-xs-12">
                <%@ include file="overView.jsp" %>
            </div>
            <div class="containerDetailView col-lg-7 col-md-6 col-sm-12 col-xs-12">
                <%@ include file="detailView.jsp" %>
            </div>
        </div>
    </div>
</body>
</html>


detailView.jsp

<ul class="flex-container">
    <li class="flex-item-photo">Photo</li>
    <li class="flex-item-pdetails">Personal Details</li>
    <li class="flex-item-cdetails">Company Details</li>
    <li class="flex-item-map">Map:
        ${detailSearchResult.name}
    </li>

</ul>


MainController.java

// Delivers the refresh-information for the ajax request, when the detail view gets reloaded
    @RequestMapping(value = "/refreshDetail", method = RequestMethod.POST)
    @ResponseBody
    private String refreshDetailView(HttpServletRequest hsr, @RequestParam String id, ModelMap model){
        model.addAttribute("detailSearchResult", Dao.getDetailViewData(id));
        return "detailView";
    }


main.js

$(document).ready(function(){
$(".overViewListEmployee").click(function () {
    var id = $(this).find(".overViewEmployeeID").text();
    console.log("Works: " + id + ".");
    $.ajax({
        type: "POST",
        accepts: "text/plain",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        url: "/refreshDetail",
        data: ({"id" : id}),
        success: function(response) {
            $(".containerDetailView").html(response);
        }
    });
});
});



What exactly doesn't work?

It seems to me, that the ajax request doesn't really get a valid response. From my understanding ajax accepts every kind of response but somehow, when I debug the JavaScript it says that the response has a Response Error: response is not defined

Error Codes:

With the code above:
400: Failed to load resource: the server responded with a status of 400 (Bad Request)

When I comment out following line in main.js:

 //contentType: "application/json; charset=utf-8",

406: (Not acceptable)


What does work? (due to debugging)

When I debugged the code I saw that the value of the id is getting into the Controller correctly and also the function Dao.getDetailView(id) works properly. So it's really just an issue of the response.

What else have I tried?

I'm not sure if it works that way, by returning a string with the name of the detailView.jsp page that should be reloaded since I don't know if the model I'm adding the attribute to is also going with it and rendered in the index.jsp (where detailView.jsp is included).
So I also tried to put the Controller-Function like this:

// Delivers the refresh-information for the ajax request, when the detail view gets reloaded
    @RequestMapping(value = "/refreshDetail", method = RequestMethod.POST)        
    private ModelAndView refreshDetailView(@RequestParam String id, ModelMap model){
        model.addAttribute("detailSearchResult", Dao.getDetailViewData(id));
        return new ModelAndView("detailView");
    }

and removing this line from main.js:

accepts: "text/plain",


I also tried by returning the model as well:

return new ModelAndView("detailView", model);



I'm not working with JavaScript that often, so it's possibly a really silly and obvious mistake but I can't seem to find it. I literally tried out everything I could find but it looks like I'm stuck here.

I would really appreciate any kind of help on this case :)


Update:

main.js

$(".overViewListEmployee").click(function () {
    var id = parseInt($(this).find(".overViewEmployeeID").text());
    console.log("Works: " + id + ".");
    $.ajax({
        type: "POST",
        accept:"text/html",
        //contentType: "application/json; charset=utf-8",
        dataType: "html",
        url: "/refreshDetail",
        data: ({"id": id}),
        success: function(response) {
            $(".containerDetailView").html(response);
        }
    });
});


MainController.java

// Delivers the refresh-information for the ajax request, when the detail view gets reloaded
    @RequestMapping(value = "/refreshDetail", method = RequestMethod.POST, produces = MediaType.TEXT_HTML_VALUE)
    @ResponseBody
    private ModelAndView refreshDetailView(HttpServletRequest hsr, @RequestBody IdObject id, ModelMap model){
        model.addAttribute("detailSearchResult", Dao.getDetailViewData(id.getId()));
        return new ModelAndView("detailView", model);
    }


IdObject.java

public class IdObject {
int id;

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}
}


Right now the Controller isn't reached at all - still got that 415 Error.


Update 2:

WebInit.java (before)

@Configuration
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer{

protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[]{RootConfig.class};
}

protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[]{WebConfig.class};
}

protected String[] getServletMappings() {
    return new String[]{"/"};
}
}

WebInit.java (after)

@Configuration
public class WebInit implements WebApplicationInitializer{

@Override
public void onStartup(ServletContext container) {
    // Create the 'root' Spring application context
    AnnotationConfigWebApplicationContext rootContext =
            new AnnotationConfigWebApplicationContext();
    rootContext.register(WebConfig.class);

    // Manage the lifecycle of the root application context
    container.addListener(new ContextLoaderListener(rootContext));

    // Create the dispatcher servlet's Spring application context
    AnnotationConfigWebApplicationContext dispatcherContext =
            new AnnotationConfigWebApplicationContext();
    dispatcherContext.register(RootConfig.class);

    // Register and map the dispatcher servlet
    ServletRegistration.Dynamic dispatcher =
            container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/");
}
}

Now I'm not sure if it's what you meant but from what I read @AnnotationDrivenConfig is the same as to do it with AnnotationConfigWebApplicationContext. And since I'm doing it with pure Java based Configuration I figured, that'd be my way to go. But I still got the same error though.

WebConfig.java

@Configuration
@EnableWebMvc
@ComponentScan("com.avectris.newtel")
public class WebConfig extends WebMvcConfigurerAdapter {

@Bean
public InternalResourceViewResolver resolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/view/");
    resolver.setSuffix(".jsp");
    return resolver;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    registry.addResourceHandler("/sass_compiled/**").addResourceLocations("/sass_compiled/");
    registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    final ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    converter.setObjectMapper(objectMapper);
    converters.add(converter);
    super.extendMessageConverters(converters);
}
}
Marc Signer
  • 25
  • 1
  • 7
  • The datatype property at your ajax request should be "html" as you are returning html code. And return a ModelAndView. If you return a String you need to remove @ResponseBody. – alfcope Feb 15 '17 at 10:21
  • @alfcope - Thanks for the fast reply! I tried that. Unfortunately I'm still getting the **400: Bad Request** Error. Any idea what else could cause this? – Marc Signer Feb 15 '17 at 10:35
  • Change accepts to 'accepts:{html: "text/html"}' and include "produces = MediaType.TEXT_HTML_VALUE" to the requestmapping annotation at your controller. – alfcope Feb 15 '17 at 11:11
  • @alfcope - still get the same error. When I debug the JS it's still throwing a **Reference Error: response is not defined**. – Marc Signer Feb 15 '17 at 12:13
  • What line throwing the error? What is the status code for the ajax response? Can you see the response for the ajax request? On chrome is under the "Network" tab. – alfcope Feb 15 '17 at 12:23
  • When I debug the whole flow of the application the error gets thrown as soon as the controller returns something. The status code still is _400_ while the type is _xhr_. Chrome is saying that the error gets thrown in the following line in the jQuery: `xhr.send( options.hasContent && options.data || null );` – Marc Signer Feb 15 '17 at 12:42
  • One more thing, change the data property at your ajax request to "data: JSON.stringify({"id" : id}),". Is your controller being reached? Are the headers fine when the ajax request is done? – alfcope Feb 15 '17 at 13:27
  • 1
    Also, you are using RequesParam to get id at the controller. As you are sending a json object you need to use RequestBody and use a object with an id propety. If you change the url to something like "/refreshDetail?id="+id you do not have to change anything at your controler, but you need to remove the content-type and data from your ajax object. – alfcope Feb 15 '17 at 13:42
  • did you solve this? – funky-nd Oct 28 '17 at 00:21

1 Answers1

0

Taking a look to your ajax request I think you are trying to send a json object (but you are not, really) and then your are trying to get the value for the id with @RequestParam instead of @RequestBody.

If you do not want to send a json object in the ajax payload, then your content-type is wrong. Try something like this. The data property is going to be is converted to a query string by jQuery.

$(document).ready(function(){
  $(".overViewListEmployee").click(function () {
    var id = $(this).find(".overViewEmployeeID").text();
    console.log("Works: " + id + ".");
    $.ajax({
        type: "POST",
        headers: {
            "Accept" : "text/html",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
        },
        url: "/refreshDetail",
        //you can get rid of data if you change your url to "/refreshDatil?id="+id
        data: {"id" : id},
        success: function(response) {
            $(".containerDetailView").html(response);
        }
    });
  });
});

If you really want to send a json object, as I have already said on my comments, you need do some changes at your controller level.


Edit

Ok, you are now mixing the two possible solutions. With your current ajax object you are not sending a json object, but a normal http request. You can check the network tab at your browser when the ajax request happend and you will see a request like /refreshDetail?id=yourid, without json object in the paylod.

Your controller, on the other hand, using @RequestBody is trying to get an object like IdObject from the payload, but your are not doing a request like that. Your controller should looks like:

@RequestMapping(value = "/refreshDetail", method = RequestMethod.POST, produces = MediaType.TEXT_HTML_VALUE)
private ModelAndView refreshDetailView(HttpServletRequest hsr, @RequestParam int id, ModelMap model){
    model.addAttribute("detailSearchResult", Dao.getDetailViewData(id));
    return new ModelAndView("detailView", model);
}

Also remove the brackets from the data property at your ajax object:

$(".overViewListEmployee").click(function () {
    var id = parseInt($(this).find(".overViewEmployeeID").text());
    console.log("Works: " + id + ".");
    $.ajax({
        type: "POST",
        accept:"text/html",
        //contentType: "application/json; charset=utf-8",
        dataType: "html",
        url: "/refreshDetail",
        data: {"id": id},
        success: function(response) {
            $(".containerDetailView").html(response);
        }
    });
});

In case you want to send a json payload If what you really need is send a json object from javascript then you need to change your ajax request

$(".overViewListEmployee").click(function () {
    var id = parseInt($(this).find(".overViewEmployeeID").text());
    console.log("Works: " + id + ".");
    $.ajax({
        type: "POST",
        accept:"text/html",
        dataType: "html",
        contentType: "application/json",
        url: "/refreshDetail",
        data: JSON.stringify({"id": id}),
        success: function(response) {
            $(".containerDetailView").html(response);
        }
    });
});

With contentType jQuery is setting the header Content-Type, telling your controller the request is coming with a json payload. Then, JSON.stringify is converting the javascript object with the info to be send into a json string and it will be used by jQuery as payload.

On the backend side, your controller, now, should use @RequestBody because the info is not coming within the request parameters, but a json payload. So, it should looks like something to this:

@RequestMapping(value = "/refreshDetail", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_HTML_VALUE)
    private ModelAndView refreshDetailView(HttpServletRequest hsr, @RequestBody IdObject id, ModelMap model){
        model.addAttribute("detailSearchResult", Dao.getDetailViewData(id.getId()));
        return new ModelAndView("detailView", model);
    }

The consumes property at the request mapping, in this case, is not mandatory, as you do not have any other mapping to the same value /refreshDetail, but it makes the code easier to read.

You do not have to use @ResponseBody at your controller, on any of this cases, because you are sending back a view. You will use it when sending back a json, xml, etc.

alfcope
  • 2,107
  • 2
  • 11
  • 17
  • Thanks for your answer! Here I get **415 (Unsupported Media Type)** - even when I add `dataType = "html"` For your previous comment: It reaches the Controller just in this state: `header:{accepts:"text/html"}, //contentType: "application/json; charset=utf-8", dataType: "html", url: "/refreshDetail", data: ({"id": id}),` And I don't really know why. I also tried to do it with an object which just has the propert "id" like here: [link](http://stackoverflow.com/questions/20245544/how-to-pass-json-object-from-ajax-to-spring-mvc-controller). – Marc Signer Feb 15 '17 at 15:28
  • 415 (Unsupported Media Type) means the content-type or accept headers are wrong based on what your controller consumes and produces. You can check the request on your browser. – alfcope Feb 15 '17 at 15:48
  • So I was trying some things out but none of the seemed to work.
    I'm not sure if I understood it correctly but since the Type in the request is **xhr**, your suggestion from before `contentType: 'application/x-www-form-urlencoded; charset=UTF-8'` should work. It's also the default value but that didn't change anything.
    So I tried it with `produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE` and `headers = "x-requested-with=XMLHttpRequest"` in the `@RequestMapping` but this also didn't help. (with `accepts:"text/html"` & `dataType:"html"`
    Still get the same error: **415 - xhr**
    – Marc Signer Feb 15 '17 at 16:31
  • You can not use that produces, it does not make sense. Your controller is producing html content. It has to be MediaType.TEXT_HTML_VALUE, and your ajax object has to accept that content. Try removing the content-type from headers. – alfcope Feb 15 '17 at 17:09
  • Alright, that makes sense to me. If I change that back and remove `contentType:"application/json ...` it's giving me again **415 (Unsupported Media Type)**. It doesn't make any difference at the moment if I keep the header or not. Btw: If I understood that correctly I can write the "accept" or "contentType" either inside the `header` or outside of it, right? – Marc Signer Feb 15 '17 at 17:43
  • Have a look to the jQuery ajax api. You can use either jquery "accept" and "contetType" properties (as you have in your post) or headers and specify inside the headers Accept and Content-Type (as I have in my answer). It is basically the same. Could you update your post with the code you used last time, please? – alfcope Feb 15 '17 at 17:53
  • Thanks man! Now it's working like a charm! Out of curiosity and maybe for later use - how would I have to do it, if I wanted to send it with a JSON? – Marc Signer Feb 16 '17 at 09:12
  • Answer updated to send an ajax request with a json payload, @MarcSigner. I hope you got the idea. Cheers. – alfcope Feb 16 '17 at 09:43
  • Definitely get the idea and it all makes to me! But when I change it back to what you suggested, to see if it works like that - it's throwing me the same error as before. I don't get it though because I'm defining that a JSON is sent to the controller and a html comes back (what the controller is returning shouldn't be the problem anyway, since it worked before when I didn't send a JSON). So it's really just that the controller doesn't seem to be able to handle that JSON. – Marc Signer Feb 16 '17 at 11:01
  • My bad. ContentType property at the ajax request should be "application/json" instead of "json". I have updated the answer, @MarcSigner – alfcope Feb 16 '17 at 11:14
  • Ups sorry, didn't mention that I tried that too - still doesn't work. I also tried it by adding `; charset=utf-8` but it didn't make any difference. – Marc Signer Feb 16 '17 at 12:12
  • It must be then something related to the spring message converters then. It looks like Spring is not able to deserialize the json string into the java object. What spring version are you using? Spring uses Jackson to de/serialize json objects. Make sure it is among your dependencies. – alfcope Feb 16 '17 at 13:13
  • spring-webmvc: 4.3.6.RELEASE I haven't had Jackson in my dependencies. So I included this ` com.fasterxml.jackson.core jackson-core 2.8.6 ` But this also didn't help – Marc Signer Feb 16 '17 at 13:24
  • Add jackson-databinds and jackson-annotations artifacts to your dependencies. – alfcope Feb 16 '17 at 13:59
  • So now I have `jackson-core`, `jackson-databinds` & `jackson-annotations` in my maven dependencies but there's still the same error. – Marc Signer Feb 16 '17 at 14:16
  • Hard to say without seeing the config. This now a problem about spring configuration. You should take a look to some documentation. The only I can think about is you are not specifing at your spring configuration or using the annotation @AnnotationDrivenConfig. – alfcope Feb 16 '17 at 14:52
  • Please have a look at my latest update (: I'm not sure if it's that what you meant but I didn't really find anything about the `@AnnotationDrivenConfig` itself. – Marc Signer Feb 16 '17 at 16:21
  • How does your WebConfig.class look? Some links for you to try: http://www.concretepage.com/spring-4/spring-4-rest-web-service-json-example-tomcat // http://www.baeldung.com/spring-httpmessageconverter-rest // http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html – alfcope Feb 16 '17 at 17:26
  • I now added the `configureMessageConverters` method because I realized that jackson somehow has to be configured. Did it with `com.fasterxml.jackson.core` now by the way. Still doesn't work. Is this correct, how I did it? @alfcope – Marc Signer Feb 17 '17 at 12:34
  • The json map converter is a default one, as long as the jar is in the classpath, so you do not need to config the converters. The only I can see is your web config should be rootContext.register(RootConfig.class) and dispatcherContext.register(WebConfig.class). Apart from that I can not see anything obviously wrong. You should open a new question with as much as information as you can and I am sure someone will realise what is wrong. – alfcope Feb 17 '17 at 14:28