I've got Angular app and Java server.
I need to send POST request with JSON object consisting of string array and string field.
I'm using Angularjs $resource and Java javax.ws.rs.
My latest try as follows:
Client:
var messages = $resource('resources/messages/getmessages', {}, {
update: { method: 'POST', url: 'resources/messages/updatemessages' }
});
//...
var _args = { 'msgIdList': ['1', '2', '3'],
'action': 'makeSmth' };
return messages.update(_args).$promise.then(
function (data) {
//...
},
function (error) {
//...
}
)
Server:
#POST
#Path("updatemessages")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON +"; charset=UTF-8")
public Response updateMessages( #FormParam("msgIdList") List<String> msgIdList,
#DefaultValue("") #FormParam("action") String action,
#CookieParam("rgsid") String c_sid,
#Context HttpServletRequest httpservletreq) {
//...
}
The problem is that I've got 415 Unsupported Media Type error, and don't know what to do next. I've tried lots of things, but may be I was wrong from the start, and I can't pass parameters this way?
Any help would be appreciated, thanks!
you can try this in your angular, maybe it can help.
var sendPost = $http({
method: "post",
url:"JAVA_SERVER_SERVICE_URL",
data: {
msgIdList: 'your_value',
action: 'your_value'
},
headers: { 'Content-Type': 'application/json' }
});
So, eventually I made a wrapper class, so now it looks this way:
#XmlRootElement
private static class RequestWrapper {
#XmlElement
private ArrayList<String> msgIdList;
#XmlElement
private String action;
public ArrayList<String> getMsgIdList() {
return msgIdList;
}
public void setMsgIdList(ArrayList<String> msgIdList) {
this.msgIdList = msgIdList;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public RequestWrapper() {
}
}
#POST
#Path("updatemessages")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON +"; charset=UTF-8")
public Response updateMessages( RequestWrapper requestData,
#CookieParam("rgsid") String c_sid,
#Context HttpServletRequest httpservletreq) {
//...}
Angular part stays unchanged.
I'm not really sure, if this the right way to go (class description and so on), but it works.
Related
I have these mappings:
#GetMapping("rparam")
public void get(#RequestParam("test") String test) {
System.out.println(test);
}
#PostMapping("rparam")
public void post(#RequestParam("test") String test) {
System.out.println(test);
}
#PatchMapping("rparam")
public void patch(#RequestParam("test") String test) {
System.out.println(test);
}
#DeleteMapping("rparam")
public void delete(#RequestParam("test") String test) {
System.out.println(test);
}
#PutMapping("rparam")
public void put(#RequestParam("test") String test) {
System.out.println(test);
}
And apart from the GetMapping and PostMapping methods all the other ones fail with 400 Bad request because they can't find the test parameter.
edit:
How the requests are made (the post one works):
(stringifying the data and adding a contentType:application/json doesn't help either)
$.ajax({
url: "api/my-controller/rparam",
method: "POST",
data: {
test: "super test"
},
headers: {
"X-XSRF-TOKEN": cookie
}
}).done(function() {
console.log("super cool")
});
$.ajax({
url: "api/my-controller/rparam",
method: "PUT",
data: {
test: "super test"
},
headers: {
"X-XSRF-TOKEN": cookie
}
}).done(function() {
console.log("super cool")
});
edit: I just noticed that when I don't stringify the data and set a contentType it's sent as form data. And sending data via a form only supports post and get so maybe that has something to do with it?
Try giving some default value by changing your method:
public void delete(#RequestParam(value = "test" , defaultValue = "default") String test)
#RequestParam can work with with all kind of #RequestMapping:
#GetMapping
#PostMapping
#PutMapping
#DeleteMapping
#PatchMapping
However, about semantics, PATCH, PUT and DELETE are supposed to accept the body of the request, and then update / store / remove the object on the resource identified by the URI.
So, I recommand using #RequestBody instead of #RequestParam.
You can take a look at this very similar question.
From a js page (in angular) I call a REST request, GET method, were I would to pass an header, this is the function that I call from the REST request:
allstaffworking: function(_getstaff){
var currentToken = _GetToken();
var Headers = {
token: currentToken.stringtoken
};
console.log("idtoken"+Headers);
if (currentToken !== null) {
$http({
method : 'GET',
headers: Headers,
url : REST_URL+'staff/working'
}).then(function successCallback(response) {
_getstaff(response)
}, function errorCallback(response) {
console.log(response.statusText);
});
} else {
console.log("NON SEI LOGGATO!!!");
}
},
Whithout headers: Headers, it works, but I want to pass an important json string: {"idtokenStaff":11,"staffType":{"idstaffType":2,"type":"Dipendente"},"tokenStaff":"88d08m8ve4n8i71k796vajkd01"} in the Headers. I don't know How I can take this string in Jersey. This is java file in with I have the REST method:
#Path("/staff")
public class StaffController {
BaseDao sDao = new StaffDaoImpl();
StaffDao stfDao = new StaffDaoImpl();
TokenStaffDao tsDao = new TokenStaffDaoImpl();
TokenStaff ts = new TokenStaff();
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Staff> getStaff()
{
List<Staff> listOfStaff=sDao.getAll(Staff.class);
return listOfStaff;
}
#GET
#Path("/working")
#Produces(MediaType.APPLICATION_JSON)
#Consumes("application/json")
public List<Staff> getWStaff(#HeaderParam("token") String token) throws JSONException
{
JSONObject jsonObj = new JSONObject(token);
Boolean id = tsDao.getExistence(jsonObj.getInt("idtokenStaff"));
if (id){
List<Staff> listOfWStaff=stfDao.getAllW();
return listOfWStaff;
}
else
return null;
}
}
Taking header from: #HeaderParam("token") String token. How Can I take the element of the header?
A bit late to answer this, but you can also use #Context annotation to get httpheaders.
Eg.
public List<Staff> getWStaff(#Context HttpHeaders httpHeaders) {
String token = httpHeaders.getHeaderString("token");
JSONObject jsonObj = new JSONObject(token);
}
I have a WidgetDto that I have annotated with swagger UI annotations. The final response wraps a list of WidgetDtos with a layer of metadata (per page 21 of this RESTful best practices document). For example:
{
"data" : [
{
"id" : 1234,
"prop1" : "val1"
...
},
{
"id" : 5678,
"prop1" : "val2"
...
},
...
]
}
My java code looks like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
List<WidgetDto> widgets;
...
Map<String, Object> responseBody = new HashMap<>();
responseBody.put("data", widgets);
return Response.ok(responseBody).build();
}
I'd like to reuse this pattern on multiple resources, and I don't want to create list DTOs for every response type. Is there an elegant way to use swagger to document these types of response bodies?
Your metadata is not a part of your resource but it's a part of your resource's representation.
In my case, responses types are 'application/hal+json' and 'application/json', each of them use a different wrapper with different metadatas.
To solve this problem, I created an extern document to explain these two wrappers and for each of them, how a single resource and a list of resources are represented with metadata.
I think my choice is correct because I separate the resource of its representations (per page 7 'Manipulation of Resources Through Representations' of this RESTful best practices document)
In your case, you returns a list of WidgetDtos, the layer of metadata is a part of the representation of your resource.
However, you can use a generic class like Resource and Resources used by spring-hateoas :
public class Resources<T> implements Iterable<T> {
private final Collection<T> content;
Resources(Iterable<T> content) {
this.content = new ArrayList<T>();
for (T element : content) {
this.content.add(element);
}
}
}
And use it like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
List<WidgetDto> widgets;
...
return Response.ok(new Resources<WidgetDto>(widgets)).build();
}
I faced a similar problem a few months ago when I was developing a project for school. The solution is to create an envelope and always return it. The envelope will contain a feild "data" which is a generic; so you will be able to bind it to any data type.
Note that even though I used it I later on read that it should be used scarecly (I think your case is a good example of usage) but technically an Exception object should be thrown if the request failed.
Anyway this is my Response class which I used to return all my responses:
public class Response <AnyData> {
private static final String SUCCESS = "success";
private static final String FAILURE = "failure";
private String status;
private AnyData data;
private String error;
private Response(String status, AnyData data, String error) {
this.status = status;
this.data = data;
this.error = error;;
}
private Response(String status, AnyData data) {
this(status, data,"");
}
private Response(String status, String error) {
this(status, null, error);
}
public static <AnyData> Response<AnyData> success(AnyData data) {
return new Response<AnyData>(SUCCESS, data);
}
public static <AnyData> Response<AnyData> failure(String error) {
return new Response<AnyData>(FAILURE, error);
}
public static <AnyData> Response<AnyData> unimplemented() {
return new Response<AnyData>(FAILURE, "Missing implementation in the backend.");
}
public static <AnyData> Response<AnyData> failureUserNotFound() {
return Response.failure("User not found!");
}
public static <AnyData> Response<AnyData> failureBusinessNotFound() {
return Response.failure("Business not found!");
}
// Removed getters and setters for simplicity.
}
After this is set we will just create the responses right from the Comtroller. I changed it a bit to make it work with the sample is should be legible enough. Note that I have static methods for my responses: 'success()', 'error()'...
#RestController
#Api(tags={"Widgets"})
public class WidgetController {
#RequestMapping(value="/api/widgets", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get all widgets.")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response<List<WidgetDto>> getWidgets() {
List<WidgetDto> widgets = new LinkedList<>();
widgets.add(new WidgetDto(1234, "val1"));
widgets.add(new WidgetDto(5678, "val2"));
return Response.success(widgets);
}
}
And here is a sample of the response body:
Hope this helps.
You can define the responseContainer attribute in the #ApiOperation annotation.
The value List will wrap your WidgetDto in a container.
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class,
responseContainer = "List"
)
I ma using Spring MVC and trying to use jQuery. I have this on my web page:
$(document).ready(function () {
var entity = {mag: "status_key", paper: "View10"};
$("#btn").click(function () {
$.ajax({
url: "ajaxJsonPost",
type: 'post',
dataType: 'json',
data: JSON.stringify(entity),
contentType: 'application/json',
});
});
});
Spring server has this:
#RequestMapping(value = "ajaxJsonPost", method = RequestMethod.POST)
public void postJson(#RequestBody Entity en) throws IOException {
System.out.println("writing entity: " + en.toString());
}
OK, Entity cames to server. BUT browser console prints 404 not found. I know that my POST request needs any response. In the Internet I've found solution which recommends me to return ResponseEntity object, OR use annotation #ResponseStatus. They both return HttpStatus well, but I don't know in which cases I should use them. What is the best way?
#Controller
#RequestMapping("/apipath")
public class SomeController {
#RequestMapping(value = "/ajaxJsonPost", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String postJson(#RequestBody final Entity en) {
System.out.println(en.toString());
//assuming you have a class "EntityService" and
//it has a method postData
//which takes Entity object as parameter and pushes into database.
EntityService.postData(en);
System.out.println("added");
return "success";
}
}
Entity object on the Server side
#JsonAutoDetect
public class Entity {
private String mag;
private String paper;
public String getMag() {
return mag;
}
public void setMag(final String mag) {
this.mag = mag;
}
public String getPaper() {
return paper;
}
public void setPaper(final String paper)
this.paper = paper;
}
}
ajax
$(document).ready(function () {
var entity = {mag: "status_key", paper: "View10"};
$("#btn").click(function () {
$.ajax({
url: "/apipath/ajaxJsonPost",
type: 'post',
dataType: 'json',
data: JSON.stringify(entity),
contentType: 'application/json',
success : function(response) {
alert(response);
},
error : function() {
alert('error');
}
});
});
});
And as far as why and when to use #ResponseStatus and #ResponseEntity, there is already a short and simple answer here by #Sotirios Delimanolis. When use #ResponseEntity .
It says :
ResponseEntity is meant to represent the entire HTTP response. You can
control anything that goes into it: status code, headers, and body.
#ResponseBody is a marker for the HTTP response body and
#ResponseStatus declares the status code of the HTTP response.
#ResponseStatus isn't very flexible. It marks the entire method so you
have to be sure that your handler method will always behave the same
way. And you still can't set the headers. You'd need the
HttpServletResponse or a HttpHeaders parameter.
Basically, ResponseEntity lets you do more.
Java - spring controller - Angularjs form submission
Problem is with "#RequestBody OfferForm data" when i submit form i get error "404 bad request" but when i replace OfferForm bean with String object it works fine and display form data in json format.
any help appreciated.
Following is my angularjs function code
$scope.submitOffer = function() {
alert('submitOffer')
$http({method: 'POST', url: '/offer/submitOffer', data: $scope.formData}).
success(function(data, status, headers, config) {
$scope.successMsg = data;
}).
error(function(data, status, headers, config) {
if(status == 400) {
$scope.errMessages = data;
} else {
alert('Unexpected server error.');
}
});
};
Following is my controller code
#RequestMapping(value="offer")
public class OfferController{
#RequestMapping(value = "/submitOffer", method = RequestMethod.POST)
#ResponseBody public ResponseEntity<?> postForm(#RequestBody OfferForm data) {
System.out.println(data);
return new ResponseEntity<String>("Offer Created", HttpStatus.OK);
}
}
Following is my java bean
public class OfferForm {
private String offerType;
private String offerTitle;
public String getOfferType() {
return offerType;
}
public void setOfferType(String offerType) {
this.offerType = offerType;
}
public String getOfferTitle() {
return offerTitle;
}
public void setOfferTitle(String offerTitle) {
this.offerTitle = offerTitle;
}
}
Your $scope.formData object has more property than expected by the webservice.
You should provide to your webservice an object which has at most two properties of type string: offerType and offerTitle.
I think you currently have more properties than expected or you don't have the correct type, thus the bad request exception.
You could do something like that in your javascript, given that the two properties are a string:
$scope.submitOffer = function () {
$http({
method: 'POST',
url: '/offer/submitOffer',
data: {
offerType: $scope.formData.offerType,
offerTitle: $scope.formData.offerTitle
}
}).
success(function (data, status, headers, config) {
$scope.successMsg = data;
}).
error(function (data, status, headers, config) {
if (status == 400) {
$scope.errMessages = data;
} else {
alert('Unexpected server error.');
}
});
};