2

I am trying to create a web service in Java that accepts two lists as FormData parameter.

I have used the Angualr JS http service as mentioned in this answer.

var queryRequest = $http({
    method:'POST',
    url:'services/test/testPath',
    data:$.param({"listA":myList1,"listB":myList2}),
    headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
});

My object myList1

var myList1=[];
var obj={};
obj["val1"]=1
obj["val2"]=2
myList1.push(obj);

My object myList2

var myList2=[];

var obj={};
obj["val11"]=3
obj["val12"]=4
myList2.push(obj);

var obj={};
obj["val11"]=5
obj["val12"]=6
myList2.push(obj);

I am using javax.ws.rs.* for the rest service

And my Java server endpoint, which accepts the data, is as-

@Path("testPath")
@POST
@Consumes("application/x-www-form-urlencoded;charset=UTF-8")
@Produces("application/json")
public DataDTO addIssuancesForFP(@FormParam("listA") List<TypeA> list1, @FormParam("listB") List<TypeB> list2) {

    System.out.println("Service is called correctly");
    return service.getDTO(list1,list2);
}

My classe 'TypeA'

 private Integer val1;
 private Integer val2;
 //getters and setters, and default constructor

My classe 'TypeB'

 private Integer val11;
 private Integer val12;
 //getters and setters, and default constructor

Endpoint is hitting correctly, but I am getting null in both the list. The request structure is:

Request Type

    Accept:application/json, text/plain, */*
    Content-Type:application/x-www-form-urlencoded;charset=UTF-8

Form Data

    listA[0][val1]:1
    listA[0][val2]:2

    listB[0][val11]:3
    listB[0][val12]:4
    listB[1][val11]:5
    listB[1][val12]:6

It seems to be correct, I think mistake is in server part. How to resolve this?

Thanks

Note: This is just the mock data, which is in exact same format

11
  • form data or from data?? Commented Mar 27, 2015 at 9:26
  • @roxid changed to form data Commented Mar 27, 2015 at 9:26
  • So are [val1], etc. properties on Javascript objects? Is that how the data is actually sent out, i.e listA[0][val1]=value1? Can you show the actual Javascript objects of myList1 and myList2. Also can show the TypeA and TypeB classes. Commented Mar 28, 2015 at 3:32
  • Ok I will check it out right now Commented Mar 28, 2015 at 10:01
  • I didn't see your complete update. It shows usable objects I think. I just wanted to look a the actual request and see how the data is being sent Commented Mar 28, 2015 at 10:08

1 Answer 1

1

Yeah so I don't think using form encoded data is going to work. The reason is that it is mainly for key value pairs, in the form

key1=value7&key2=value2&key3=value3...

What you are doing is using only two keys, listA and listB. So imagine what the values would need to look like, to send the entire list. It isn't pretty. For complex data, it is more viable to send the data in a format like JSON. The problem with this for your particular use case is that there are two unrelated objects (or arrays) to needs to send. For this, a solution would be to use multipart. You're in luck, because I just posted a late answer yesterday, on exactly how this can be handled with Angular.

I won't go through an explanation here about the code. Everything is explained in that link. Please read through it, as you go through this answer. I will be using Jersey as may JAX-RS implementation (as does the example in the link - but it also offers other alternative implementations)

Resource

import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.glassfish.jersey.media.multipart.FormDataParam;

@Path("/form")
public class FormResource {

    @POST
    @Consumes("multipart/form-data")
    @Produces("text/plain")
    public String addIssuancesForFP(@FormDataParam("listA") List<TypeA> list1, 
                                     @FormDataParam("listB") List<TypeB> list2) {

        StringBuilder response = new StringBuilder();
        for(TypeA a: list1) {
            response.append(a.toString()).append("; ");
        }

        for (TypeB b: list2) {
            response.append(b.toString()).append("; ");
        }

        System.out.println("Service is called correctly");
        return response.toString();
    }
}

Angular

<!DOCTYPE html>
<html ng-app="formApp">
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="js/libs/jquery/jquery.js"></script>
        <script src="js/libs/angular.js/angular.js"></script>
        <script>
            angular.module("formApp", [])
            .controller("defaultCtrl", function($scope, $http) {
                $scope.sendData = function() {
                    var myList1 = [];
                    var obj = {};
                    obj["val1"] = "value1";
                    obj["val2"] = "value2";
                    myList1.push(obj);

                    var myList2 = [];

                    var obj = {};
                    obj["val11"] = "value11";
                    obj["val12"] = "value12";
                    myList2.push(obj);

                    var obj = {};
                    obj["val11"] = "value211";
                    obj["val12"] = "value212";
                    myList2.push(obj);

                    var list1 = JSON.stringify(myList1);
                    var list2 = JSON.stringify(myList2);

                    var boundary = Math.random().toString().substr(2);
                    var header = "multipart/form-data; charset=utf-8; boundary=" + boundary;

                    $http({
                        url: "/api/form",
                        headers: {"Content-Type": header},
                        data: createRequest(list1, list2, boundary),
                        method: "POST"
                    }).then(function(response) {
                        $scope.result = response.data;
                    });

                    function createRequest(list1, list2, boundary) {
                        var multipart = "";
                        multipart += "--" + boundary
                            + "\r\nContent-Disposition: form-data; name=listA"
                            + "\r\nContent-type: application/json"
                            + "\r\n\r\n" + list1 + "\r\n";
                        multipart += "--" + boundary
                            + "\r\nContent-Disposition: form-data; name=listB"
                            + "\r\nContent-type: application/json"
                            + "\r\n\r\n" + list2 + "\r\n";
                            multipart += "--" + boundary + "--\r\n";
                        return multipart;
                    }
                };
             });
        </script>
    </head>
    <body>
        <div ng-controller="defaultCtrl">
            <button ng-click="sendData()">Send</button>
            <p>{{result}}
        </div>
    </body>
</html>

Result

TypeA{val1=value1, val2=value2}; 
TypeB{val1=value11, val2=value12}; 
TypeB{val1=value211, val2=value212};

Which is expected, as I just built a string from the toString() methods, which I implemented in the TypeA and TypeB classes.

public class TypeA {
    public String val1;
    public String val2;
    @Override
    public String toString() {
        return "TypeA{" + "val1=" + val1 + ", val2=" + val2 + '}';
    }
}

public class TypeB {
    public String val11;
    public String val12;
    @Override
    public String toString() {
        return "TypeB{" + "val1=" + val11 + ", val2=" + val12 + '}';
    } 
}

Hope this helps.

Sign up to request clarification or add additional context in comments.

11 Comments

I tried this also, but I was getting something like could not found boundary of media error, What does this boundary is for?
It 's part of the multipart specification. Most likely you did not send the request out correctly, maybe allowing the browser to create the request. Even if you set the content type to false/undefined, using FormData as proposed in some of these answers, the data would still be sent out as plain text, and you would still need to parse it on the server side. With the JSON approach, you let the framework parse it. In the solution above, we are created the request body ourselves
And also, I do not have @FormDataParam annotation, that you have imported from org.glassfish.jersey.media.multipart.FormDataParam; and I was using javax.ws.rs.FormParam;. Is there any alternative for this in Spring web. I am also using this org.springframework.web.bind.annotation.RequestBody
Are you using Jersey? If so, go to the link, and it will have the Maven dependency you need to add. Then you need to register the feature also. The link shows Java configuration of the feature. If you are using web.xml, then you can set an init param for the jersey servlet jersey.config.server.provider.classnames - value - org.glassfish.jersey.media.multipart.MultiPartFeature
I am not using Jersey, I am using javax.ws.rs APIs, and also Spring. For example RequestBody was Spring annotation and QueryParam was javax annotation. FormDataParam is not there. How can I add
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.