Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a simple map structure from spring mvc controller to ajax

I am using spring mvc 4 and trying to return a simple map to ajax - from my controller to jsp file.

The controller:

    @RequestMapping(value = "/ajaxtest", method = RequestMethod.GET)
    public @ResponseBody
    Map<String, String> myTest() {
        System.out.println("------------------------------------test");

        Map<String,String> myMap = new HashMap<String, String>();
        myMap.put("a", "1");
        myMap.put("b", "2");
        return myMap;
    }

Ajax from the jsp file:

function testAjax() {
        $.ajax({
            url : '../../ajaxtest.html',
            dataType: "json",
            contentType: "application/json;charset=utf-8",
            success : function(data) {
                alert("1");
                alert(data);
            }
        });
    }

But I am not getting any alert but just the error HTTP/1.1 406 Not Acceptable.

However, changing the code to return a simple string works fine. Controller:

    @RequestMapping(value = "/ajaxtest", method = RequestMethod.GET)
    public @ResponseBody
    String myTest() {
        System.out.println("------------------------------------test");
        return "hello";
}   

Ajax:

function testAjax() {
        $.ajax({
            url : '../../ajaxtest.html',
            success : function(data) {
                alert("1");
                alert(data);
            }
        });
    }

Alerting 1 and hello from ajax as expected.

I added the jackson jar files as expected (by pom.xml):

dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.5.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.5.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.5.1</version>
    </dependency>

Am I missing something? I just want to return a simple mapping structure (or other class structure in the future).

Update: From spring console (Don't sure it's related): Resolving exception from handler [null]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

Thanks in advance! Mike

like image 486
Mike Avatar asked Mar 06 '15 07:03

Mike


4 Answers

I don't know if it the correct way but I solved it as following.

In the controller, I converted the map to json:

    @RequestMapping(value = "/ajaxtest", method = RequestMethod.GET)
    public @ResponseBody
    String myTest() {
        System.out.println("------------------------------------random");

        Map<String,String> myMap = new HashMap<String, String>();
        myMap.put("a", "1");
        myMap.put("b", "2");
        ObjectMapper mapper = new ObjectMapper();
        String json = "";
        try {
            json = mapper.writeValueAsString(myMap);
        } catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return json;
}

and in the jsp:

function testAjax() {
        $.ajax({
            url : '../../ajaxtest.html',
            type:"GET",
            contentType: "application/json;charset=utf-8",
            success : function(data) {
                alert("1");
                alert(data);
                var obj = jQuery.parseJSON( data );
                alert(obj.a);
                alert(obj.b);
            }
        });

Thanks you all! Mike }

like image 182
Mike Avatar answered Nov 13 '22 16:11

Mike


Your "problem" is due to your request ending in .html. You need to add the following configuration to make it work as you expect

<bean id="contentNegotiationManager"              class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false" />
    <property name="defaultContentType" value="application/json" />
</bean>

 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />

and than do as @StanislavL suggested add the produces={ "application/json"} don't add the consumes cause your not posting any JSON

an explanation

When spring determines the representation to which it converts it first looks at the path part of the request (e.g. .html, .json, .xml), than it looks for a parameter explicitly setting the conversion representation, finally goes for an Accept header (the so call PPA strategy)

that is why your example works

function testAjax() {
        $.ajax({
            url : '../../ajaxtest.html',
            success : function(data) {
                alert("1");
                alert(data);
            }
        });
    }

you're getting HTML back. However with the request

function testAjax() {
        $.ajax({
            url : '../../ajaxtest.html',
            dataType: "json",
            contentType: "application/json;charset=utf-8",
            success : function(data) {
                alert("1");
                alert(data);
            }
        });
    }

you're explicitly saying that you expect JSON back from the server, however, .html is hinting that it should be HTML instead, and you end up in problems

To learn the details of the content negotiation strategy you should read this blog, its almost famous by now :) It will also show you the pure java config version

like image 38
Master Slave Avatar answered Nov 13 '22 15:11

Master Slave


Try to add consumes="application/json" and produces={ "application/json"} to the @RequestMapping to let spring process your json

UPDATE 406 error description

HTTP Error 406 Not acceptable

Introduction

A client (e.g. your Web browser or our CheckUpDown robot) can indicate to the Web server (running the Web site) the characteristics of the data it will accept back from the Web server. This is done using 'accept headers' of the following types:

Accept: The MIME types accepted by the client. For example, a browser may only accept back types of data (HTML files, GIF files etc.) it knows how to process. Accept-Charset: The character sets accepted by the client. Accept-Encoding: The data encoding accepted by the client e.g. the file formats it understands. Accept-Language: The natural languages (English, German etc.) accepted by the client. Accept-Ranges: Whether the client accepts ranges of bytes from the resource i.e. a portion of the resource. If the Web server detects that the data it wants to return is not acceptable to the client, it returns a header containing the 406 error code.

It means you somehow should change your server logic to accept MIME/Charset/Encoding etc. of the request you sent from client. Can't say exactly what's wrong but try to play with headers and consumes of the RequestMapping.

like image 2
StanislavL Avatar answered Nov 13 '22 17:11

StanislavL


Your Ajax call should something similar to:

$("#someId" ).click(function(){
            $.ajax({ 
                url:"getDetails",    
                type:"GET", 
                contentType: "application/json; charset=utf-8",
                success: function(responseData){
                    console.log(JSON.stringify(responseData));
                    // Success Message Handler
                },error:function(data,status,er) { 
                    console.log(data)
                 alert("error: "+data+" status: "+status+" er:"+er);
               }
            });
            });

Spring Controller Code should be as below :

@RequestMapping(value="/getDetails",method=RequestMethod.GET)
    public @ResponseBody Map<String,String> showExecutiveSummar(){
        Map<String,String> userDetails = new HashMap<String,String>();
        userDetails .put("ID","a" );
        userDetails .put("FirstName","b" );
        userDetails .put("MiddleName","c" );
        userDetails .put("LastName","d" );
        userDetails .put("Gender","e" );
        userDetails .put("DOB","f" );
    return userDetails 
    }

You can also refer to this link for understanding the library which support this functionality.

like image 1
V.R.Manivannan Avatar answered Nov 13 '22 16:11

V.R.Manivannan