I need to have my Java server receive a PUT request to create a new user from an id and a json body, the URI needs to be like:
/usermanagement/user/$id { "name":john, "type":admin }
Given that I've made a simple Java class and can later convert the JSON to a POJO using Jackson, here's my problem:
How do I specify the PUT request to accept both the id and the JSON body as parameters? So far I've got:
#PUT
#Path("{id}")
#Consumes(MediaType.APPLICATION_JSON)
public String createUser(#PathParam("id") int id){
User user = new User();
User.setId(id);
return SUCCESS_MSG;
}
And this works, but I've had no luck adding the JSON body and having the function parse it. I've tried:
public String createUser(#PathParam("id") int id, String body){
return body;
}
It should return the same input JSON when testing in Postman, however it always returns a "resource not available" error.
I feel there's something obvious that I'm missing here?
As per REST API conventions, a POST method on a uri like /usermanagement/users is what is needed. PUT method is used for updating an existing resource. You can go through this wonderful article on how to design pragmatic RESTful API. http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api.
If you are trying to create a new user, why give it an ID? You have to POST the data such as user name, lastname, email, ... and let the backend generate an ID (like an auto-incremented id, or some UUUID) for this new resource.
For example, in my app, I use a json body for a POST request like below:
{
"loginId": "ravi.sharma",
"firstName": "Ravi",
"lastName": "Sharma",
"email": "myemail#email.com",
"contactNo": "919100000001",
"..." : ".."
}
Moreover, your response should return HTTP-201, after successful creation, and it should contain a location header, pointing to the newly created resource.
Instead of Using String body, use a Class with Member variables name and Type Like this.
public class User {
private String name;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
(This works in Spring Boot Web out-of-box. incase of Spring MVC, you might need to add Jackson dependency): On your Controller , Add #RequestBody Annotation, then Jackson will take care of the un-marshaling of JSON String to User Object.
public String createUser(#PathParam("id") int id, #RequestBody User user){
Related
I am completely new to Reactive Spring Webflux.
I am writing a handler for a Post request which should
Accept Json body(Employee id, name and role),
Fetch some more Employee details from database 1 using id field,
Return Employee json back with additional attributes like age and dept..
My router code is below:
#Bean
-- something like this
POST("/empl/create").and(contentType(APPLICATION_JSON)), emplHandler::getMoreEmplDetails
Post Body:
{
"id":"213"
"name": "John",
"role": "Manager"
}
Model Class looks like this
Public class Employee
{
Public string id;
Public string name;
Public string role;
Public string dept;
Public int age;
}
Handler Code is Below
public Mono<ServerResponse>getMoreEmplDetails(ServerRequest request){
Mono<Employee> np = request.bodyToMono(Employee.class);
np.subscribe(x-> System.out.println("Print Body"+x));
//This returns a MonoOnErrorResume
/* More code should follow here*/
Return Mono.empty()// THIS IS TEMPORARY. I WANT TO RETURN COMPLETE EMPL JSON WITH AGE AND DEPT HERE
};
Problem is I want to print body for debugging purpose but Keep getting MonoOnErrorResume as indicated in comments in handler code. How do I make sure that my Body was received fine by the handler method ??
I was able to use map to log the results.
Mono<Employee> np = request.bodyToMono(Employee.class);
np.map(s->system.out.println(s));
i am trying to send nested json object in POST request to my spring REST API.
Object java code
public class TestModel {
private String id;
private String name;
public TestModel(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
Post method code in rest controller
#RequestMapping(value = "/helloPost")
public ResponseEntity<TestModel> helloPost(#RequestBody TestModel t) {
return new ResponseEntity<TestModel>(t, HttpStatus.OK);
}
My postman screenshot
It has to return status 200 ok and object i sent, but it returns 400 bad request permanently.
Please, tell me what am i doing wrong. It was ok when i sent one string(my #RequestBody was string too) but completly not working with custom objects.
P.S
i have added comma, no changes
You missed the "," after the id field in JSON. proper JSON is your case would be below :-
{
"id" : "1",
"name" : "test"
}
It's a malformed json you are sending to the server. You need to add comma to separate elements in json.
Even postman showing wrong icon at the left.
{
"id" : 1,
"name" : "test"
}
Also you need to add setters and default constructor in object model to set those values.
As mentioned in the comment, please add the default constructor for TestModel class. It should resolve the problem.
As an additional step, if the web service is going to accept json as input, then add consumes annotation with content type as application json.
I'm implementing RESTful webservice by using Spring framework. There is a scenario where I need to accept the request where a field of request body will be a file(PDF or Image). My request body will look like :
{
"id" : "101",
"name" : "John",
"report" :
}
Here report field in above request body will be a file(PDF or Image).
I read many articles on this but unable to get the proper answer. I got the solution for accepting the file alone but not file as a field of request body
Please help me out
Okay, there are many approaches for this. I will explain one such approach.
Step 1: Create StudentReport class.
Notice the byte[] array used here.
public class StudentReport {
private long id;
private String Name;
byte[] report;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public byte[] getReport() {
return report;
}
public void setReport(byte[] report) {
this.report = report;
}
}
Step 2 : Create the required rest end point.
Here I wish to tell you few things.
The Json has to be send to end point as string and keep the report empty there. This will be passed seperatley like shown below.
#RestController
#RequestMapping("/NameYourController/")
public class MyController{
#RequestMapping(value = "addDetails", method = RequestMethod.POST , consumes = "multipart/form-data")
public StudentReport addProduct(#RequestParam String studentReportJson, #RequestParam MultipartFile report) throws JsonParseException, JsonMappingException, IOException {
//Convert your Json in Strign format as actual object
StudentReport studentReport = new ObjectMapper().readValue(studentReportJson, StudentReport.class);
//convert file to byte array
byte[] myReport = report.getBytes();
studentReport.setReport(myReport);
// Now, pdf is set in the object
// do whatever you want to do with it like save in database etc
//based on that , have some return type defined. I have just used returned the object in this example .
return studentReport; // Spring boot auto converts this object to Json when send to UI.Note that Json is an object and Json String is string object consisting of Json. We recieved Json String but returning Json object
}
//and other endpoints you want to define
}
That's it done. Note that for this you need to import the Jackson library. If you are using Maven, you can add this in your pom.xml.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
Now, how would you test your rest end point. One way will be from the UI code which will consume these.But then be sure of that code that it is correct. If you don't have UI code still you can do this. This is done by chrome app PostMan. But, for that you need to use chrome web browser.
Youtube have lots of videos on how to use PostMan. I will just give you a screen shot of how to use specifically in your case :
Notice these things in image -> POST, URL Path, BODY , form-data, TEXT , FILE. :)
Output will be displayed below in postman if you have some return data.(not shown in image)
JSON Doesn't support Binary Data. You must encode your File as a Base64 String and attach it.
{
"id" : "101",
"name" : "John",
"report" : "/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxyiiiv3E8wKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Z"
}
This above Base64 Encoding is for a Image file from here
You must use a #RequestParam or #RequestPart to send and process a file. It is not impossible to use #ResponseBody, but for that you have to write your custom converters.
public String handleFileUpload(#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
// Your File Processing goes here
}
Why cant we get a file data from a RequestBody in java?
When creating or listing and item using REST api I return also the whole resource path.
For example creating a person record returns http://service:9000/person/1234in response. In order to get schema, host & port part like http://service:9000, I extract it from URL obtained by HttpServletRequest.getRequestURL().
Example (not the production code but conceptually same):
#RequestMapping(value = "/person", method = RequestMethod.PUT)
public Object putPerson(
#RequestParam(value = "name") String name,
HttpServletRequest req) {
long id = createPerson(name);
String uriStart = RestUtils.getSchemeHostPortUrlPart(req
.getRequestURL().toString());
String uri = uriStart + "/person/" + id;
Person person = new Person(name, id, uri);
return person; //Serialized to json by Spring & Jackson
}
//Simple bean-like class
public class Person {
//Getter methods for uri, name & id
}
Since this is quite a boiler plate code which repeats in every method I was wondering if Spring does not have support for this which eluded me when reading it's documentation.
By this I mean accessing either URL without neededn HttpServletRequest or even better its schema, host, port part only.
The documentation provides a lot of examples for constructing URIs using a UriComponentsBuilder.
Furthermore I recommend to take a look at Spring HATEOAS if you want to take your REST API to the next level.
BTW: PUT means that you place what you send (request body) to the location to which you send it (URL). If nothing is there something new is created, otherwise what exists is updated (replaced).
This is not what is happening in your example. The proper way would be to either POST to /person or PUT to the person's own URL, e.g. /person/1234, provided you have the ID beforehand.
You can construct the URI in an interceptor (that's executed previous to controller methods) and put it as an attribute and use it in the controller method.
I believe it is quite simple. Look at this example:
#RequestMapping(value = "/person", method = RequestMethod.PUT)
public Object putPerson(#RequestParam(value = "name") String name, HttpServletRequest req) {
long id = createPerson(name);
Person person = new Person(id, name, req);
return person; //Serialized to json by Spring & Jackson
}
public class JsonResponse {
private String url;
public JsonResponse(HttpServletRequest request) {
url = request.getRequestURI() + "?" + request.getQueryString();
}
public final String url() {
return url;
}
}
public class User extends JsonResponse {
private Long id;
private String name;
public User(Long id, String name, HttpServletRequest request) {
super(request);
this.id = id;
this.name = name;
}
// Getter, Setter
}
You can use org.springframework.web.servlet.support.ServletUriComponentsBuilder like this:
String uri = ServletUriComponentsBuilder.fromCurrentRequest()
.replacePath("/person/{id}")
.buildAndExpand(id)
.toUriString();
I have a spring web service method where i want to get a string as a parameter. The string is sent in body of the request. My web service class is:
#Controller
#RequestMapping(value = "/users/{uid}/openchart")
public class OpenChartWebService {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public String saveABC(#PathVariable("uid") Long uid,
#RequestBody String myString) {
System.out.println("sent string is: "+myString);
return "something";
}
}
My request in body is :
{
"name":"Ramesh"
}
But this is not working. This shows "Bad Request" HTTP error(400). How to send a string in a body and how to get a string sent in a body inside webservice method?
As #Leon suggests, you should add the media type to your request mapping, but also make sure you have Jackson on your classpath. You'll also want to change your #RequestBody argument type to something that has a "name" property, rather than just a String so that you don't have to convert it after.
public class Person {
private name;
public getName() {
return name;
}
}
If your data object looked like the above, then you could set your #RequestBody argument to Person type.
If all you want is a String, then perhaps just pass the value of "name" in your request body rather than an object with a name property.