IllegalStateException:Target object must not be null Spring Boot - java

I am developing a platform based on the micro services architecture (JAX-RS) and a nodeJS API.
I have a problem adding an object to the database, because it always marks null by spring boot.
*Here is my REST controller code (JAX-RS):
#RequestMapping(value="/Add")
public Actifs AjouterActifs( #RequestBody Actifs act){
return Actif.saveT(act);
}
*Here the code node API to add the object "Actifs":
app.post("/act/add",function (req,res) {
var addActif = JSON.stringify(req.body);
console.log("params: "+addActif);
try {
http.get(url+"/Add",+addActif, function (response) { //problem is here "addActif is null"
var dataJson ='';
response.on('data',function(data){
dataJson += data;
});
response.on('end',function(){
try
{
var addAct = JSON.parse(dataJson);
}
catch(err) {
console.log('erreur de retourner l\'actif -->', +err);
}
res.json(addAct);
});
});
}
catch(e) {
console.log("erreur d'ajouter les info d'actif -->", +e);
}
});
*Postman:
I get this error:
org.springframework.http.converter.HttpMessageNotReadableException:
Required request body is missing:
How to avoid a null object passing from node JS to the JAX-RS service ?
thank you for helping me,

You are sending the actif to be added as a query parameter
http.get(url+"/Add?act="+addActif, function (response) {
...
}
But your SpringMVC Endpoint expects to find the Actif object in the request body
#RequestMapping(value="/Add")
public Actifs AjouterActifs( #RequestBody(required=false) Actifs act) {
...
}
Option 1: Use #RequestParameter("act") Actifs act and register an editor to parse the object from the query parameter string (see this question).
Option 2: Actually send the Actif json as the request body, e.g. by performing a POST request to url + "/Add" instead of a GET. You will have to use http.request to implement that.
Furthermore I would suggest to use #RequestBody (without required=false). That ensures that the parameter must be non-null and lets the application fail fast if that is not the case.

I solved the problem by changing the code like this
app.post("/act/add",function (req,res) {
var addActif = JSON.parse(req.body); //parse object
console.log("params: "+addActif);
try {
http.get(url+"/Add",addActif, function (response) { // delete '+'
var dataJson ='';
response.on('data',function(data){
dataJson += data;
});
response.on('end',function(){
try
{
var addAct = JSON.parse(dataJson);
}
catch(err) {
console.log('erreur de retourner l\'actif -->', +err);
}
res.json(addAct);
});
});
}
catch(e) {
console.log("erreur d'ajouter les info d'actif -->", +e);
}
});

Related

How to return java response object based on conditional logic in reactive java?

Here I have a method where fetchReport is an external call to a vendor API. I want to copy that data to Azure Blob Storage but not if There was an error. If there was an error then I want to return the CustomResponse with the error details. writeToBlob() also returns a CustomResponse. I want to be able to preserve the error message from the external API to give to the consumer.
Is there any way I can use some conditional logic like
if response.contains("Failed") -> then return response with error details
else -> write to blob
public Flux<CustomResponse> getAndSaveReport(Mono<JsonNode> fetchReport, String reportFilePrefix) {
Mono<JsonNode> reportMono = fetchReport
.doOnSuccess(result -> {
log.info(Logger.EVENT_UNSPECIFIED, "Successfully retrieved report");
})
.switchIfEmpty(Mono.just(objectMapper.convertValue(new CustomResponse("No content"), JsonNode.class)))
.onErrorResume(BusinessException.class, err -> {
log.error(Logger.EVENT_FAILURE, "Failed to retrieve report");
JsonNode errJson = null;
CustomResponse apiResponse = new CustomResponse();
apiResponse.setStatus("Failed");
apiResponse.setMessage("Error message: " + err.getMessage());
apiResponse.setType(reportFilePrefix);
errJson = objectMapper.convertValue(apiResponse, JsonNode.class);
return Mono.just(errJson);
});
return writeToBlob(reportMono.flux(), reportFilePrefix).flux();
}
Any help would be appreciated!
Not sure what fetchReport returns but the code could be simplified by applying flatMap. Also, not sure why are you using flux() everywhere when only one signal is passed - you can use Mono instead.
public Mono<CustomResponse> getAndSaveReport(Mono<JsonNode> fetchReport, String reportFilePrefix) {
return fetchReport
.flatMap(result -> {
if (result.response.contains("Failed")) {
// error handling
return Mono.just(errorResponse);
} else {
return writeToBlob(result.report, reportFilePrefix)
}
});
}

Why is spring returning me an empty llist?

I dont seem to know why Spring is returning me an empty list enough I have passed in a JSON.stringify() string from reactJS
This is my code for reactJS
postData(item){
console.log(item)
fetch("http://localhost:8080/addSuspect", {
"method": "POST",
"headers": {
"content-type": "application/json"
},
"body": item
})
.then(response => {
console.log(response);
})
.catch(err => {
console.log(err);
});
}
uploadFile(event) {
let file
let file2
//Check if the movements andsuspected case profiles are uploaded
if(event.target.files.length !== 2){
this.setState({error:true, errorMsg:"You need to upload at least 2 files!"})
return
}
//Check if the file is the correct file
console.log("Files:")
for (var i=0, l=event.target.files.length; i<l; i++) {
console.log(event.target.files[i].name);
if (event.target.files[i].name.includes("_suspected")){
file = event.target.files[i]
}
else if (event.target.files[i].name.includes("_movements")){
file2 = event.target.files[i]
}
else{
this.setState({error:true, errorMsg:"You have uploaded invalid files! Please rename the files to <filename>_suspected (For suspected cases) or <filename>_movement (For suspected case movement)"})
return
}
}
//Reads the first file (Suspected profile)
if (file) {
const reader = new FileReader();
reader.onload = () => {
// Use reader.result
const lols = Papa.parse(reader.result, {header: true, skipEmptyLines: true}, )
console.log(lols.data)
// Posting csv data into db
// this.postData('"' + JSON.stringify(lols.data) + '"')
this.postData(JSON.stringify(lols.data))
// Adds names into dropdown
this.setState({dataList: ["None", ...lols.data.map(names => names.firstName + " " + names.lastName)]})
const data = lols.data
this.setState({suspectCases: data})
}
reader.readAsText(file)
}
}
Here is what I get from console.log():
[{"id":"5","firstName":"Bernadene","lastName":"Earey","email":"bearey4#huffingtonpost.com","gender":"Female","homeLongtitude":"","homeLatitude":"","homeShortaddress":"","homePostalcode":"552209","maritalStatus":"M","phoneNumber":"92568768","company":"Yadel","companyLongtitude":"","companyLatitude":""},{"id":"14","firstName":"Mada","lastName":"Lafaye","email":"mlafayed#gravatar.com","gender":"Female","homeLongtitude":"","homeLatitude":"","homeShortaddress":"","homePostalcode":"447136","maritalStatus":"M","phoneNumber":"85769345","company":"Eare","companyLongtitude":"","companyLatitude":""}]
Below shows the Code in my Spring Controller
#RestController
public class HomeController {
private final profileMapper profileMapper;
private final suspectedMapper suspectedMapper;
public HomeController(#Autowired profileMapper profileMapper, #Autowired suspectedMapper suspectedMapper) {
this.profileMapper = profileMapper;
this.suspectedMapper = suspectedMapper;
}
#GetMapping("/listAllPeopleProfiles")
//Removes the CORS error
#CrossOrigin(origins = "http://localhost:3000")
private Iterable<Peopleprofile> getAllPeopleProfiles (){
return profileMapper.findAllPeopleProfile();
}
#GetMapping("/listAllSuspectedCases")
#CrossOrigin(origins = "http://localhost:3000")
private Iterable<Suspected> getAllSuspected(){
return suspectedMapper.findallSuspected();
}
#PostMapping("/addSuspect")
#CrossOrigin(origins = "http://localhost:3000")
private void newSuspectedcases(ArrayList<Suspected> unformattedcases){
// try {
// final JSONObject obj = new JSONObject(unformattedcases);
//
// System.out.println(obj);
//// ObjectMapper mapper = new ObjectMapper();
//// List<Suspected> value = mapper.writeValue(obj, Suspected.class);
// } catch (JSONException e) {
// e.printStackTrace();
// }
//
// Gson gson = new Gson();
// List<Suspected> suspectedCases = gson.fromJson(unformattedcases, new TypeToken<List<Suspected>>(){}.getType());
System.out.println(unformattedcases);
// for (Suspected suspected : suspectedCases){
// suspectedMapper.addSuspectedCase(suspected);
// }
}
}
I am not sure I understand your issue. This is my best guess about what you meant and what you want to happen :
You want your controller to receive ArrayList < Suspected > as the POST request body
You want your controller to return ArrayList < Suspected > as the POST response body
If that's the case, try this :
[...]
#PostMapping("/addSuspect")
#CrossOrigin(origins = "http://localhost:3000")
#ResponseBody
private ArrayList<Suspected> newSuspectedcases(#RequestBody ArrayList<Suspected> unformattedcases){
[...]
System.out.println(unformattedcases);
[...]
return unformattedcases;
}
If it's not what you meant, please provide more information.
Firstly, your controller method is returning void and not, if I undestand correctly, the payload that you're trying to send. You have to make your controller method return List<Suspected> to receive a body in the response.
Another issue is that you're missing a #RequestBody annotation on the param, which tells Spring to get the body from the request and try to deserialize it to a ArrayList of Suspects.
Another thing to note, it is a good practice to use interfaces instead of implementation classes as parameters and return value in your methods. Consider using List<Suspected> instead of ArrayList<Suspected>
So the final method should look like this:
#PostMapping("/addSuspect")
#CrossOrigin(origins = "http://localhost:3000")
private List<Suspected> newSuspectedcases(#RequestBody List<Suspected> unformattedcases){
[...]
System.out.println(unformattedcases);
[...]
return unformattedcases;
}
PS For CORS issues you may want to using a local proxy setup as described in React docs: https://create-react-app.dev/docs/proxying-api-requests-in-development/ And configure CORS for remote environments, without adding localhost:3000.

Angular JS - How to get spring returned map value inside angular controller?

I am new to angular, can anyone tell me how to retrieve spring returned map value inside angular's controller?
Here is my code snippet:
app.js
// Service -----------------------------------------------------------------
myApp.service('FolderService', function ($log, $resource, $http) {
return {
onlineView: function(docId) {
var viwerResource = $resource('processOnlineView', {}, {
get: {method: 'POST', params: {'docId' : docId}}
});
return viwerResource.get();
}
}
})
// Controller -----------------------------------------------------------------
.controller('FolderController', function ($scope, $log, FolderService) {
//click online view
$scope.view = function(doc) {
var rtnMap = FolderService.onlineView(doc.cmObjectId);
console.log('rtnMap: ' + rtnMap );
// it shows rtnMap: [object Object]
var key = 'response';
var value = rtnMap[key];
console.log('value: ' + value );
// I want to get map value, but it shows undefined
// I except get "d:/tomcat/bin/hello.swf" here
$scope.rtnFileName = rtnMap;
}
});
my spring controller java code
#RequestMapping(value = "/processOnlineView", method = RequestMethod.POST)
public #ResponseBody Map<String, String> processOnlineView(#RequestParam(value = "docId") String docId) {
String resultDocName = "";
try {
// get File by docId
File file = queryFile(docId);
// set resultDocName value
resultDocName = file.getAbsolutePath(); // real file path, like: d:/tomcat/bin/hello.swf
} catch (Exception e) {
e.printStackTrace();
}
return Collections.singletonMap("response", resultDocName);
}
chrome log:
I can get expect value in html by using this:
rtnFileName: {{rtnFileName.response}}
html shows:
rtnFileName: d:/tomcat/bin/hello.swf
But how to get map value in angular controller directly?
Any suggestion would be appreciated.
Problem solved.
First, use $http post instead of $resource:
onlineView: function(docId) {
$http({
method: 'POST',
url: urlBase + '/processOnlineView',
params: {
docId: docId
}
})
.success(function(data, status, headers, config) {
console.log('success data: ' + data); // result: success data: [object Object]
for (key in data){
console.log('>> data key: ' + key );
console.log('>> data value: ' + data[key] );
}
var resultDocName = data['response'];
console.log('resultDocName: ' + resultDocName);
runFlexpaper(resultDocName);
})
.error(function(data, status, headers, config) {
});
}
Second, retrieve returned map inside 'success' block, because $http post is asynchronous call.
Use a service. For example:
var app = angular.module('myApp', [])
app.service('sharedProperties', function () {
var mapCoord= 'Test';
return {
getProperty: function () {
return mapCoord;
},
setProperty: function(value) {
mapCoord= value;
}
};
});
Inside your Main controller
app.controller('Main', function($scope, sharedProperties) {
$scope.mapCoord= sharedProperties.setProperty("Main");
});
Inside your Map controller
app.controller('Map', function($scope, sharedProperties) {
$scope.mapCoord= sharedProperties.getProperty();
});

Pass json object to a play-framework action

I am currently using Play v1.2.3. I have an endpoint to which I want to send a json object which will be deserialized into a Java object. So, I have something that looks like this:
public class UserController extends Controller {
public static class FullName {
public String first;
public String last;
}
public static void putName( FullName name ) { ... }
}
##### routes
PUT /user/name UserController.putName
With that in place, I would hope to call the endpoint with the given javascript:
$.ajax({
type: "PUT",
data: { first: "Michael", last: "Bailey" },
url: "/user/name"
});
Unfortunately, with the above setup, it seems that play is not wanting to send the entire data object, but is instead attempting to populate two parameters (first and last). Is there a way to define the endpoint to consume the complete body directly, or does it have to be done by hand?
To cast the entire input body into a Model class:
public static void create(JsonObject body) {
CaseFolder caseFolder = new Gson().fromJson(body, CaseFolder.class);
caseFolder.user = getConnectedUser();
if(caseFolder.validateAndSave()) {
renderJSON(
new JSONSerializer()
.exclude("*.class")
.exclude("user")
.serialize(caseFolder));
} else
error();
}
Also, the above code takes the resulting Model object and serializes it back out to JSON as the response body.
If you want to just access certain fields within the JSON request, you can use:
public static void update(JsonObject body) {
try {
Long id = (long) body.get("id").getAsInt();
CaseFolder cf = CaseFolder.loadAndVerifyOwner(getConnectedUser(), id);
cf.number = body.get("number").getAsString();
cf.description = body.get("description").getAsString();
if(cf.validateAndSave())
ok();
else
error();
}
catch (NullIdException e) {error();}
catch (NotFoundException e) {notFound();}
catch (NotOwnerException e) {forbidden();}
catch (Exception e) {e.printStackTrace(); error();}
}
Play's action method parameter binding mechanism does not accept JSON. You need to bind it manually. In your example, the code could be something like:
public static void putName( String data ) {
FullName fname = new Gson().fromJSON(data, FullName.class);
...
}
Note, Gson is provided with play!framework distribution, so you are free to use it
With your settings play is expecting params with names "name.first" and "name.last" and you are sending "first" and "last". Try with this ajax post
$.ajax({
type: "PUT",
data: {
name: {
first: "Michael",
last: "Bailey"
}
},
url: "/user/name"
});

JavaScript - how to display error message from backend system based on Spring MVC

I have a web application with HTML / jQuery which ic connected with AJAX / JSON to a backend system with Java EE / Spring MVC.
In the frontend, a Person can be created by fill in the form fields and then it is submitted and this jQuery code executed:
var person = $(this).serializeObject();
$.postJSON("add/", person, function(data) {
alert("Person with ID "+data.person.id+"' added successfully");
});
In the best case, the Person is created and I'll get a Person object and I can access the values with data.person.*.
Now I want to validate the data which is sent to the backend system and in a case of an error, I want to display in the first step an alert error message.
I did this in the backend system:
#RequestMapping(value="add/", method=RequestMethod.POST)
public #ResponseBody Map<String, ? extends Object> addPerson(#RequestBody Person p, HttpServletResponse response) {
Set<ConstraintViolation<Person>> failures = validator.validate(p);
if (!failures.isEmpty()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return validationMessages(failures);
} else {
Person person = this.personService.addPerson(p);
return Collections.singletonMap("person", new SerialPerson(person.getId(), person.getName(), ...));
}
}
// internal helpers
private Map<String, String> validationMessages(Set<ConstraintViolation<Person>> failures) {
Map<String, String> failureMessages = new HashMap<String, String>();
for (ConstraintViolation<Person> failure : failures) {
failureMessages.put(failure.getPropertyPath().toString(), failure.getMessage());
System.out.println(failure.getPropertyPath().toString()+" - "+failure.getMessage());
}
return failureMessages;
}
My Person object is annotated, and I get the System.out.println(failure.getPropertyPath().toString()+" - "+failure.getMessage()); on the console, that for example, "name - must be between 1-30 chars"
But how can create an alert message in jQuery in the frontend system?
Thank you in advance for your help & Best Regards.
Update: Link to the Spring MVC AJAX example, where I found the validationMessages method. But there is also no solution how to get the error message.
SOLUTION:
I have to call:
jQuery.ajax({
'type': 'POST',
'url': "add/",
'contentType': 'application/json',
'data': JSON.stringify(person),
'dataType': 'json',
'success': function(data) {alert("success");},
'error': function(xhr) {alert(xhr.responseText);}
});
You can do something like this:
var person = $(this).serializeObject();
$.postJSON("add/", person, function(data) {
if(data.person) {
alert("Person with ID "+data.person.id+"' added successfully");
}
else {
var errors = "";
for(var key in data) if(data.hasOwnProperty(key)) {
errors += data[key] + "\n";
}
alert(errors);
}
});
You shouldn't need to send back a bad request either. Is this what you want?
UPDATE
You can use the code shown in Spring Source, but you'd have to use jQuery.ajax
jQuery.ajax({
type: 'POST',
url: "add/",
data: person,
dataType: "json",
success: function(data) {
alert("Person with ID "+data.person.id+"' added successfully");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
var errorJSON = JSON.parse(XMLHttpRequest.responseText); //if this is JSON otherwise just alerting XMLHttpRequest.responseText will do
var errors = "";
for(var key in errorJSON) if(errorJSON.hasOwnProperty(key)) {
errors += errorJSON[key] + "\n";
}
alert(errors);
}
});

Categories