Having trouble with a 400 Bad Request (Angular + WebAPI) - javascript

I am learning by trying to build a database that tracks comics. I can POST new comics and GET them with no trouble. But when I'd like to PUT, I run into a problem. I keep getting a bad request sent, but I think all the information is correct. I think all my info matches up, but I'm not sure what else is wrong.
All I am trying to do is update the comic list so you can track your physical and digital copies of the comic.
Thanks in advance.
Here is my DBController.cs:
[Authorize]
public IHttpActionResult Put(Comic comic)
{
string UserId = User.Identity.GetUserId();
if (UserId == null) return BadRequest("You Must Be Logged In to Edit");
else if (comic.UserId == UserId || User.IsInRole("Admin"))
{
using (ApplicationDbContext db = new ApplicationDbContext())
{
var currentComic = db.Comics.Find(comic.ComicId);
currentComic.IsDigital = comic.IsDigital;
currentComic.IsPhysical = comic.IsPhysical;
db.SaveChanges();
}
return Ok();
}
else return BadRequest("Insufficient privileges");
}
}
Here is my CollectionController.js:
$scope.physical = false;
$scope.digital = false;
$scope.updateComic = function (ComicId) {
var comic = {
ComicId: ComicId,
IsPhysical: $scope.physical,
IsDigital: $scope.digital,
}
return MarvelApiFactory.editComic(comic).then(function (data) {
})
}
And my ApiFactory.js
var editComic = function (comic) {
var deferred = $q.defer();
$http({
url: '/api/ComicDB',
method: "PUT",
headers: { Authorization: "Bearer " + localStorage.getItem('token') },
data: comic
}).success(function () {
deferred.resolve();
}).error(function () {
deferred.reject();
})
return deferred.promise;
}
return {
editComic: editComic,
}
Here is my .html:
<button class="btn btn-danger" ng-click="updateComic(x.ComicId)">Save</button>
And lastly, my error messages. Sorry, not really sure how/what you need. Last night when I was figuring this out, I had clicked on the network tab and was ble to find inner exceptions and such. Either I can't find them this time, or I didn't get any. But this is from my JS console:
PUT http://localhost:53612/api/ComicDB 400 (Bad Request)angular.js:9827 (anonymous function)angular.js:9628 sendReqangular.js:9344 serverRequestangular.js:13189 processQueueangular.js:13205 (anonymous function)angular.js:14401 Scope.$evalangular.js:14217 Scope.$digestangular.js:14506 Scope.$applyangular.js:21440 (anonymous function)jquery-1.10.2.js:5109 jQuery.event.dispatchjquery-1.10.2.js:4780 elemData.handle

PUT and DELETE are not enabled in IIS Express and IIS8 by default. You can enable these verbs by following these steps:
Open the applicationHost.config file on the machine
running the Web Api application. The file is located at %userprofile%\documents\IIS{Express|8}\config”.
scroll down to the bottom of the IIS
applicationHost.config file and look for a handler entry
that starts with:
<add name="ExtensionlessUrl-Integrated-4.0"...`.
In the "verb" attribute add PUT and DELETE so the "verb" attribute
looks like: verb="GET,HEAD,POST,DEBUG,PUT,DELETE"

Related

Node JS Express how can I send a 404 error if a bad request is made to third party API?

In my Node JS server I have this route handler that sends a request to a third party API to get a username:
app.get('/players/:player', apiLimiter, function(request, response) {
const player = request.params.player;
const api_url = `https://api.com/shards/steam/players?filter[playerNames]=${player}`;
var options = {
method: "GET",
observe: 'body',
};
let apiRequest = https.request(api_url, options, function (res) {
let data = "";
res.on("data", chunk => {
data += chunk;
})
res.on("end", () => {
let objectParsed = JSON.parse(JSON.stringify(data));
response.send(objectParsed);
})
if(!player) {
res.status(404).send("Not found.");
}
})
apiRequest.end();
})
This works fine to get a user that exists. However, if I put in a fake username to my /players page, that page still loads with a 200 status instead of getting a 404 response. The page loads and looks broken because it's not actually getting any data from the API.
I feel like this is a dumb question .. In my research I have found how to handle errors if it's just the route, and not if it's the route dependent on the path parameter as in /players/:player
I found a question that was similar to mine (How to throw a 404 error in express.js?) and I tried using an If statement: if (!player){res.status(404).send("Not found."); } but no dice. Am I using this if statement in the wrong place?
How can I get my Node JS server to respond with a 404 if the user from the database doesn't exist?
You have to check the result of the API call and see if you got valid data back and send the 404 there. I also added a check to make sure something was passed for the player name and send back a 400 (bad request) if there's no player specified at all:
app.get('/players/:player', apiLimiter, function(request, response) {
const player = request.params.player;
if (!player) {
res.status(400).send("No player specified.");
return;
}
const api_url = `https://api.com/shards/steam/players?filter[playerNames]=${player}`;
var options = {
method: "GET",
observe: 'body',
};
let apiRequest = https.request(api_url, options, function(res) {
let data = "";
res.on("data", chunk => {
data += chunk;
})
res.on("end", () => {
let objectParsed = JSON.parse(data);
// test objectParsed here
if (!some condition in objectParsed) {
res.status(404).send("No data for that player name.");
} else {
response.send(objectParsed);
}
});
});
apiRequest.end();
});
Also, you don't want JSON.parse(JSON.stringify(data)) here. Your data is already a string. Just do JSON.parse(data).
FYI, if you use a small http request library such as got(), this code gets a lot simpler as it accumulates the response and parses the JSON for you in one line of code as in:
let data = await got(options).json()

Spring Web Application Returning 405 error on PUT Call

I have a Java Spring web application that creates a list of roles that can be assigned to users. However, I am having an issue creating new roles which is invoked through an AJAX PUT call that returns a 405 error. The application is running on Java 8 and Spring 5.1.1.
I have tried debugging both the front end and back end side. What I found was, the call successfully reaches the back-end, processes the call through and returns. However, the front-end will claim that an error occurred and returns a 405 error. But the issue is, the error does not provide any details on what is failing exactly. The most information I could find was this message:
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
at Function.invokeGetter (<anonymous>:2:14)
at Object.error (http://localhost:8000/xxx/admin-user-search.html:1011:10)
at fire (http://localhost:8000/xxxx/webjars/jquery/3.1.1/jquery.js:3305:31)
at Object.fireWith [as rejectWith] (http://localhost:8000/xxxx/webjars/jquery/3.1.1/jquery.js:3435:7)
at done (http://localhost:8000/xxxx/webjars/jquery/3.1.1/jquery.js:9244:14)
at XMLHttpRequest.<anonymous> (http://localhost:8000/xxxx/webjars/jquery/3.1.1/jquery.js:9484:9)
Javascript:
function submitCreateNewRole(){
isBlank = false;
var myData;
newRoleName = $('#modalUserRoleSearchText').val();
newRoleDescription = $('#modelUserRoleDescText').val();
if (newRoleName=='' || newRoleDescription==''){
isBlank = true;
}
if (isBlank){
appAPI.setErrorBannerRole("Blank data is not allowed. Please enter non-blank data to create new Role.");
} else {
var UserRoleSearchModel = {};
var userRoleAction = "createNewUserRole" ;
RoleModel.ldapName = newRoleName;
RoleModel.roleDesc = newRoleDescription;
var token = $("meta[name='_csrf']").attr("content");
var URL = "json/admin-user-search?userRoleAction=" + userRoleAction + "&roleName=" + newRoleName + "&roleDesc=" + newRoleDescription;
var req = JSON.stringify(RoleModel);
var jqxhr = $.ajax({
type: "PUT",
url: URL,
headers: { "X-CSRF-TOKEN" : token },
data: req,
contentType: "application/json",
error: function (xhr, status, error) {
console.log("Failure caught");
console.log(xhr.responseText);
},
success: function(data){
myData = data;
}
}).done(function( msg ) {
$('#alertMessageSuccess').val('Successfully create new row');
}).fail(function(jqxhr) {
$('#alertMessageError').val('failed to create role' + newRoleName);
});
}
return myData;
}
Java Spring:
#RequestMapping(value = {
"admin-user-search"
}, method = RequestMethod.PUT)
public ModelAndView createNewUserRole(#AuthenticationPrincipal Principal principal,
#RequestParam(required = false) String pageCommand,
#ModelAttribute("UserModel") UserModel userSearch,
#ModelAttribute("RoleModel") RoleModel userRoleSearch,
#RequestParam(value = "roleName", required = false) String roleName,
#RequestParam(value = "roleDesc", required = false) String roleDesc,
#RequestParam(value = "userRoleAction", required = false) String userRoleCommmand, HttpServletRequest request) {
Results results = null;
List<Role> roleVOs = null;
String roleResponseMessage;
ModelAndView rView = new ModelAndView("admin-user-search");
if ("createNewUserRole".equals(userRoleCommmand)) {
userRoleSearch.clearAlertMessages();
userSearch.clearAlertMessage();
if ("".equals(roleName)) {
roleResponseMessage = "Unable to create a new role due to invalid or blank LDAP username enterred. Please try again with valid LDAP username.";
userRoleSearch.setErrorMessages(roleResponseMessage);
} else if ("".equals(roleDesc)) {
roleResponseMessage = "Unable to create a new role due to invalid or blank Role Description entered.";
userRoleSearch.setErrorMessages(roleResponseMessage);
} else {
try {
this.tdmcRoleDao.addNewRole(roleName, roleDesc);
roleResponseMessage = String.format("New user role '%s' has been added.", userRoleSearch.getLdapDn());
userRoleSearch.setSuccessMessages(roleResponseMessage);
userSearch.setSuccessMessages(roleResponseMessage);
roleVOs = retrieveAllRoles();
} catch (final SQLException e) {
LOGGER.error(e, TDMCMessages.TDMC_0142_DATABASE_INSERT_EXCEPTION, "tdmcRoleDao.addNewRole(newRoleLdap)");
roleResponseMessage = "Unable to create a new role -'%s' due to DB problem. Please retry with a new valid role name.";
userRoleSearch.setErrorMessages(roleResponseMessage);
userSearch.setErrorMessages(roleResponseMessage);
} catch (final DuplicateKeyException dupEx) {
roleResponseMessage = "Unable to create a duplicate role'. Please retry with non-duplicated role name.";
userRoleSearch.setErrorMessages(roleResponseMessage);
userSearch.setErrorMessages(roleResponseMessage);
}
if (roleVOs != null && !roleVOs.isEmpty()) {
results = populateRolesToResults(roleVOs);
}
userRoleSearch.setResults(results);
userRoleSearch.setSelected(roleVOs);
rView.addObject("RoleModel", userRoleSearch);
}
}
return rView;
}
When I run the application and try to create a new Role, I see that the PUT call reaches the Java server and successfully returns the view. However, on the Web client side, it throws the 405 error, and it's not clear what exactly is failing. Any insight would be very helpful.
On another note, the application also makes POST and GET calls as well, but those seem to work fine, so I cannot understand why the PUT calls are failing in this case.
EDIT: Fix code
first of all your url seems to be wrong, please check.
and change to post mapping, then post through body, something
like #requesrbody

Getting rid of irrelevant console errors (404 (Not Found))

I have a simple program that is scraping a web site for some items. I am using Angular $http service to call the below C# method to get the markup from the page and then handling everything else with JS. Everything is working perfectly fine with the exception of a minor annoyance: a bunch of 404 errors.
The 404 errors are being displayed in the developer tools once the http get call completes. It's almost like the javascript is trying to interpret the HTML and then fails on all the get requests for the images in the browser:
What I'm trying to figure out is how to get the 404 errors to go away or fail silently (not display in the console). I'm not finding anything in my research but am assuming there is some way to handle this whether it be on the server or client side
C#
public static string GetPageSource()
{
JObject result = new JObject();
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://awebpage.html");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
result["data"] = reader.ReadToEnd();
result["success"] = true;
reader.Close();
response.Close();
}
catch (Exception ex)
{
result["data"] = ex.Message;
result["success"] = false;
}
return JsonConvert.SerializeObject(result);
}
JS
$scope.getPageSource = function () {
var ajaxProcessor = Utils.ajaxMessage('Scraping Beer Menu From Source');
ajaxProcessor.start();
$http({
method: 'POST',
url: 'AJAX/MenuHandler.aspx/GetPageSource',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: {}
}).then(function (response) {
ajaxProcessor.stop();
var result = $.parseJSON(response.data.d);
if (result.success === false) {
Utils.showMessage('error', result.data);
} else {
var beerMenu = new BeerMenu(result.data, $scope.loggedInUser, function (beerMenu) {
$scope.buildDisplayMenu(beerMenu);
});
}
}, function (err) {
ajaxProcessor.stop();
console.log(err);
Utils.showMessage('error', err.data.Message);
});
};
UPDATE
Thanks to #dandavis, my issue is narrowed down to calling $.parseHTML within the buildDisplayMenu function (which calls buildCurrentMenu). Is there anyway to make it ignore the images or any get request?
buildCurrentMenu: function () {
var html = $.parseHTML(this.pageSource);
var menuDiv = $(html).find('.TabbedPanelsContent')[0];
var categories = $(menuDiv).find('h2');
var categegoryItems = [];
var beerArray = [];
for (var i = 0; i < categories.length; i++) {
...
}
return beerArray;
}
The resolution is to remove any img tags (or any other tag that should be ignored) from the page source before calling $.parseHTML
this.pageSource = this.pageSource.replace(/<img[^>]*>/g, "");

Can't connect to web service by JQuery

I use jquery (ajax) to connect to a web service which returns string , it is not working with me. it always go to error function. here is my web service :
[HttpGet]
[ActionName("GetImage")]
public string GetImage(string base64String, string imgName,string reqTitle , string reqSubject, string reqStatus,string Creator , DateTime creationdate )
{
try
{
using (PhMobAppEntities context = new PhMobAppEntities())
{
ClaimsApproval _ca = new ClaimsApproval();
_ca.imageBasestrg = base64String;
_ca.imageName = imgName;
_ca.Creator = Creator;
_ca.CreationTime = creationdate;
_ca.ReqStatus = reqStatus;
_ca.ReqTitle = reqTitle;
_ca.ReqSubject = reqSubject;
context.ClaimsApprovals.Add(_ca);
context.SaveChanges();
return "Success";
}
}
catch (DbEntityValidationException ex)
{
var errorMessages = ex.EntityValidationErrors
.SelectMany(x => x.ValidationErrors)
.Select(x => x.ErrorMessage);
var fullErrorMessage = string.Join("; ", errorMessages);
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}
and here is my js code :
$("#sendphoto").click(function () {
var url = "http://41.128.183.109:1212/api/Data/GetImage";
var data = {
imgName: "test"
};
$.ajax({
url: url,
type: 'Get',
data: data,
success: function (data) {
alert("Success");
},
error: function (data) {
alert("Please Check Your Internet Connection");
}
});
});
It is running ok when i tested my web service in advanced rest client ,please advice .
I tried connecting to your web service and I get the following response:
{"$id":"1","Message":"No HTTP resource was found that matches the request URI 'http://41.128.183.109:1212/api/Data/GetImage'."}
I think what you have is an internal problem with your c# code, probably with your routing. Your javascript call is probably working fine, but you are passing only one parameter, "test" while you have many more in your declaration.
What http response code are you getting?

Posting to Twitter through OAuthSimple.js

I've been stuck on this one for a while. I'm trying to use OAuthSimple.js to interact with Twitter in a Chrome extension I've written.
The signing process seems to work fine for requests to retrieve a user's statuses, but I can't seem to construct a request that will successfully authenticate when I try to retweet, reply, or mark a tweet as favorite.
I'm following the guides here. I have also tried numerous ways of structuring the request, and comparing the request contents against the output of the OAuth tool provided by Twitter ( which seems to check out ), but I'm still getting 401 errors and generic "We couldn't authenticate you" responses.
Here's how I'm trying to form the request:
var sendTwitterRequest = function(url, params, method, callback) {
var request = null;
if ( localStorage.twitterAuthToken ) {
OAuthSimple().reset();
request = OAuthSimple(TwitterConsumerKey,TwitterConsumerSecret).sign({
action:method,
method:"HMAC-SHA1",
dataType:"JSON",
path:url,
parameters:params,
signatures:{
oauth_version:'1.0',
oauth_token:localStorage.twitterAuthToken,
oauth_secret:localStorage.twitterAuthVerifier
}
});
console.log(request);
$j.ajax({
url:request.signed_url,
type:method,
data:request.parameters,
success:callback
});
}
};
Then, making calls into this method like this:
// this works, I get the data and can do stuff with it
sendTwitterRequest('http://api.twitter.com/1/statuses/user_timeline.json?user_id=',null,'GET',someMethod());
// this fails and throws a 401 error every time
sendTwitterRequest("https://api.twitter.com/1/statuses/retweet/"+tweetKey+".json",null,'POST',someOtherMethod());
Am I missing something? Thanks in advance!
It turns out the requests I am creating are fine, I just needed a final one to exchange request tokens for OAuth tokens. I thought this step was covered when the user was prompted for input, but turns out I was wrong.
I also ended up switching from OAuthSimple.js to just OAuth.js, on account of the fact that I could only get OAuth.js to process both the token requests and the timeline requests.
Some of this is pretty specific to what my application is doing, so you will probably need to modify it.
The new sendTwitterRequest method:
var sendTwitterRequest = function(options){
var accessor={
consumerSecret:TwitterConsumerSecret
};
var message={
action:options.url,
method:options.method||"GET",
parameters:[
["oauth_consumer_key",TwitterConsumerKey],
["oauth_signature_method","HMAC-SHA1"],
["oauth_version","1.0"]
]
};
if(options.token){
message.parameters.push(["oauth_token",options.token])
}
if(options.tokenSecret){
accessor.tokenSecret=options.tokenSecret
}
for(var a in options.parameters) {
message.parameters.push(options.parameters[a])
}
OAuth.setTimestampAndNonce(message);
OAuth.SignatureMethod.sign(message,accessor);
try {
$j.ajax({
url:message.action,
async:options.async||true,
type:message.method||'GET',
data:OAuth.getParameterMap(message.parameters),
dataType:options.format||'JSON',
success:function(data) {
if (options.success) {options.success(data);}
}
});
} catch ( e ) {
}
};
And the methods that depend on it:
// asks Twitter for an oauth request token. User authorizes and the request token is provided
requestTwitterToken = function() {
// this is semi-specific to what my extension is doing, your callback string may need
// to be slightly different.
var callbackString = window.top.location + "?t=" + Date.now();
var params = [
[ 'oauth_callback', callbackString ]
];
sendTwitterRequest({
url: "https://api.twitter.com/oauth/request_token",
method: 'POST',
parameters: params,
format: 'TEXT',
success: function(data) {
var returnedParams = getCallbackParams(data);
if ( returnedParams.oauth_token ) {
chrome.tabs.create({
url:"https://api.twitter.com/oauth/authorize?oauth_token=" + returnedParams.oauth_token
});
}
},error:function( e ) {
console.log( 'error' );
console.log( e );
}
});
};
// exchanges the Twitter request token for an actual access token.
signIntoTwitter = function(token, secret, callback) {
var auth_url = "https://api.twitter.com/oauth/access_token";
var authCallback = function(data) {
var tokens = getCallbackParams(data);
localStorage.twitterAuthToken = tokens.oauth_token || null;
localStorage.twitterAuthTokenSecret = tokens.oauth_token_secret || null;
callback();
};
try {
sendTwitterRequest({url:auth_url, method:'POST', async:true, format:'TEXT', token:token, tokenSecret:secret, success:authCallback});
} catch ( e ) {
console.log(e);
}
};
With this, the steps are as follows:
ask Twitter for a token ( requestTwitterToken() ) and provide a callback
in the callback, check to see if a token is provided. If so, it's an initial token
pass the token back to Twitter and open the Twitter auth page, which allows the user to grant access
in the callback to this call, see if an access token was provided
exchange the request token for an access token ( signIntoTwitter() )
After that, I simply use the sendTwitterRequest() method to hit Twitter's API to fetch the timeline and post Tweets.

Categories