400 Bad Request with a $http.post that does not contains a body - angularjs

I have an angular js application, and when trying to issue the following post request :
$resource('api/'+API_VERSION+'/projects/:projectId/users/:userId')
.save(
{
projectId:$scope.project.id,
userId:id
},
{}
,function(){
// Handle callback
});
I get a 400 bad request error.
the request is handled by a spring RestController and the method looks like the following :
#RequestMapping(value = "/projects/{projectId}/users/{userID}",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#RolesAllowed(AuthoritiesConstants.USER)
void addUsers(#PathVariable Long projectId, #PathVariable Long userId) {
log.debug("REST request to add admin to project");
projectService.addUser(projectId, userId);
}
I Checked the request that is been sent to the server, and nothing bad strikes me.
The url is correct (all parameter are of valid type), and the content type is set to Application json.
Thanks in advance for any help.

Your API consumes JSON and returns void, so I think you should have consumes = MediaType.APPLICAT_JSON_VALUE in your #RequestMapping.
[Edit]:
Apart from the consumes annotation everything is fine with your back-end. Can you try making your post request with the following code :
$resource('api/'+API_VERSION+'/projects/:projectId/users/:userId',
{projectId: $scope.project.id, userId: id}).$save();
or again, creating an instance of the resource :
var Project = $resource('api/'+API_VERSION+'/projects/:projectId/users/:userId',
{projectId: $scope.project.id, userId: id});
var newProject = new Project();
newProject.$save();
And let me know if it worked ?

Related

Debugging RestEasy RestClient

I am using the framework in quarkus to build a rest client for the mandrill API
#RegisterRestClient
#Path("1.0")
#Produces("application/json")
#Consumes("application/json")
public interface MailService {
#POST
#Path("/messages/send-template.json")
JsonObject ping(JsonObject mandrillInput);
}
This is the relevant portion of my application.properties
com.example.service.MailService/mp-rest/url=https:/mandrillapp.com/api
And my example resource
#Path("/hello")
public class ExampleResource {
#Inject
#RestClient
MailService mailService;
#Produces(MediaType.TEXT_PLAIN)
#GET
public String hello() {
System.out.print("In the API");
JsonObject key = Json.createObjectBuilder().add("key", "ABCD").build();
System.out.println("The json built is "+key);
JsonObject response = mailService.ping(key);
System.out.println("The response is " + response);
return "hello";
}
}
What I saw is that if the API I am calling (Mandrill in this case) returns an error response (If my key is wrong for example), then the variable I am using to store the response doesnt get the response. Instead the REST API I am exposing to my application wrapping around this, gets populated with the response from Mandrill.
Is this expected behaviour? How can I debug the output of a rest client implementation in Quarkus?
The REST API being called is https://mandrillapp.com/api/docs/users.JSON.html#method=ping2
If you want to be able to get the body of the response when an error occurs, I suggest you use javax.ws.rs.core.Response as the response type.
You could also go another route and handle exceptions using ExceptionMapper

Error 400 when POST'ing JSON in angularjs + Spark Single Page Application

I'm new to Single Page Application area and I try to develop app using angularjs and Spark framework. I get error 400 bad request when I want to post JSON from my website. Here is code fragment from client side:
app.controller('PostTripCtrl', function($scope, $http) {
$scope.newTrip = {};
$scope.submitForm = function() {
$http({
method : 'POST',
url : 'http://localhost:4567/trips/add',
data : $scope.newTrip,
headers : {
'Content-Type' : 'application/x-www-form-urlencoded'
}
}).success(function(data) {
console.log("ok");
}).error(function(data) {
console.log("error");
console.log($scope.newTrip);
});
};
});
Values that are to be assigned to newTrip are read from appropriate inputs in html file. Here is server-side fragment:
post("/trips/add", (req, res) -> {
String tripOwner = req.queryParams("tripOwner");
String startDate = req.queryParams("startDate");
String startingPlace = req.queryParams("startingPlace");
String tripDestination = req.queryParams("tripDestination");
int tripPrice = Integer.parseInt(req.queryParams("tripPrice"));
int maxNumberOfSeats = Integer.parseInt(req.queryParams("maxNumberOfSeats"));
int seatsAlreadyOccupied = Integer.parseInt(req.queryParams("seatsAlreadyOccupied"));
tripService.createTrip(tripOwner, startDate, startingPlace, tripDestination, tripPrice, maxNumberOfSeats,
seatsAlreadyOccupied);
res.status(201);
return null;
} , json());
At the end I obtain error 400 bad request. It is strange for me that when I want to see output on the console
System.out.println(req.queryParams());
I get json array of objects with values written by me on the website. However, when I want to see such output
System.out.println(req.queryParams("tripOwner"));
I get null. Does anyone have idea what is wrong here?
I think the main problem is that you are sending data to your Spark webservice with the 'Content-Type' : 'application/x-www-form-urlencoded' header. Try sending it as 'Content-Type' : 'application/json' instead, then in your Java code declare a String to receive req.body(), you'll see all your data in there.
Note: When you try to acces your data like this req.queryParams("tripOwner"); you're not accessing post data, but you're seeking for a get parameter called tripOwner, one that could be sent like this http://localhost:8080/trips/add?tripOwner=MyValue.
I would advise using postman to post a request to your server and see if it works. Try a different content type too. Try using curl and play with the various headers you are sending. 400 suggests the wrong data is being sent or expected data is missing or the data is the wrong type but based on your code you've provided I can see nothing wrong (but see below).
When your server receives a request log all request headers being received and see what changing them does. If it works in postman then you can change your client code to mirror the headers postman is using.
Does your spark server validate the data being sent before your controller code is hit? If so ensure you are adhering to all validation rules
Also on looking at your code again your client is sending the data in the post data but your server is expecting the data in the query string and not in the post data?
What happens if your server just sends a 201 response and does nothing else? Does your client get a 201 back? If so it suggests the hook up is working but there is something wrong with the code before you return a 201, build it up slowly to fix this.
Ok, I managed to cope with that using another approach. I used Jackson and ObjectMapper according to Spark documentantion. Thanks for your answers.
You can see more about that here: https://sparktutorials.github.io/2015/04/03/spark-lombok-jackson-reduce-boilerplate.html
You're probably just needed to enable CORS(Cross-origin resource sharing) in your Spark Server, which would have allowed you to access the REST resources outside the original domain of the request.
Spark.options("/*", (request,response)->{
String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
if (accessControlRequestHeaders != null) {
response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
}
String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
if(accessControlRequestMethod != null){
response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
}
return "OK";
});
Spark.before((request,response)->{
response.header("Access-Control-Allow-Origin", "*");
});
Read more about pre-flighted requests here.

Spring RESTful backend and Angularjs front end : How to add parameters from the client side?

I have a spring restful backend, one of the mapping is this:
#RequestMapping("/getPeopleList")
public List<Person> getPeeps(#RequestParam(value="ssid", required=true) Integer ssid){
return Backend.getPeople(ssid);
}
How do i make a request to this api? I am doing this with my angularjs
$http.get(url+'/getPeopleList').success(function(peeps) {
console.log(peeps);
});
but this doesnt work......
I also tried this
$http.get(url+'/getPeopleList/234').success(function(peeps) {
console.log(peeps);
});
The id 234 is a random id but that doesn;'t work either. I get 404 or 400 bad request everytime .
you are asking for #RequestParam but you are passing #PathVariable.
Please see their doc.
Just change to :
$http.get(url+'/getPeopleList?ssid=234').success(function(peeps) {
console.log(peeps);
});

WebAPI CORS not allowing Post requests

I have exhausted every resource I can find regarding Cors, and I still get the following error message when sending a request from Angular's $http service (via Chrome):
POST http://localhost:61459/api/LoginAuth/PostLoginByHandle 500 (Internal Server Error)
Get requests work just fine. I have found a hundred variations of similar instructions that seem to work for other people, but I just can't crack this one. I'll post my code.
WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
//jsonFormatter.SerializerSettings = new CamelCasePropertyNamesContractResolver();
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
}
}
From my understanding, this should be enough to allow any Cors request globally. In addition, I mark the controller with:
[EnableCors(origins: "*", headers: "*", methods: "*")]
I've tried modifying my web.config with various things I've found online, but I've read that it's not necessary when doing it programatically. Any idea what I'm doing wrong?
I've tagged this post as angularjs in case I'm doing something wrong there, as I'm very new to it. Here is my call:
$http.post("http://localhost:61459/api/LoginAuth/PostLoginByHandle",this.LoginRequest).success(function(data){
testCtrl.user = data;
console.log("Retrieved: " + data);
});
**Edit: I am able to hit the controller with Postman when I remove the top method. Any idea why these would conflict? Postman gives this error:
"Message": "An error has occurred.",
"ExceptionMessage": "Multiple actions were found that match the request: \r\nLoginByKey on type RecruitingTool.Web.Controllers.LoginAuthController\r\nPostLoginByHandle on type RecruitingTool.Web.Controllers.LoginAuthController"
Here is the controller code. I don't get why these would conflict:
[HttpPost]
public LoginResult LoginByKey(LoginRequest req)
{
LoginResult l = new LoginResult();
if (!string.IsNullOrEmpty(req.Key) &&
HttpContext.Current.Cache["credentials." + req.Username.ToUpper()].ToString() == req.Key)
{
l.Success = true;
}
else
{
l.Success = false;
l.ErrorMessage = "The credentials key is not valid.";
}
return l;
}
[HttpPost]
[EnableCors(origins: "*", headers: "*", methods: "POST")]
public LoginResult PostLoginByHandle(LoginRequest req)
{
LoginResult l = new LoginResult();
if (req.Username.ToUpper() == "TESTUSER" && req.Password == "test")
{
//change to authenticate against DB
l.Success = true;
l.CredentialsKey = Guid.NewGuid().ToString();
l.ErrorMessage = "";
HttpContext.Current.Cache["credentials." + req.Username.ToUpper()] = Guid.NewGuid().ToString();
}
else
{
l.Success = false;
l.ErrorMessage = "The username or password is not correct. Please check your information and try again.";
}
return l;
}
**Edit 2: The problem was a conflict between the two method's default routes. I'm not sure why that would be the case, but as soon as I specify an explicit route for both of them, it is resolved. I'm still interested to know the answer if anyone knows. Thanks all!
If you put a breakpoint in your Web API POST controller action is it not hitting it? HTTP 500 normally indicates some issue with your code (unhandled exception).
If it's not hitting that controller action it must be earlier in the pipeline. Have you tried just posting directly to your API method from something like POSTman? Very useful extension for Chrome..
1- Your method parameters are missing the [FromBody] attribute, so it should be like
public LoginResult PostLoginByHandle([FromBody]LoginRequest req)
2- Also, both methods have the exact same signature, in terms of the number of parameters and the data type of req parameters.
hope that helps.
WebAPI supports Convention based routing:
To find the action, Web API looks at the HTTP method, and then looks for an action whose name begins with that HTTP method name. For example, with a GET request, Web API looks for an action that starts with "Get...", such as "GetContact" or "GetAllContacts". This convention applies only to GET, POST, PUT, and DELETE methods. You can enable other HTTP methods by using attributes on your controller. We’ll see an example of that later.
Are you sure you don't have two methods in there, one called PostXXX and one called XXX? Or perhaps the one named POSTxxx is triggering convention-based routing. I've seen mention of "the conventions can result in conflicts in the routing table, matching incorrect actions."
Try renaming your methods to something other than Postxxx, Getxxx, ...
[PS Attribute-routing is much better]
I encountered same problem today. As it turns out the problem was in pre-flight OPTIONS request. I used this solution: Handling CORS Preflight requests to ASP.NET MVC actions
Additionally I added headers into response and removed Microsoft.AspNet.WebApi.Cors from project:
protected void Application_BeginRequest()
{
//CORS
if (Request.Headers.AllKeys.Contains("Origin"))
{
Response.Headers.Add("Access-Control-Allow-Origin", string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["CORS_ORIGIN"]) ? "*" : ConfigurationManager.AppSettings["CORS_ORIGIN"]);
Response.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, DELETE");
Response.Headers.Add("Access-Control-Allow-Headers", "Access-Control-Allow-Methods, Access-Control-Allow-Origin, Content-Type, Accept, X-Requested-With, Session");
//handle CORS pre-flight requests
if (Request.HttpMethod == "OPTIONS")
Response.Flush();
}
}

angularjs custom REST action and error handling

I'm having some trouble with error handling in a little angularjs application. I'm interacting with a Flask backend and a Postgres DB.
I have a factory service
appointServices.factory('Appointments', ['$resource', function($resource){
return $resource(someUrl, {}, {
query: { ... }
,
create: {
method: 'POST'
,url: 'http://somedomain:port/new/:name/:start/:end/:treatment'
,params: { start: '#start', end: '#end', name: '#name', treatment: '#treatment' }
,isArray:false
}
});
}
]);
Inside a controller I'm making the following call
Appointments.create($scope.appointment, function(value, responseHeaders) {
// success handler
console.debug('success: ', JSON.stringify(value));
}, function(httpResponse) {
// error handler
console.debug('error: ', JSON.stringify(httpResponse));
});
Here $scope.appointment contains the relevant parameters for the create action.
Now, in the backend I'm able to catch DB errors involving constraints and I'm trying to return an error code with a 'meaningful' message. So I have a python method
def create(name, start, end, treatment):
try:
...
transaction_status = 'ok'
code = 200
except IntegrityError as e:
...
transaction_status = 'IntegrityError'
code = 500
finally:
...
return make_response(transaction_status, code)
Everything works fine, I'm able to talk to the backend, create new data and insert this in the DB. As I said, any violation of the constraints is detected and the backend responds
curl -X POST "http://somedomain:port/new/foo/bar/baz/qux" -v
...
< HTTP/1.0 500 INTERNAL SERVER ERROR
...
IntegrityError
So, the problem is, no matter whether the action create was successful or not, the intended error handler specified inside the controller is always fired. Moreover, I always end up with a status code 404 in the httpResponse. Firebug shows correctly the code 500 as above, though.
Anybody has any idea of why I'm getting this behavior?
Any suggestions on how to improve the error handling mechanism are also welcome.
Thx in advance.
P.S. Following the documentation on $resource I have also tried variations on the factory service call, e.g.
Appointments.create({}, $scope.appointment, successCallback, errorCallback);
Appointments.create($scope.appointment, {}, successCallback, errorCallback);
with the same results.
Update:
Forgot to mention the important fact that I'm interacting with the backend via CORS requests. The POST request in create above is having place with the OPTIONS method instead. As I mentioned everything is working correctly except for the error response.
Under further investigation, I tried to isolate the factory service, in case I did something wrong, and I also tried the approach shown in the credit card example ($resource docs), but with no positive result.
However, I came up with two workarounds. Firstly, I was able to create a plain JQuery POST request, as in the example shown in the docs. This time, the request is not replaced by OPTIONS and I got the error code correctly.
I also managed to connect to the backend with the low-level $http service as follows:
var urlBase = 'http://somedomain:port/new/:name/:start/:end/:treatment';
var url = urlBase.replace(/:name/g, $scope.appointment.name);
url = url.replace(/:start/g, $scope.appointment.start);
url = url.replace(/:end/g, $scope.appointment.end);
url = url.replace(/:treatment/g, $scope.appointment.treatment);
// force method to be POST
var futureResponse = $http({ method: 'POST', url: url });
futureResponse.success(function (data, status, headers, config) {
console.debug('success: ', JSON.stringify(data));
});
futureResponse.error(function (data, status, headers, config) {
console.group('Error');
console.debug(JSON.stringify(status));
console.debug(JSON.stringify(data));
console.groupEnd();
});
This time, as in the case of JQuery, the request is done effectively with POST and error codes are correctly received.
Notice also that I'm not calling $http.post but I set the method to POST as part of the object parameter to $http, otherwise the connection takes places with OPTIONS as before.
Still trying to figure out what is happening with $resource.

Resources