How to catch HttpResponseException in AngularJS service? - angularjs

In my WebAPI controller, I have a function that returns either a correct result or a HttpResponseException.
public async Task<SearchResult> Search([FromUri] SearchArguments args)
{
try
{
SearchResult searchResult = await _case.Search(args, true);
string validationError = searchResult.Error;
if (!string.IsNullOrEmpty(validationError))
{
throw new HttpResponseException(new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
{
ReasonPhrase = string.Format("Could not find the case. {0}", validationError)
});
}
return searchResult;
}
catch (Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError)
{
ReasonPhrase = ex.Message
});
}
}
It is used in angularjs service which calls this function using a http get request as below.
svc.Search = function (searchArgs) {
var deferred = $q.defer();
var url = 'sci/case/search?';
$http.get(sc.baseURL + url, { params: searchArgs }).
then(function (r) {
if (!r || !r.data) {
console.log('Error: No Data Returned');
deferred.reject('No Data Returned');
}
deferred.resolve(r.data);
}, function (err) {
console.log('Error: ' + err);
deferred.reject(err);
});
return deferred.promise;
}
When there is a successful result, r.data contains the expected result. But when there is HttpResponseException thrown from the controller, I can't catch the ReasonPhrase. I need to format custom error messages in the controller and display them in UI. Is HttpResponseException appropriate to accomplish this? Thank you for any suggestions.

When you throw exception the execution flow gets interrupted and internal server error will be returned as result.you should handle error and return error message as the result.
I think you better define a class to represent API call result like this :
public class ApiCallResult {
public ApiCallResult (){
Succeeded = true;
}
public object Result {get;set;}
public bool Succeeded{get;set;}
public string Message {get;set;}
}
then :
public async Task<SearchResult> Search([FromUri] SearchArguments args)
{
var result = new ApiCallResult ();
try
{
SearchResult searchResult = await _case.Search(args, true);
result.Result = searchResult;
string validationError = searchResult.Error;
if (!string.IsNullOrEmpty(validationError))
{
result.Message = string.Format("Could not find the case");
result.Succeeded = false;
}
}
catch (Exception ex)
{
result.Message = ex.Message;
result.Succeeded = false;
}
return result;
}
and inside view :
svc.Search = function (searchArgs) {
var deferred = $q.defer();
var url = 'sci/case/search?';
$http.get(sc.baseURL + url, { params: searchArgs }).
then(function (r) {
if (!r || !r.Succeeded) {
console.log('Error: No Data Returned');
deferred.reject('No Data Returned');
}
deferred.resolve(r.Result.data);
}, function (r) {
console.log('Error: ' + r.Message);
deferred.reject(r.Message);
});
return deferred.promise;
}

You should read the statusText property value in the error callback. statusText property of the response object is a string value which has the status text of the response. HttpResponseException will set the ReasonPhrase value as the status text of the response.
$http.get(sc.baseURL + url, { params: searchArgs }).
then(function (r) {
if (!r || !r.data) {
console.log('Error: No Data Returned');
deferred.reject('No Data Returned');
}
deferred.resolve(r.data);
}, function (err) {
var errMsg = err.statusText;
alert(errMsg);
});

Related

Passing params: to Web API works with $http.get but not $http.Post

AngularJS 1.59
This API call works with $http.get.
JS ViewModel
$scope.placeOrder = function () { //'api/order/create'
var order = { AccountId : accountId, Amount : $scope.subTotal,
Tax: $scope.tax, Shipping: $scope.shipping }
var orderJSON = JSON.stringify(order);
viewModelHelper.apiGet('api/order/create', { params: { order: orderJSON } },
function (result) {
var orderId = result.data;
});
}
App.js
self.apiGet = function (uri, data, success, failure, always) {
self.isLoading = true;
self.modelIsValid = true;
$http.get(AlbumApp.rootPath + uri, data)
.then(function (result) {
success(result);
if (always != null)
always();
self.isLoading = false;
}, function (result) {
if (failure == null) {
if (result.status != 400)
self.modelErrors = [result.status + ': ' + result.statusText +
' - ' + result.data];
else
self.modelErrors = [result.data + ''];
self.modelIsValid = false;
}
else
failure(result);
if (always != null)
always();
self.isLoading = false;
});
}
self.apiPost = function (uri, data, success, failure, always) {
self.isLoading = true;
self.modelIsValid = true;
$http.post(AlbumApp.rootPath + uri, data)
.then(function (result) {
success(result);
if (always != null)
always();
self.isLoading = false;
}, function (result) {
if (failure == null) {
if (result.status != 400)
self.modelErrors = [result.status + ': ' + result.statusText + ' - ' + result.data];
else self.modelErrors = [result.data];
self.modelIsValid = false;
}
else failure(result);
if (always != null) always();
self.isLoading = false;
});
}
APIController
[HttpGet]
[Route("create")]
public IHttpActionResult Create(string order) {
var _order = JsonConvert.DeserializeObject<Order>(order); ... }
But since this is a Create function I want to use $http.post. When I change the call to
$scope.placeOrder = function () { //'api/order/create'
var order = { AccountId : accountId, Amount : $scope.subTotal,
Tax: $scope.tax, Shipping: $scope.shipping }
var orderJSON = JSON.stringify(order);
viewModelHelper.apiPost('api/order/create', { params: { order: orderJSON } },
//null,
function (result) {
var orderId = result.data;
});
}
and my controller Action to
[HttpPost]
[Route("create")]
public IHttpActionResult Create(string order) {
var _order = JsonConvert.DeserializeObject<Order>(order); ... }
I get a 404 error:
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost:50597/api/order/create'.
</Message>
<MessageDetail>
No action was found on the controller 'OrderApi' that matches the request.
</MessageDetail>
</Error>
Is this a bug or am I missing some conceptual point or do I have an error in my code?
Solution: (Thank you Giovani)
params: needs to be passed to the config in the $http.get and $http.post. The two methods have different signatures.
In apiGet renamed data to config.
In apiPost added a config.
In apiPost call added a null so the params: is passed to config rather than data.
App.js
self.apiGet = function (uri, config, success, failure, always) {
self.isLoading = true;
self.modelIsValid = true;
$http.get(AlbumApp.rootPath + uri, config)
...
self.apiPost = function (uri, data, config, success, failure, always) {
self.isLoading = true;
self.modelIsValid = true;
$http.post(AlbumApp.rootPath + uri, data, config)
JS ViewModel
$scope.placeOrder = function () { //'api/order/create'
var order = { AccountId : accountId, Amount : $scope.subTotal,
Tax: $scope.tax, Shipping: $scope.shipping }
var orderJSON = JSON.stringify(order);
viewModelHelper.apiPost('api/order/create', null, { params: { order: orderJSON } },
function (result) {
var orderId = result.data;
}); }
$http.get() and $http.post() have a different method signature. more info
$http.get(<URL>, <DATA (params, responseType, etc..)>)
$http.post(<URL>, <BODY_DATA>, <DATA (params, responseType, etc..)>

node mssql stream return issue

I am attempting to stream the data from a procedure that returns multiple data sets using node and mssql. If using as a stand a long function it works, but I need it to return the dataset from the route i am using.
handler: function(request, reply) {
var inputValues = request.payload.inputParams;
var procName = request.params.procedureName;
var request = new sql.Request(mainsettings.connection);
request.stream = true;
var newGroup = [];
var count = 0;
var recordSetArr = [];
for(var key in inputValues) {
var currentParam = inputValues[key];
var paramType = getParamType(sql, currentParam.paramType);
try {
request.input(currentParam.paramName, paramType, currentParam.paramValue);
}
catch(err) {
console.error(err);
}
}
request.execute(procName);
request.on('recordset', function(columns) {
// Emitted once for each recordset in a query
count++;
if(count > 1) {
recordSetArr.push(newGroup);
}
newGroup = [];
});
request.on('row', function(row) {
// Emitted for each row in a recordset
newGroup.push(row);
});
request.on('error', function(err) {
// May be emitted multiple times
console.error(err);
});
request.on('done', function(returnValue) {
// Always emitted as the last one
return (recordSetArr);
});
}
I am getting the following error.
Debug: internal, implementation, error
Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (F:\FARA_API\node_modules\hapi\l
ib\toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
180406/194833.505, (1523044113505:MISOM-DEV-002:6976:jfod4ysa:10140) [error] mes
sage: handler method did not return a value, a promise, or throw an error, stack
: Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (F:\FARA_API\node_modules\hapi\l
ib\toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
Any suggestions on how I am supposed to return the data set properly?
Also tried the following
handler: function(request, reply) {
recordRouteCall("procedure");
var inputValues = request.payload.inputParams;
var procName = request.params.procedureName;
sql.connect(mainsettings.connection, err => {
var request = new sql.Request();
request.stream = true;
var newGroup = [];
var count = 0;
var recordSetArr = [];
for(var key in inputValues) {
var currentParam = inputValues[key];
var paramType = getParamType(sql, currentParam.paramType);
try {
request.input(currentParam.paramName, paramType, currentParam.paramValue);
}
catch(err) {
console.error(err);
}
}
request.execute(procName);
request.on('recordset', columns => {
// Emitted once for each recordset in a query
count++;
if(count > 1) {
recordSetArr.push(newGroup);
}
newGroup = [];
});
request.on('row', row => {
// Emitted for each row in a recordset
newGroup.push(row);
});
request.on('error', err => {
// May be emitted multiple times
console.error(err);
});
request.on('done', result => {
// Always emitted as the last one
console.log(recordSetArr);
return (recordSetArr);
});
});
sql.on('error', err => {
console.error(err);
return err;
});
}

Angularjs $http get not working

I am trying to access REST web service from angularjs. I am not able to call it successfully.
AngularJs Code
var singleOrderUrl = "/singleOrder/retrieve";
function getSingleOrderDetails(userName,singleOrderUrl,$http,$q) {
var fd = new FormData();
var deffered = $q.defer();
fd.append('USERNAME', 'test123');
//fd.append();
//fd.append();
console.log("inside service"+userName+"singleOrderUrl:::"+singleOrderUrl);
return $http.get(singleOrderUrl, fd, {
withCredentials : false,
transformRequest : angular.identity,
headers : {
'Content-Type' : undefined,
}
}).success(function(response) {
console.log(response);
responseData = response.data.toString();;
deffered.resolve(response);
return responseData;
}).error(function(error) {
alert("error");
deffered.reject(error);
return "failed";
});
};
Rest Service code
#RestController
public class SingleOrderHistoryController {
private static final Logger logger = LoggerFactory.getLogger(SingleOrderHistoryController.class.getName());
#RequestMapping(value = "/singleOrder/retrieve", method=RequestMethod.GET, produces="application/json")
public List<SingleHistoryRecord> getSingleOrderDetails(#RequestParam(value = Constants.USER_NAME, required = true) String userName, HttpServletRequest request,HttpServletResponse response) throws Exception {
logger.debug("inside SingleOrderHistoryController ");
List<SingleHistoryRecord> singleOrderHistoryList = new ArrayList<SingleHistoryRecord>();
SingleHistoryRecord record1 = new SingleHistoryRecord();
SingleHistoryRecord record2 = new SingleHistoryRecord();
record1.setClientIdentifier(userName);
record1.setSubmitDate("01/05/2017");
record1.setStatus("Complete");
record1.setReferenceID("1234555");
record1.setOrderID("test123");
record2.setClientIdentifier(userName);
record2.setSubmitDate("01/05/2017");
record2.setStatus("Complete");
record2.setReferenceID("1234555");
record2.setOrderID("test123");
singleOrderHistoryList.add(record1);
singleOrderHistoryList.add(record2);
return singleOrderHistoryList;
}
Can anyone please advise what I am doing wrong here, It is getting the source code of the page in response instead of getting the list.

pass to error case when function returns a rejected promise in angular

I need to return a rejected promise from a js function. I am using angular $q as you can see. But it doesn't work.
In function getDBfileXHR, when the promise getDBfileXHRdeferred is rejected using getDBfileXHRdeferred.reject() I would to pass into the the error case of the function getDBfileXHR and run fallbackToLocalDBfileOrLocalStorageDB(). But it doesn't work.
Is there a syntax error ?
I am a bit new to promises.
Thanks
this.get = function () {
var debugOptionUseLocalDB = 0,
prodata = [],
serverAttempts = 0;
if (debugOptionUseLocalDB) {
return fallbackToLocalDBfileOrLocalStorageDB();
}
if (connectionStatus.f() === 'online') {
console.log("Fetching DB from the server:");
return getDBfileXHR(dbUrl(), serverAttempts)
.then(function () { // success
console.log('-basic XHR request succeeded.');
return dbReadyDeferred.promise;
}, function () { // error
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
});
}
}
function getDBfileXHR(url, serverAttempts) {
var getDBfileXHRdeferred = $q.defer(),
request = new XMLHttpRequest();
if (typeof serverAttempts !== "undefined") serverAttempts++;
request.open("GET", url, true); //3rd parameter is sync/async
request.timeout = 2000;
request.onreadystatechange = function () { // Call a function when the state changes.
if ((request.readyState === 4) && (request.status === 200 || request.status === 0)) {
console.log('-we get response '+request.status+' from XHR in getDBfileXHR');
var jsonText = request.responseText.replace("callback(", "").replace(");", "");
if (jsonText === '') {
console.error('-error : request.status = ' + request.status + ', but jsonText is empty for url=' + url);
if (serverAttempts <= 2){
sendErrorEmail("BL: jsonText is empty, trying to reach server another time", 11);
getDBfileXHR(url, serverAttempts);
return;
} else {
sendErrorEmail("BL: jsonText is empty and attempted to reach server more than twice", 14);
var alertPopup = $ionicPopup.alert({
title: 'Error '+"11, jsonText is empty",
template: "Sorry for the inconvenience, a warning email has been sent to the developpers, the app is going to restart.",
buttons: [{
text:'OK',
type: 'button-light'
}]
});
getDBfileXHRdeferred.reject();
}
} else {
}
} else {
console.error('-error, onreadystatechange gives : request.status = ' + request.status);
getDBfileXHRdeferred.reject();
}
};
if (url === "proDB.jsonp") {
console.log("-Asking local proDB.json...");
} else {
console.log("-Sending XMLHttpRequest...");
}
request.send();
return getDBfileXHRdeferred.promise;
}
EDIT:
I rewrote my function using this approach. It seems better and cleaner like this. But now can you help me handle the multiple attempds ?
function getDBfileXHR(url, serverAttempts) {
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.open("GET", url, true); request.timeout = 2000;
var rejectdum;
if (url === "proDB.jsonp") {
console.log("-Asking local proDB.json...");
} else {
console.log("-Sending XMLHttpRequest...");
}
request.onload = function () {
if ( (request.readyState === 4) && (request.status === 200 || request.status === 0) ) {
console.log('-we get response '+request.status+' from XHR in getDBfileXHR');
var jsonText = request.responseText.replace("callback(", "").replace(");", "");
if (jsonText === '') {
console.error('-error : request.status = ' + request.status + ', but jsonText is empty for url=' + url);
sendErrorEmail("BL: jsonText is empty, trying to reach server another time", 11);
sendErrorEmail("BL: jsonText is empty and attempted to reach server more than twice", 14);
var alertPopup = $ionicPopup.alert({
title: 'Error '+"11, jsonText is empty",
template: "The surfboard database could not be updated, you won't see the new models in the list, sorry for the inconvenience.",
buttons: [{
text:'OK',
type: 'button-light'
}]
});
console.log('oui on passe rejectdum')
rejectdum = 1;
reject({
status: this.status,
statusText: request.statusText
});
} else {
var parsedJson;
try {
parsedJson = JSON.parse(jsonText);
} catch (e) {
console.warn("Problem when trying to JSON.parse(jsonText) : ");
console.warn(e);
console.warn("parsedJson :");
console.warn(parsedJson);
}
if (parsedJson) {
var prodata = jsonToVarProdata(parsedJson);
console.log('-writing new prodata to localStorage');
console.log('last line of prodata:' + prodata[prodata-1]);
storageService.persist('prodata', prodata);
storageService.store('gotANewDB', 1);
}
resolve(request.response);
dbReadyDeferred.resolve();
}
}
};
request.onerror = function () {
reject({
status: this.status,
statusText: request.statusText
});
};
request.send();
});
}
Is it a clean way to do this to do several attempts :
return getDBfileXHR(dbUrl(), serverAttempts)
.then(function () { // success
console.log('-basic XHR request succeeded.');
return dbReadyDeferred.promise;
})
.catch(function (){
if (typeof serverAttempts !== "undefined") serverAttempts++;
console.log('on passe dans le catch, serverAttempts = ', serverAttempts)
if (serverAttempts < 2) {
return getDBfileXHR(dbUrl(), serverAttempts)
.then(function () { // success
console.log('-basic XHR request succeeded.');
return dbReadyDeferred.promise;
})
.catch(function (){
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
})
} else {
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
}
})
if you remove the code to retry (twice?) on failure your code would possibly work (haven't looked into that) -
the issue is, the only promise your calling code gets is that of the first attempt. If the first attempt fails, that promise is never resolved or rejected
You need to resolve the promise with the promise returned by getDBfileXHR(url, serverAttempts); - so, something like
if (serverAttempts <= 2){
sendErrorEmail("BL: jsonText is empty, trying to reach server another time", 11);
getDBfileXHRdeferred.resolve(getDBfileXHR(url, serverAttempts));
return;
} else {
Because if promise(1) resolves to a rejected promise(2), the result is that promise(1) rejects with the rejection value of promise(2)
This is how native Promises, and many many Promise/A+ compliant libraries work,
so this should be the case with $.defer if it follows the Promise/A+ spec

Spring Data Jpa, REST, org.springframework.beans .TypeMismatchException from Angularjs front end

I have request from my Angularjs factory is as follows:
.factory('TrackingNumberDetailsUpsGeneralService', function ($http) {
return {
findByInvoiceDetailsId: function (invoiceDetails) {
console.log("invoiceDetails " + invoiceDetails.id);
return $http.get('/api/trackingNumberDetailsUpss/byInvoiceDetailsId', {params: {invoiceDetailsId : invoiceDetails.id}}).then(function (response) {
return response.data;
});
}
};
});
And in my Tracking...Resource.java, I have
#RequestMapping(value = "/trackingNumberDetailsUpss/byInvoiceDetailsId/",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public List<TrackingNumberDetailsUps> findByInvoiceDetailsId(#RequestParam(value="invoiceDetailsId") Long invoiceDetailsId,
#RequestParam(value = "page" , required = false) Integer offset,
#RequestParam(value = "per_page", required = false) Integer limit) {
log.debug("REST request to get TrackingNumberDetailsUps By Invoice Details Id " + invoiceDetailsId);
return trackingNumberDetailsUpsService.findByInvoiceDetailsId(invoiceDetailsId);
}
But I am getting the following error:
http://localhost:8080/api/trackingNumberDetailsUpss/byInvoiceDetailsId?cacheBuster=1444014377012&invoiceDetailsId=2 400 Bad Request
{"timestamp":1444015194707,"status":400,"error":"Bad Request","exception":"org.springframework.beans
.TypeMismatchException","message":"Failed to convert value of type 'java.lang.String' to required type
'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: \"byInvoiceDetailsId
\"","path":"/api/trackingNumberDetailsUpss/byInvoiceDetailsId"}
I am not able to figure out where is the problem. Can any one help me in resolving this.
The following is my Request :
.factory('TrackingNumberDetailsUpsGeneralService', function ($http) {
return {
findByInvoiceDetailsId: function (invoiceDetails) {
console.log("invoiceDetails " + invoiceDetails.id);
return $http.get('/api/trackingNumberDetailsUpss/byInvoiceDetailsId', {params: {invoiceDetailsId : invoiceDetails.id}}).then(function (response) {
return response.data;
});
}
};
});
Try removing the extra / from the end of your #RequestMapping annotation.
It does matter - just tested.

Resources