Parameter binding to a VO with #Form- RestEasy - JAX-Rs - java

I have a few variables as #PathParam. I want to put them in a Bean and accept all of them in one.
public void show( #PathParam("personId"> String personId,
#PathParam("addressId") String addressId
#Context HttpRequest request) {
// Code
}
Now I would like to put all of the parameters in a Bean/VO with #Form argument.
My class:
class RData {
private String personId;
private String addressId;
private InputStream requestBody;
#PathParam("personId")
public void setPersonId(String personId) {
this.personId = personId;
}
#PathParam("addressId")
public void setAddressId(String addressId) {
this.addressId = addressId;
}
// NOW HERE I NEED TO BIND HttpRequest Context object to request object in my VO.
// That is #Context param in the original method.
}
My method would change to:
public void show( #Form RData rData) {
// Code
}
My VO class above contains what I need to do.
So I need to map #Context HttpRequest request to HttpRequest instance variable in my VO.
How to do that? Because it does not have an attribute name like #PathParam.

You can inject #Context values into properties just like the form, path, and header parameters.
Example Resource Method:
#POST
#Path("/test/{personId}/{addressId}")
public void createUser(#Form MyForm form)
{
System.out.println(form.toString());
}
Example Form Class:
public class MyForm {
private String personId;
private String addressId;
private HttpRequest request;
public MyForm() {
}
#PathParam("personId")
public void setPersonId(String personId) {
this.personId = personId;
}
#PathParam("addressId")
public void setAddressId(String addressId) {
this.addressId = addressId;
}
public HttpRequest getRequest() {
return request;
}
#Context
public void setRequest(HttpRequest request) {
this.request = request;
}
#Override
public String toString() {
return String.format("MyForm: [personId: '%s', addressId: '%s', request: '%s']",
this.personId, this.addressId, this.request);
}
}
Url:
http://localhost:7016/v1/test/1/1
Output:
MyForm: [personId: '1', addressId: '1', request: 'org.jboss.resteasy.plugins.server.servlet.HttpServletInputMessage#15d694da']

I thought I would add an answer for those that are using pure JAX-RS not not RestEasy specifically. Faced with the same problem, and surprised that JAX-RS doesn't have out-of-box support for http Form binding to Java Objects, I created a Java API to marshal/unmarshal java objects to forms, and then used that to create a JAX-RS messagebody reader and writer.
https://github.com/exabrial/form-binding

Related

Why am I getting null values on the server side after sending a post request by Postman

I have created a RESTful webservice using Spring boot to add a record to H2 database but when I send data from postman to my handler method I get null values on the server side and on the response sent to the client side as well, could anybody help me?
Eclipse Snapshot
Postman Snapshot
My Controller Code:
#RestController
public class AlienController {
#Autowired
AlienRepo repo;
#RequestMapping("/")
public String home() {
return"home.jsp";
}
#PostMapping(path="/alien")
public Alien addAlien(Alien alien) {
System.out.println(alien);
repo.save(alien);
return alien;
}
My DAO Class:
#Entity
public class Alien {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int aid;
private String aname;
private String lang;
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid=aid;
}
public String getAname() {
return aname;
}
public void setAname(String aname) {
this.aname=aname;
}
public String getLang() {
return lang;
}
public void setLang(String lang) {
this.lang=lang;
}
#Override
public String toString() {
return "Alien Info: Aid=" + aid + ", Aname=" + aname + ", Lang=" +lang;
}
}
My AlienRepository code:
public interface AlienRepo extends JpaRepository<Alien, Integer>{
}
You should use the annotation #RequestBody
public Alien addAlien(#RequestBody Alien alien)
You should inform Spring that you are waiting for a HttpRequest that contains a Body , Spring will automatically deserialize the inbound HttpRequest body onto Java object

Java RESTful returning Object inside of an object, takes it's toString instead of converting to JSON

(NOTE: I am sorry if the layout of this post isn't the best, I've
spent quite a lot of time figuring the features of this editor)
Hi, I am doing a RESTful web project and I run into a problem returning an object that contains another object (But the object inside is literally an "Object").
In my case I have a Company, Customer and Coupon resources. Each one of then contains fields, #XMLRootElement annotation in the class level, an empty constructor (along with constructors that receives the arguments) and of course, the getters and setters.
As for the service, there are annotations in the class level:
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
And the get method it's self:
#GET
#Path("/myCompany)
public Message getMyCompany(){
Message message;
try{
message = new MessageSuccess(company);
} catch(Exception e){
message = new MessageError(e.getMessage());
}
return message;
}
Now the way Message object is built, it's an abstract class (that contains the #XMLRootElement as well) it has three fields:
messageType (enum)
value (Object)
message (String)
it has all the features of the resource (getters and setters, construction, etc...)
And there are two classes that extending the Message.
they aswell have an empty constructor and parameterized one, they don't have the #XMLRootElement annotations.
Now the problem is, when ever the client does the get method, it receives a JSON object that has
messageType: 'SUCCESS'
value: 'com.publicCodes.resources.Company#6c4sad546d'
Basically it returns a toString() of the Company object.
I have no clue how to fix that.
Returning servlet's Response object is not an option due to a bad practice.
Returning the Company object it's self is as well not an option.
Thanks and waiting for your solutions!
**
EDIT for those who wanna see the actual code:
**
Here is the Message abstract class:
package com.publicCouponRest.util;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlRootElement
public abstract class Message {
private MessageResultType messageType;
private Object value;
private String message;
public Message() {
}
public Message(MessageResultType messageType, String message) {
this.messageType = messageType;
this.message = message;
}
public Message(MessageResultType messageType, Object value) {
this.messageType = messageType;
this.value = value;
}
public MessageResultType getMessageType() {
return messageType;
}
public void setMessageType(MessageResultType messageType) {
this.messageType = messageType;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
And here is MessageSuccess that extends Message:
package com.publicCouponRest.util;
public class MessageSuccess extends Message {
public MessageSuccess() {
}
public MessageSuccess(Object value) {
super(MessageResultType.SUCCESS, value);
}
}
and of course Company resource:
package com.publicCodes.resources;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
import com.publicCouponRest.services.AttributeKeys;
#XmlRootElement
public class Company {
private long id;
private String compName;
private String password;
private String email;
private Map<Long, Coupon> coupons;
private CompanyStatus companyStatus;
private AttributeKeys userType = AttributeKeys.COMPANY;
public Company(long id, String compName, String password, String email, Map<Long, Coupon> coupons, CompanyStatus companyStatus) {
this(compName, password, email);
this.id = id;
this.coupons = coupons;
this.companyStatus = companyStatus;
}
public Company(String compName, String password, String email) {
super();
this.compName = compName;
this.password = password;
this.email = email;
}
public Company() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCompName() {
return compName;
}
public void setCompName(String compName) {
this.compName = compName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Map<Long, Coupon> getCoupons() {
return coupons;
}
public CompanyStatus getCompanyStatus() {
return companyStatus;
}
public void setCompanyStatus(CompanyStatus companyStatus) {
this.companyStatus = companyStatus;
}
public void setCoupons(Map<Long, Coupon> coupons) {
this.coupons = coupons;
}
public AttributeKeys getUserType() {
return userType;
}
public void setUserType(AttributeKeys userType) {
this.userType = userType;
}
}
Ok. I think that you are having too much fun with jackson:
You are trying to put 'whatever object' in a node. aren't you?
To do that you must use the annotation:
#XmlAnyElement(lax=false)
so something like:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public abstract class Message {
....
#XmlAnyElement(lax=false)
private Object value;
....
Should be necessary. This way you will be able to put whatever incoming XML data node in that Object (JAXB will have to know the class of that Object and that class must be annotated, but it let you manage an undetermined class)
Also (EDITED):
In the other way: Object-> XML: The problem now is that you are sending to JAXB your 'Company' object but it only sees an 'Object' because you are telling it that it's an object of type 'Object', and JAXB only know how to serialize an 'Object.class' calling to it's .toString() because Object.class hasn't got any JAXB annotation. Try returning, instead of the object, the result of this method:
(Data will be your response and clazz Company.class or whatever)
import javax.xml.bind.JAXBContext;
import javax.xml.transform.dom.DOMResult;
import org.w3c.dom.Element;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
...
public static Element marshallToElement(Object data, Class clazz) {
DOMResult res = null;
try {
JAXBContext ctx = JAXBContextManager.getInstance(clazz.getPackage().getName());
Marshaller marshaller = ctx.createMarshaller();
res = new DOMResult();
marshaller.marshal(data, res);
} catch (JAXBException e) {
LOG.error(e);
}
return ((Document)res.getNode()).getDocumentElement();
}
This way you will return a JAXBElement, which is a 'bunch of nodes' that JAXB will know how to marshall.
At this point, if it works for you, it's a good practice caching the JAXBContext, it can be do saffely (JAXBContext is thread-safe, Marshallers NO) and it's a heavy duty for JAXB to execute that:
JAXBContextManager.getInstance(clazz.getPackage().getName())
So try to do it only once for each transformation.
PS:
Try putting JAXB annotations only in final classes, I'd had problems with that (because I was using annotations in an annotated subclass... And finally is cleaner to have all annotations in the same class)
Jersey/JAX-RS 2 client
I consider you read a bit of WebTarget API, how it works and what it returns. And Also return a Response Object
And then you can change your method to this:
#GET
#Path("/myCompany)
public Response getMyCompany() {
Message message;
try {
message = new MessageSuccess(company);
return Response.status(200).entity(message).build();
} catch (Exception e) {
message = new MessageError(e.getMessage());
return Response.status(500).entity(message).build();
}
}
After that you should add this to your main method:
WebTarget target = client.target(BASE).path("myCompany");
Response response = target.request().accept(...).post(Entity.json(message));//modify this line to suit your needs
Message message = response.readEntity(Message.class);
Have tried similar thing before and I got my help from #peeskillet's answer on this stackoverflow page.
Hope it did be of Help,thank you.

Java Spring REST API Status 400 response on POST / PUT

I built a REST API Service using Java Spring Cloud / Boot. Firstly, I made a simple class connected to a MongoDB and a controller with service that should allow me to add, delete, update and get all the objects. When using POSTMAN these all work, however when I want to add or update an object using redux and fetch API I get a status 400 and "bad request" error. This seems to have something to do with the JSON I'm sending in the body but it is the exact same format of JSON that is working with for example POSTMAN.
My action in Redux. For simplicity / test purposes I added an object at the top in stead of using the object being sent from the page.
var assetObject = {
"vendor" : "why dis no work?",
"name": "wtf",
"version": "231",
"category" : "qsd",
"technology" : "whatever"
}
export function addAsset(access_token, asset) {
return dispatch => {
fetch(constants.SERVER_ADDRESS + '/as/asset/add',
{
method: 'POST',
credentials: 'include',
headers: {
'Authorization': 'Bearer' + access_token,
'Content-Type': 'application/json'
},
body: assetObject
})
.then(res => dispatch({
type: constants.ADD_ASSET,
asset
}))
}
}
Controller code in Java Spring:
#RequestMapping(method = RequestMethod.POST, path = "/add")
public void addAsset(#RequestBody Asset asset) {
assetService.addAsset(asset);
}
Status ok while doing it in postman:
The error I get when using Redux / Fetch API (I only removed the directory structure because it has company name in it):
Have been stuck on this for a while, any help is much appreciated!
EDIT Asset Object:
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "assets")
public class Asset {
#Id
private String id;
private String vendor;
private String name;
private String version;
private String category;
private String technology;
public Asset() {
}
public Asset(String id,
String vendor,
String name,
String version,
String category,
String technology) {
this.id = id;
this.vendor = vendor;
this.name = name;
this.version = version;
this.category = category;
this.technology = technology;
}
public String getId() {
return id;
}
public String getVendor() {
return vendor;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
public String getCategory() {
return category;
}
public String getTechnology() {
return technology;
}
public void setId(String id) {
this.id = id;
}
public void setVendor(String vendor) {
this.vendor = vendor;
}
public void setName(String name) {
this.name = name;
}
public void setVersion(String version) {
this.version = version;
}
public void setCategory(String category) {
this.category = category;
}
public void setTechnology(String technology) {
this.technology = technology;
}
}
your error message says :
; required request body is missing
i think the error happens when your controller method
trying to form an object from the incoming request.
when you are sending the request you have to set each and every field related to the object.
if you are planning on not setting a property you should mark that field with #JsonIgnore annotation.
you can use #JsonIgnore annotation on the variable which will ignore this property
when forming the object as well as when outputing the object.
use #JsonIgnore annotation on the setter method , which i think you should do now since
you are ignoring the id property when making the request.
#JsonIgnore
public void setId(String id) {
this.id = id;
}
and you can return httpstatus code from the controller method,
so that client knows request was successful
#ResponseBody
public ResponseEntity<String> addAsset(#RequestBody Asset asset) {
return new ResponseEntity<String>("your response here", HttpStatus.OK);
}

Spring: RequestBody marshalling on POST of data specifying xml namespace

I am having a problem marshalling a RequestBody when the parent class has a namespace.
Class:
#XmlRootElement(name = "blah")
public class Test {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
XML:
<blah>
<id>23333</id>
</blah>
Code:
#RequestMapping( value = "/blah", method = RequestMethod.POST, consumes = { MediaType.TEXT_XML_VALUE }, produces = { MediaType.TEXT_XML_VALUE})
public String getBlah( #RequestBody Test request ) throws Exception
{
assert(null != request.getId());
return "blah";
}
This works fine. However, if I use #XmlRootElement(name = "blah", namespace="home") on the class, and <blah xmlns="home"> in the request, the Test class constructs, but it's ID value is never set.
I'm at a loss.
Before public void setId method add annotation #XmlElement

JAX-RS/Jersey: java.lang.ClassCastException - cannot be cast to javax.xml.bind.JAXBElement

I have application separated to frontend and backend modules which communicate through restfull webservice. Unfortunately, something goes wrong in this code and I get from Backend part:
java.lang.ClassCastException: com.rrd.ecomdd.data.SharedFile cannot be cast to javax.xml.bind.JAXBElement
Frontend snippet:
#Override
public void share(Set<SharedFile> fileSet) {
apiTarget.path(ApiConstant.FILESERVICE)
.path(ApiConstant.FILESERVICE_SHARE)
.request(MediaType.APPLICATION_JSON_TYPE.withCharset("UTF-8"))
.post(Entity.entity(fileSet.toArray(new SharedFile[0]), MediaType.APPLICATION_JSON_TYPE.withCharset("UTF-8")), new GenericType<Set<SharedFile>>() {
});
}
Backend snippet
#POST
#Path(ApiConstant.FILESERVICE_SHARE)
#Produces("application/json; charset=UTF-8")
#Consumes("application/json; charset=UTF-8")
public List<SharedFile> share(SharedFile[] sharedList) {
for (SharedFile s : sharedList) {
fileService.share(s);
}
return Arrays.asList(sharedList);
}
SharedFile class:
public class SharedFile {
private Long id;
private User user;
private ManagedFile file;
private UUID uuid = UUID.randomUUID();
public SharedFile(User user, ManagedFile file) {
this.user = user;
this.file = file;
}
public SharedFile() {
}
//getters, setters, equals and hashcode below
}
Any ideas how to fix this?
Try to annotate the class and its attributes as mentioned here:
#XmlRootElement
public class SharedFile {
#XmlElement
private Long id;
#XmlElement
private User user;
#XmlElement
private ManagedFile file;
Follow this for more: http://docs.oracle.com/javaee/6/tutorial/doc/gkknj.html

Categories

Resources