I'm trying to call a JavaEE 6 rest service from an Angular factory and I am running into issues.
The java rest service was created by someone else and I'm trying to work off of it and I'm not super-well versed in JavaEE yet, so if you need more info, I'll chip in where I can.
#Path("bills")
public class BillResource {
#EJB
#Resource
private BillableEventBean billableEventBean;
private final BillableEventMapper billableEventMapper = new BillableEventMapper();
BillService implementation not working
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("query")
public List<BillableEventDuplicate> listBillableEvents(BillableEventQueryFilter queryFilter) {
List<BillableEvent> ejbRet = billableEventBean.listBillableEvents(queryFilter.getUserID(),
queryFilter.getBillingTeamID(), queryFilter.getBillStatus(), null);
List<BillableEventDuplicate> ret = billableEventMapper.toBillableEventDuplicateList(ejbRet);
return ret;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{billableEventI}")
public BillableEventDuplicate getBillableEvent(#PathParam("billableEventI") String id) {
BillableEvent ejbRet = billableEventBean.getBillableEvent(id);
BillableEventDuplicate ret = billableEventMapper.toBillableEventDuplicate(ejbRet);
return ret;
}
}
My angular factory for the service looks like this:
'use strict';
var aumBills = angular.module('appBills', ['ngResource']);
appBills.factory('Bills', ['$resource',
function($resource)
{
return $resource('/ua_appcore/api/v1/bills/:billNumber',
{
billNumber:'#billNumber'
},
{
getList: {method: 'POST', params: {'userID':'ABC123'}, url: '/ua_appcore/api/v1/bills/query/'}
});
}]);
The factory in invoked from the controller thusly:
'use strict';
angular.module('app.bills', ['ngRoute','appBills'])
.config(['$routeProvider', function($routeProvider)
{
$routeProvider.
when('/bills',
{
templateUrl: 'bills/bill-list.html',
controller: 'BillListCtrl'
}).
when('/bills/:billId',
{
templateUrl: 'bills/bill-detail.html',
controller: 'BillDetailCtrl'
}).
when('/billsearch',
{
templateUrl: 'bills/bill-search.html',
controller: 'BillSearchCtrl'
});
}])
.controller('BillListCtrl', ['$scope', '$http', '$routeParams', 'Bills',
function($scope, $http, $routeParams, Bills)
{
Bills.getList({},{}).$promise.then(function(billList)
{
$scope.billList = billList;
});
}])
.controller('BillDetailCtrl', ['$scope', '$http', '$routeParams', 'Bills',
function($scope, $http, $routeParams, Bills)
{
Bills.get({},{billNumber: $routeParams.billNumber }).$promise.then(function(bill)
{
$scope.bill = bill;
});
}]);
In my understanding, I needed to create a custom action in the factory to make use of the URL option since there is a POST to get a list of bills back using the same root call. The problem I'm having is that I can't seem to be able to feed any parameters into the queryFilter object even with the Consumes annotation. Am I doing something fundamentally wrong?
First of all you can use chrome plugin Postman to check if your rest service is working correctly if true there is problem with your angular $resource. In my opinion there is missing header "Content-Type=application/json" in your request. I've never use angular in that way you can try to create service like that (this tutorial should be also helpful)
app.service("billsService", function ($resource) {
var bills = $resource("/ua_appcore/api/v1/bills/query");
this.getBills = function () {
var billsResource = new bills();
billsResource.sampleVar = 'value'; // here you can place your vars
return billsResource.$save();
}
});
So, it turns out that it was an infrastructure issue the entire time. We had been deploying to a Websphere Liberty server, and it does not contain the entire WebProfile that JavaEE 6 needs to recognize the rest services. Their may be some work-arounds for it, but the quickest short-term solution is to run a full Websphere server for deployments.
Thanks for the help!
Related
Somewhat based on this guide:
https://jaxlondon.com/blog/java-core-languages/put-spring-boot-und-vue-js-practical-use-project-tutorial/
I have created a multi module maven project where one submodule is my backend and another submodule is my frontend. When I build the whole project first the frontend is "build" then its dist/ resources are copied to the backend which is then build and I can successfully start my spring boot backend with java -jar target/backend-1.0.0-SNAPSHOT and access it on localhost:8080
which makes sense based on the controller I have implemented in the backend:
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
#RequestMapping("/")
public Greeting root(#RequestParam(value = "name", defaultValue = "Root!") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
If I instead access: http://localhost:8080/index.html I end up in my frontend:
Which currently have the following two routes:
router.js
Vue.use(Router);
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: HomeRoute
},
{
path: '/myroute',
name: 'myroute',
component: MyRoute
}
]
});
export default router;
And in e.g. App.vue I have:
<template>
<div class="hello">
<li>
<router-link to="/MyRoute">GoToMyRoute</router-link>
</li>
<li>
<router-link to="/">GoToHome</router-link>
</li>
<router-view></router-view>
</div>
</template>
That I can also access, e.g.:
So far so good. But if I try to enter:http://localhost:8080/MyRoute directly in my browser I get:
which I assume is because I am missing a backend #RequestMapping for /MyRoute in my controller.
Based on the above my questions become:
Do I need to maintain a backend RequestMapping for each vuejs route I have if I want to be able to access it directly in the browser?
How do I separate/order my frontend and backend endpoint? Right now it seems there is no convention for when a backend endpoint is accessed compared to a pure frontend endpoint/route.
I would suggest you to do it this way:
Have one "ui" controller on your backend which would forward any unmapped routes to your frontend application e.g.:
#RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
Other rest endpoints defined in your backend should be defined after some prefix like "/api", or "/rest" (e.g. localhost:8080/api/some-data would return your json data). So every data endpoint will have this prefix.
Every route visible to user (view navigation) should be done on vue.js side as SPA routing. (e.g. localhost:8080/welcome etc.)
I tried with the following code (inspired by Gus answer):
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
#RestController
public class RouterCtrl {
#RequestMapping("/**/{path:[^.]*}")
public ModelAndView redirect() {
return new ModelAndView("forward:/");
}
}
And consider:
Your backend endpoints must start with the prefix api/ (or some other distinctive word)
Deal with the 404 not found with Router Vue, something like this:
Vue.use(Router);
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: HomeRoute
},
{
path: '/myroute',
name: 'myroute',
component: MyRoute
},
{
path: '/:catchAll(.*)',
name: 'notFound',
component: NotFound
}
]
});
export default router;
I made a simple gif to ilustrate :9
I would suggest remove the following from your code:
#RequestMapping("/")
public Greeting root(#RequestParam(value = "name", defaultValue = "Root!") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
Once the above is done, all routes will then go via index.html and your vue routes will take over from there.
You can place your other endpoints behind /api or something else.
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);
}
});
My days of Java web development now lie about 6 years behind me and despite my hectic new life as a non-technical consultant I want to get back in to the tech world and equip myself with some essential web dev skills.
To get me started, I installed a vagrant box using this tutorial:
https://dzone.com/articles/vagrant
... which worked like a treat and got me my Ubuntu box on my Windows host machine up and running in no time. Also it comes with Java 7 and the Tomcat app server that I'm still quite familiar with from past days. Notwithstanding the fact that there are probably better servers out there to practice on, this one works and I'll use it for my tinkering for now. The example web app that came with the tutorial also works, so I'm confident that my Tomcat is running on the guest machine on port 8080.
The next step was to find a good AngularJS and Spring MVC tutorial. Again, while I know that AngularJS is the latest craze in web dev, Spring MVC may be somewhat outdated (?) but since I'm a Java-boy since I hatched from the Uni-egg I wouldn't mind going with it for now.
The tutorial I found is this one:
http://websystique.com/springmvc/spring-mvc-4-angularjs-example/
I downloaded the project from git and deployed it into my tomcat webapps folder. In the user_service.js file I left the REST_SERVICE_URI as http://localhost:8080/Spring4MVCAngularJSExample/user/ given that Tomcat runs on port 8080 on the host Ubuntu box and I can access the application on my guest machine in the browser at http://192.168.33.10:8080/Spring4MVCAngularJSExample
The problem is that the application (while it's showing up in the browser), does not load the mock-users that are populated in the UserServiceImpl class and that should show up when loading the app. When I check my Firefox console under the JavaScript tab, I get the 'Error while fetching Users' error message from the fetchAllUsers function in the user_controller.js script.
I suspect that the problem here is that the front-end (AngularJS $http service) cannot contact the back-end (Spring service). If there were no users in the back-end and returned 0, I wouldn't get the error but an empty set instead, hence my suspicion of some other problem.
My question is how to debug this web app from here? I have tried to look through the front-end console logs using the FF Developer tool (Debugger) and I must admit I haven't written any JUnit test to actually run a test against the Spring service implementation class.
Thanks for your advice, and let me know if I should provide any more details.
Cheers
AHL
Spring controller:
package com.websystique.springmvc.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import com.websystique.springmvc.model.User;
import com.websystique.springmvc.service.UserService;
#RestController
public class HelloWorldRestController {
#Autowired
UserService userService; //Service which will do all data retrieval/manipulation work
//-------------------Retrieve All Users--------------------------------------------------------
#RequestMapping(value = "/user/", method = RequestMethod.GET)
public ResponseEntity<List<User>> listAllUsers() {
List<User> users = userService.findAllUsers();
if(users.isEmpty()){
return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
//-------------------Retrieve Single User--------------------------------------------------------
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(#PathVariable("id") long id) {
System.out.println("Fetching User with id " + id);
User user = userService.findById(id);
if (user == null) {
System.out.println("User with id " + id + " not found");
return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
//-------------------Create a User--------------------------------------------------------
#RequestMapping(value = "/user/", method = RequestMethod.POST)
public ResponseEntity<Void> createUser(#RequestBody User user, UriComponentsBuilder ucBuilder) {
System.out.println("Creating User " + user.getUsername());
if (userService.isUserExist(user)) {
System.out.println("A User with name " + user.getUsername() + " already exist");
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}
userService.saveUser(user);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/user/{id}").buildAndExpand(user.getId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
//------------------- Update a User --------------------------------------------------------
#RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
public ResponseEntity<User> updateUser(#PathVariable("id") long id, #RequestBody User user) {
System.out.println("Updating User " + id);
User currentUser = userService.findById(id);
if (currentUser==null) {
System.out.println("User with id " + id + " not found");
return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
currentUser.setUsername(user.getUsername());
currentUser.setAddress(user.getAddress());
currentUser.setEmail(user.getEmail());
userService.updateUser(currentUser);
return new ResponseEntity<User>(currentUser, HttpStatus.OK);
}
//------------------- Delete a User --------------------------------------------------------
#RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public ResponseEntity<User> deleteUser(#PathVariable("id") long id) {
System.out.println("Fetching & Deleting User with id " + id);
User user = userService.findById(id);
if (user == null) {
System.out.println("Unable to delete. User with id " + id + " not found");
return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
userService.deleteUserById(id);
return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
//------------------- Delete All Users --------------------------------------------------------
#RequestMapping(value = "/user/", method = RequestMethod.DELETE)
public ResponseEntity<User> deleteAllUsers() {
System.out.println("Deleting All Users");
userService.deleteAllUsers();
return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
}
IndexController.java:
package com.websystique.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping("/")
public class IndexController {
#RequestMapping(method = RequestMethod.GET)
public String getIndexPage() {
return "UserManagement";
}
}
Javascript user_controller.js:
'use strict';
angular.module('myApp').controller('UserController', ['$scope', 'UserService', function($scope, UserService) {
var self = this;
self.user={id:null,username:'',address:'',email:''};
self.users=[];
self.submit = submit;
self.edit = edit;
self.remove = remove;
self.reset = reset;
fetchAllUsers();
function fetchAllUsers(){
UserService.fetchAllUsers()
.then(
function(d) {
self.users = d;
},
function(errResponse){
console.error('Error while fetching Users');
}
);
}
function createUser(user){
UserService.createUser(user)
.then(
fetchAllUsers,
function(errResponse){
console.error('Error while creating User');
}
);
}
function updateUser(user, id){
UserService.updateUser(user, id)
.then(
fetchAllUsers,
function(errResponse){
console.error('Error while updating User');
}
);
}
function deleteUser(id){
UserService.deleteUser(id)
.then(
fetchAllUsers,
function(errResponse){
console.error('Error while deleting User');
}
);
}
function submit() {
if(self.user.id===null){
console.log('Saving New User', self.user);
createUser(self.user);
}else{
updateUser(self.user, self.user.id);
console.log('User updated with id ', self.user.id);
}
reset();
}
function edit(id){
console.log('id to be edited', id);
for(var i = 0; i < self.users.length; i++){
if(self.users[i].id === id) {
self.user = angular.copy(self.users[i]);
break;
}
}
}
function remove(id){
console.log('id to be deleted', id);
if(self.user.id === id) {//clean form if the user to be deleted is shown there.
reset();
}
deleteUser(id);
}
function reset(){
self.user={id:null,username:'',address:'',email:''};
$scope.myForm.$setPristine(); //reset Form
}
}]);
Javascript user_service.js:
'use strict';
angular.module('myApp').factory('UserService', ['$http', '$q', function($http, $q){
var REST_SERVICE_URI = 'http://localhost:8080/Spring4MVCAngularJSExample/user/';
var factory = {
fetchAllUsers: fetchAllUsers,
createUser: createUser,
updateUser:updateUser,
deleteUser:deleteUser
};
return factory;
function fetchAllUsers() {
var deferred = $q.defer();
$http.get(REST_SERVICE_URI)
.then(
function (response) {
deferred.resolve(response.data);
},
function(errResponse){
console.error('Error while fetching Users');
deferred.reject(errResponse);
}
);
return deferred.promise;
}
function createUser(user) {
var deferred = $q.defer();
$http.post(REST_SERVICE_URI, user)
.then(
function (response) {
deferred.resolve(response.data);
},
function(errResponse){
console.error('Error while creating User');
deferred.reject(errResponse);
}
);
return deferred.promise;
}
function updateUser(user, id) {
var deferred = $q.defer();
$http.put(REST_SERVICE_URI+id, user)
.then(
function (response) {
deferred.resolve(response.data);
},
function(errResponse){
console.error('Error while updating User');
deferred.reject(errResponse);
}
);
return deferred.promise;
}
function deleteUser(id) {
var deferred = $q.defer();
$http.delete(REST_SERVICE_URI+id)
.then(
function (response) {
deferred.resolve(response.data);
},
function(errResponse){
console.error('Error while deleting User');
deferred.reject(errResponse);
}
);
return deferred.promise;
}
}]);
On the server (the vagrant host machine), I can wget the URL and get my data back from the Spring server:
vagrant#precise32:~$ wget http://localhost:8080/Spring4MVCAngularJSExample/user/--2016-08-26 11:08:24-- http://localhost:8080/Spring4MVCAngularJSExample/user/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/json]
Saving to: `index.html'
[ <=> ] 206 --.-K/s in 0s
2016-08-26 11:08:24 (9.77 MB/s) - `index.html' saved [206]
vagrant#precise32:~$ less index.html
this gives me the expected result set:
[{"id":1,"username":"Sam","address":"NY","email":"sam#abc.com"},{"id":2,"username":"Tomy","address":"ALBAMA","email":"tomy#abc.com"},{"id":3,"username":"Kelly","address":"NEBRASKA","email":"kelly#abc.com"}]
From your spring controller code all your request mappings are expecting user in the url so if you don't have this the spring controllers will not be called. Is you dispatcher servlet set up to except all http requests E.g /
The problem was a simple mistake:
In the user_service.js file, the REST_SERVICE_URI must be set to the address of the host machine:
http://192.168.33.10:8080/Spring4MVCAngularJSExample/user/
So, when deploying this to a (remote) server, I suppose the 192.168.33.10:8080 portion would need to be changed to that server's IP and the respective Tomcat port.
My problem existed (probably) because I was using a virtual box and had (mistakenly) used the host machine's IP instead of the guest machine's IP. Please correct me if I'm wrong, I'm still somewhat confused ....
I configured Angularjs ui-grid options, to be specific, columnDefs, from Java but because the grid loads before it gets the columnDefs configuration from Java, filtering doesn't show because it loads before and doesn't see any columns.
I used the $http.get('url') function(gridOps) to get the columnDefs.
How could I load config data from Java before ui-grid loads?
Thank you!
Found the solution:
var app = angular.module('app', ['ngTouch', 'ui.grid', 'ui.grid.selection', 'ui.grid.exporter', 'ui.grid.importer', 'ui.grid.autoResize']);
fetchData().then(bootstrapApplication);
}
function fetchData() {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
return $http.get("url").then(function(response) {
app.constant("config", response.data);
}, function(errorResponse) {
// Handle error case
});
}
app.controller('MainCtrl', ['$scope','uiGridConstants', '$http','config', function ($scope, uiGridConstants, $http, config) {
I am fiddling around with JBOSS's Web Services, and I have created the following:
http://127.0.0.1:8080/IM/TestService?wsdl
Now I need to access Web Methods from that Web Service from JavaScript.
Say I have a web method named foo in TestService, how do I make an ajax call to it?
I tried accessing the method via http://127.0.0.1:8080/IM/TestService/foo, but I'm getting an HTTP Status 404.
I wrote the following JavaScript that will allow me to call the Web Methods from the JBoss Web Service.
Dependencies
jQuery
XML Objectifier
jQuery Soap Client (depends on jQuery and XML Objectifier)
var WS = function (url, ns, nsName) {
return function (method, parameters, callback) {
var i, j, para, soapBody = new SOAPObject(method), sr, response;
soapBody.ns = {
name: nsName,
uri: ns
};
if (typeof parameters === "function") {
callback = parameters;
} else if (parameters && parameters.length) {
for (i = 0, j = parameters.length; i < j; ++i) {
para = parameters[i];
soapBody.appendChild(new SOAPObject(para.name)).val(para.value);
}
}
sr = new SOAPRequest(method, soapBody);
SOAPClient.Proxy = url;
SOAPClient.SendRequest(sr, function (r) {
response = r.Body[0][method + "Response"][0]["return"][0]["Text"];
if (callback) {
callback.call(sr, response);
}
});
}
};
Usage
var ws = WS("http://127.0.0.1:8080/IM/TestService", "http://wservices/", "ns2");
ws("foo", [{name: "name", value:"dreas"}], function (r) {
console.log(r);
});
Disclaimer: This is still very much untested, so it can still blow up your computer