I was having some problem when trying to execute POST request with multiple parameters. Here is my service.ts class written in typescript:
retrieve(packageId: any[], date: any) {
console.log(packageId);
console.log(date);
let packgeIdStr: string = JSON.stringify(packageId);
const data = { stringArrDo: packgeIdStr, date: date };
const url = this.serviceAPI + '/...../retrieve';
return this.http.post<SRFDO[]>(
url,
{ params: data }
);
}
When I print out in my console, I managed to get the data here.
However, when I pass the data to my controller written in Java, the parameters became null:
#PostMapping("/retrieve")
public DO[] retrieve(#RequestBody String stringArrDo, java.sql.Date date)
throws RemoteException, JaMClientRemoteException, JaMException {
System.out.println(stringArrDo);
System.out.println("date :"+ date);
// removed code
}
I not sure why the data is printed out at service.ts but when passing it to the controller then became null. Any ideas?
Thanks!
You can open up your Chrome dev tools, go to the Network tab, and inspect your POST to see if angular is actually passing the right stuff to the right endpoint. That will tell you if your problem is client-side vs server-side.
Edit:
It looks like your server may be expecting an array of strings, but you're sending an object. In javascript, when you do {var1, var2}, that's just shorthand for {var1: var1Value, var2: var2Value}
Edit 2: I looked up spring, and it looks like you're using #RequestBody incorrectly. It looks like you're supposed to pass it a class that it can deserialize the JSON into, like
public class RequestData {
private String[] packageIds;
private Date date;
// ...
}
You've also put your data under the top-level variable params, so you'd want to remove that and put all your stuff at the top level of your object.
Try reading through this tutorial for more info: https://www.baeldung.com/spring-request-response-body
The attributes name on two classes may be different.
class A{
// class in springboot
private String name;
}
class A {
//class in angular
data: String;
}
In this case, attribute name is main cause for null pointer exception in server side.
Related
My main question is how to pass a (Map, String) to a REST API, I know if I use #RequestBody all the passed contents are stored to map but what can be done to pass map as well as any other parameters REST API.
#GetMapping(path="/invoices")
public String invoiceReceived( Map<String,Object> invoice,String format) throws MessagingException {
System.out.println(format); // this prints NULL
return "returnValue";
}
So I tried using PathVariable but they throw exception. What can be done?
#GetMapping(path="/invoices/{invoiceData}/{format}")
public String invoiceReceived(#PathVariable("invoiceData") Map<String,Object> invoice,
#PathVariable("format") String format) throws MessagingException {
System.out.println(format); // this prints NULL
return "returnValue";
}
What should I do to accept a map and a variable as input? And what should be the JSON file look like which should be given as input?
{
"invoiceData":[{"invoiceId":"23642",
"clientName":"Client",
"amount":"23742.67",
"email":"client#abc.com"
}],
"format":"html"
}
This question was identified similar to another question, So I am trying to explain how is this different, I know that I can use #RequestBody to get all the variables in the map, but The call will be made with two parameters some of which will be stored in map but one parameter will be used for another variable. So how can I send a map along with any other variable?
I think you can use query strings and path variables.
If you declare a controller's method like:
#GetMapping(path="/invoices")
public String invoiceReceived(#RequestBody Map<String,Object> invoice, #RequestParam String format) {
...
}
the url to which the request is send and the JSON request body will be something like below.
The url:
http://localhost:8080/invoices?format=html
The JSON request body:
{
"invoiceId":"23642",
"clientName":"Client",
"amount":"23742.67",
"email":"client#abc.com"
}
Also you can use a path variable like:
http://localhost:8080/invoices/html
#GetMapping(path="/invoices/{format}“)
public String invoiceReceived(#RequestBody Map<String,Object> invoice, #PathVariable String format) {
...
}
Already searched a lot but couldn't find an appropriate answer. I have the following JSON. I understand this is a List object. How do I send a POST request as #Body in retrofit 2? Also, what is the POJO that I need to have to get a successful response from the API.
Please note that I have looked into all JSONObject based solutions. I am looking only for POJO based solutions where List/fields are sent as constructors.
{
"ring":[
{
"ring_pitch_id":"xxxx-xxxx-xxxx-xxxx",
"ring_match_id":"xxxx-xxxx-xxxx-xxxx",
"name":"xxxx",
"type":"xxxx",
"status":"xxxx"
}
]
}
Here is your pojo.
This is body:
public class RingBody {
List<RingModel> ring = new ArrayList<RingModel>();
}
This is items of list of your body.
public class RingModel {
#SerializedName("ring_pitch_id")
String ringPitchId;
#SerializedName("ring_match_id")
String ringMatchId;
String name;
String type;
String status;
}
The way I resolved this is by creating two model classes, ringList and ring. Similar to Ionut's answer, the ringList class contained List with setter. The ring model class contained all the 5 fields with getters and setters. In the calling method had the code that created object of ring class passing all 5 parameters and creating a list of it by writing List<ring> temp = new ArrayList<>();. temp.add(object_of_ring);. Creating object of ringList class and passing ring either as constructer or setter worked.
I am having a bit of trouble getting a return value from a bean in camel and using it in my route.
I have a route that looks like this:
from(file:test/?delete=true)
.unmarshal(jaxb)
.bean(testBean, "testMethod")
.to(direct:nextRoute);
The bean looks something like this:
public void testBean (PojoName pojoInstance){
//do stuff
int i= 75; //a number I generate within the bean after I've started this method
}
I want to use the number I generate inside of my bean and in my route. Something like this:
from(file:test/?delete=true)
.unmarshal(jaxb)
.bean(testBean, "testMethod")
.log(integer generated from testBean)
.to(direct:nextRoute);
What I tried:
So, instead of returning void in my bean, I changed the return type to an int and returned the integer. Then, I was hoping to do something like this in my route:
.log("${body.intFromBean}")
My thinking is that once I return the value from a bean, it should store that value in the exchange body (at least that's what I'm getting from the Camel documentation). Then, I could access it in my route.
The Problem:
However, when I change the testBean return type to an int, I get the following errors:
org.apache.camel.CamelExecutionException: Execution occurred during execution on the exchange
Caused by: org.apache.camel.InvalidPayloadException: No body available of type: PojoName but has value: numberIGenerated of type java.lang.Integer
(Sorry I don't have the full stack trace. I'm using the s.o mobile app)
My question:
From reading some other s.o. submissions, I think I understand the problem. The message body is of one type and the return type is another. However, even when I tried using .
.convertTo(Integer.class)
before calling the bean, but that didn't work either. (Conceptually, that wouldn't work either because if I converted it to an int right after I unmarshalled it, I wouldn't be able to use the unmarshalled data. But I thought I'd try it anyway).
Can someone help me understand how I can properly return the integer and use it in my route?
I've read the documentation on bean binding and the exchange, and I thought I understood it well enough to do this. But I must be missing something.
I think a simpler solution would be:
public class TestBean {
public int testMethod() {
return 75;
}
}
Whether you want the return result to be stored in a header or in the body should be up to the route definition.
As you read in the Camel documentation, the default behavior is to set the return value in the body:
TestBean testBean = new TestBean();
from("file:test/?delete=true")
.unmarshal(jaxb)
.bean(testBean, "testMethod")
.log("${body}")
.to("direct:nextRoute");
And if you want it in a header:
TestBean testBean = new TestBean();
from("file:test/?delete=true")
.unmarshal(jaxb)
.setHeader("MyMagicNumber", method(testBean, "testMethod"))
.log("${header.MyMagicNumber}")
.to("direct:nextRoute");
Be careful that if you use a Camel version older than 2.10, you will need to use the (now deprecated) "bean" method instead of the "method" method :-)
Depending on what you need to use it for you can either add it to the header or you can make it the body.
To add it to the header(key/value) do the following:
public class TestBean
{
#Handler
public void testMethod
(
#Body Message inMessage,
#Headers Map hdr,
Exchange exch
) throws Exception
{
int i= 75;
hdr.put("MyMagicNumber", i);
}
}
Your "return" result is now stored in the header and you can read it from there in the steps that follow.
For the body do the following:
public class TestBean
{
#Handler
public void testMethod
(
#Body Message inMessage,
#Headers Map hdr,
Exchange exch
) throws Exception
{
int i= 75;
inMessage.setBody(i);
}
}
The body of the message will now contain i.
I have 2 different URLs :
GET /stuff/{id} (where id is an Integer)
GET /stuff/foo?bar={someValue} (foo is not an Integer, it is a hard coded String)
When calling /stuff/foo&bar=someValue, I have the following error:
Failed executing GET /stuff/foo&bar=someValue
...
Caused by: java.lang.NumberFormatException: For input string: "foo&bar=someValue"
The code is :
#GET
#Path("/stuff/{id}")
public Response getById(#PathParam("id") int id) {
// code
}
#GET
#Path("/stuff/foo")
public Response foo(#QueryParam("bar") String bar) {
// code
}
I am using RESTEasy and I would like to keep my URLs that way if possible. Obviously RESTEasy just tries the getById method where it should use the foo method.
How can I do to make this work in RESTEasy ? ("change your URLs" is not an answer without detailed explanations about RESTEasy limitations). I tried (to be sure) putting foo code before getById but I had the same error (of course).
Is there any priority notion between declared URLs ?
As a note : I already implemented that kind of URLs in another framework (python-flask) and it worked just fine : you just have to be careful to declare /stuff/foo before /stuff/{id} (specific case before a more generic one).
EDIT: I just made a stupid mistake ! I was calling /stuff/foo&bar=someValue where I should be calling /stuff/foo?bar=someValue. Thanks #Scobal for pointing it out !
You are calling GET /stuff/foo&bar=someValue
You should be calling GET /stuff/foo?bar=someValue
RESTEasy is trying to parse foo&bar=someValue as the {id} field.
I can't give you an answer about RESTEasy URL priorities but you could do this:
#GET
#Path("/stuff/{id}")
public Response getById(#PathParam("id") String id, #QueryParam("bar") String bar) {
try {
int intId = Integer.parseInt(id);
// do int id things
} catch(NumberFormatException e) {
// do foo + bar things
}
}
Question is pretty self explanatory. I want to send 2 different arrays of objects through a POST form without ajax to my controller.
I changed my question to using ajax and using a get request due to the size of the params. Currently getting a 400 (Bad Request). I have no idea why. Please take a look...
I have objects:
var phone = {phoneId:"", phoneNumber:"", phoneType:""};
var schedule = {scheduleId:"", time:"", day:""};
Which I place into a javascript arrays:
var phones = [phone1, phone2, phone3];
var schedules = [schedule1, schedule2];
and I use ajax to send:
var data = {
index: id,
schedules: schedules,
phones: phones
}
var url = "/myController/myUrl"
$.getJSON(url, data, function(result){
if(result.ok){
$('#messageAlertSuccess').show();
} else {
$('#messageAlertError').show();
}
});
I created wrapping classes to map them like so:
public class PhoneWrapper(){
private String phoneId;
private String phoneNumber;
private String phoneType;
}
And of course the scheduleWrapper follows the same convention.
Here's the method in my controller:
#ResponseBody
#RequestMapping(value="/myUrl", method=RequestMethod.GET)
public Result doSomething(#RequestParam("index") int index,
#RequestParam("phones") Set<PhoneWrapper> phoneWrappers,
#RequestParam("schedules") Set<ScheduleWrapper> scheduleWrappers,
Model model,
HttpSession session){
//do stuff here.
}
I am currently getting a 400. So what's wrong?
Update: here's the url that the .getJSON jquery method is building:
http://localhost:8080/myApp/myController/myUrl?index=9&schedules%5B0%5D%5BscheduleId%5D=1&schedules%5B0%5D%5BfromDay%5D=Monday&schedules%5B0%5D%5BtoDay%5D=Friday&schedules%5B0%5D%5BfromTime%5D=08%3A30%3A00&schedules%5B0%5D%5BtoTime%5D=16%3A00%3A00&schedules%5B1%5D%5BscheduleId%5D=5&schedules%5B1%5D%5BfromDay%5D=Saturday&schedules%5B1%5D%5BtoDay%5D=Monday&schedules%5B1%5D%5BfromTime%5D=09%3A00%3A00&schedules%5B1%5D%5BtoTime%5D=13%3A00%3A00&phones%5B0%5D%5BphoneId%5D=6&phones%5B0%5D%5BphoneNumber%5D=787-788-1111&phones%5B0%5D%5BphoneType%5D=PHONE&phones%5B1%5D%5BphoneId%5D=106&phones%5B1%5D%5BphoneNumber%5D=787-795-4095&phones%5B1%5D%5BphoneType%5D=FAX
I see a few things that don't look right
unless you have getters and setters in your wrappers (DTO is a better name), i don't use them for my DTOs for xhr calls, you need to change
public class PhoneWrapper(){
private String phoneId;
private String phoneNumber;
private String phoneType;
}
to have public fields vs private
public class PhoneWrapper(){
public String phoneId;
public String phoneNumber;
public String phoneType;
}
Your js arrays are not arrays but objects;
var phones = {phone1, phone2, phone3};
var schedules = {schedule1, schedule2};
Here they are as arrays
var phones = [phone1, phone2, phone3];
var schedules = [schedule1, schedule2];
Make sure you naming is the same of both the js and java sides. I find it very helpful to turn on the debugging when troubleshooting these problems. log4j -
<logger name="org.springframework.web.servlet.mvc" >
<level value="debug" />
</logger>
EDIT
So after the question was updated with more info I notice that it was the same problem as Binding a list in #RequestParam
I would say that you are almost there! The first thing the you need is a wrapper to hold the two Set<> parameters since spring is not able to map a collection directly to parameters (yet?).
Also, there are two ways to handle this kind of requests:
use a json request and #Requestbody with a single javascript object in the request body an map this into a java class (automatically by spring). This means you need to change a little how the data is send down and this approach has one side effect: you cannot merge data simply by defining the parameter as a model attribute.
a second possibility is to stay with the post form submit. Also here you need to create the wrapper and use this one as a requestparam. Either one per Set<> parameter like #Sotirios mentioned in his answer or one parameter which holds both sets. Then you need to modify your submit data to send the phone and schedule information like input fields. I haven't used sets in this case but
lists and the parameter names would look like phoneWrapper[0].phoneId.
The advantage of the second approach is that you can merge the request data with existing values so you do not need to send down a complete phone information all the time.
var phones = {phone1, phone2, phone3};
var schedules = {schedule1, schedule2};
These two are not arrays (square brackets), but objects (curly brackets).
Compare with
var phones = ["phone1", "phone2", "phone3"];
var schedules = ["schedule1", "schedule2"];
and if you are to pass actual object references (phone1, phone2, phone3, schedule1 and schedule2 are object variables) then you need to use
var phones = [phone1, phone2, phone3];
var schedules = [schedule1, schedule2];
For spring the map request parameters to Class instance fields, they have to match the name of the parameter.
So with
<input type="hidden" name="someParameter" value="123"/>
and
public class SomeClass {
private String someParameter;
// getters and setters
}
a Spring controller will be able to be injected with a SomeClass instance whose field someParameter has the value 123 that comes from the html hidden input request parameter. This is also known as a command object.
A javascript array has no meaning to either html or http.
As for the solution, I would keep your class PhoneWrapper, use javascript to populate 3 <input> elements, and change the method definition to
#RequestMapping(value=MY_URL, method=RequestMethod.POST)
public String doSomething(#RequestParam("index") int index,
PhoneWrappers phoneWrappers,
ScheduleWrappers scheduleWrappers,
Model model,
HttpSession session){
Notice there are no more array [] brackets. (You would do the same for ScheduleWrappers).