Плагин Struts2 REST: отправка объекта JSON через PUT
Я пишу сервис RESTful с Struts2 и плагином Struts2 REST. В настоящее время мой сервис может обрабатывать запросы GET без проблем, но я застрял, пытаясь заставить его работать запрос "update" (PUT).
У меня есть две возможные модели, List для show() и объект ClientFeature для update(), где ClientFeature является классом pojo.
Контроллер REST:
public class ClientfeatureController extends ControllerParent implements ModelDriven<Object> {
private ClientFeature clientFeature = new ClientFeature();
private List<ClientFeature> clientFeatureList;
//Client ID
private String id;
public ClientfeatureController() {
super(ClientfeatureController.class);
}
@Override
public Object getModel() {
return (clientFeatureList != null ? clientFeatureList : clientFeature);
}
/**
* @return clientFeatureList through Struts2 model-driven design
*/
public HttpHeaders show() {
-logic for GET request here..-
//todo: add ETag and lastModified information for client caching purposes
return new DefaultHttpHeaders("show").disableCaching();
}
// PUT request: /clientfeature/update/<id> + JSON data
public String update() {
logger.info("client id: " + id);
logger.info("updated model test:" + clientFeature.getClientId());
return "update";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<ClientFeature> getClientFeatureList() {
return clientFeatureList;
}
public void setClientFeatureList(List<ClientFeature> clientFeatureList) {
this.clientFeatureList = clientFeatureList;
}
}
ClientFeature:
public class ClientFeature {
private Long clientId;
private Feature feature;
private ArrayList<String> countries;
public ClientFeature() {
this.countries = new ArrayList<String>();
}
public Long getClientId() {
return clientId;
}
public void setClientId(Long clientId) {
this.clientId = clientId;
}
public Feature getFeature() {
return feature;
}
public void setFeature(Feature feature) {
this.feature = feature;
}
public ArrayList<String> getCountries() {
return countries;
}
public void setCountries(ArrayList<String> countries) {
this.countries = countries;
}
}
И я тестирую сервис, используя расширение Postman для Chrome и отправляя данные JSON, что-то вроде:
{
"clientFeature": {
"feature" : {"featureId" : 999, "featureName" : "testFeature"}
"countries": ["CA","US"]
"clientId" : 001
}
}
И ошибка:
356572 [http-bio-8080-exec-6] WARN net.sf.json.JSONObject - Tried to assign property clientFeature:java.lang.Object to bean of class com.foo.bar.ClientFeature
Я довольно плохо знаком со всем, что связано, поэтому любая помощь будет очень признательна.
РЕДАКТИРОВАТЬ:
Попытался отправить следующий JSON:
{"com.foo.entity.clientFeature": {"clientId": 10}}
И получил следующую полную ошибку:
1016894 [http-bio-8080-exec-3] ERROR freemarker.runtime - Method public java.lang.String org.apache.commons.lang.exception.NestableRuntimeException.getMessage(int) threw an exception when invoked on net.sf.json.JSONException: Error while setting property=com.foo.entity.clientFeature type class java.lang.Object
Method public java.lang.String org.apache.commons.lang.exception.NestableRuntimeException.getMessage(int) threw an exception when invoked on net.sf.json.JSONException: Error while setting property=com.foo.entity.clientFeature type class java.lang.Object
The problematic instruction:
----------
==> ${msg[0]} [on line 68, column 29 in org/apache/struts2/dispatcher/error.ftl]
----------
Java backtrace for programmers:
----------
freemarker.template.TemplateModelException: Method public java.lang.String org.apache.commons.lang.exception.NestableRuntimeException.getMessage(int) threw an exception when invoked on net.sf.json.JSONException: Error while setting property=com.foo.entity.clientFeature type class java.lang.Object
at freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:130)
at freemarker.ext.beans.SimpleMethodModel.get(SimpleMethodModel.java:138)
at freemarker.core.DynamicKeyName.dealWithNumericalKey(DynamicKeyName.java:111)
at freemarker.core.DynamicKeyName._getAsTemplateModel(DynamicKeyName.java:90)
at freemarker.core.Expression.getAsTemplateModel(Expression.java:89)
at freemarker.core.Expression.getStringValue(Expression.java:93)
at freemarker.core.DollarVariable.accept(DollarVariable.java:76)
at freemarker.core.Environment.visit(Environment.java:210)
at freemarker.core.MixedContent.accept(MixedContent.java:92)
at freemarker.core.Environment.visit(Environment.java:210)
at freemarker.core.IfBlock.accept(IfBlock.java:82)
at freemarker.core.Environment.visit(Environment.java:210)
at freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:179)
at freemarker.core.Environment.visit(Environment.java:417)
at freemarker.core.IteratorBlock.accept(IteratorBlock.java:102)
at freemarker.core.Environment.visit(Environment.java:210)
at freemarker.core.MixedContent.accept(MixedContent.java:92)
at freemarker.core.Environment.visit(Environment.java:210)
at freemarker.core.IfBlock.accept(IfBlock.java:82)
at freemarker.core.Environment.visit(Environment.java:210)
at freemarker.core.MixedContent.accept(MixedContent.java:92)
at freemarker.core.Environment.visit(Environment.java:210)
at freemarker.core.Environment.process(Environment.java:190)
at freemarker.template.Template.process(Template.java:237)
at org.apache.struts2.dispatcher.Dispatcher.sendError(Dispatcher.java:797)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:519)
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:851)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:278)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:300)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.lang.NullPointerException
at freemarker.ext.beans.SimpleMemberModel.unwrapArguments(SimpleMemberModel.java:85)
at freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:106)
... 43 more
1 ответ
Решил проблему, используя правильный синтаксис JSON.
В случае, если кто-то застрянет на чем-то похожем, в моем случае правильный синтаксис:
{clientFeature: {"feature":{"id":3,"enabled":true,"description":"description here","type":"FEATURE_TYPE_HERE"},"countries":["SG"],"clientId":10}}
I would like to explain what i've been found when develop API using Strtus2 rest plugin. you should understand how rest plugin manage our bean that expose to outside world (json, xml, etc)
i've stuck for a day to figure out how to my json bellow does't converting into my bean list properly.
so, this is my json string:
{
"messagesList": [{
"id": "E57EC40F",
"body": "Ok!",
... other_property
},{
"id": "25B42CC8CCE57EC40F",
"body": "Testing",
... other_property
}],
"ackList": [{
"id": "5B42CC8CCE57EC40F",
"queueNumber": 100,
"chatId": "3434",
"status": "delivered"
},{
"id": "E6A25B42CC8CCE57EC40F",
"queueNumber": 100,
"chatId": "1747",
"status": "viewed"
}]
}
with this json, i assume i could get and automatically translate that json to my list by add property
List<OurModel> messageList;
List<OurModel> ack;
in my controller. so my wrong controller looks like this:
@ParentPackage("api-pesan")
@Namespace("/api/pesan")
public class RetrieveMessageApi implements ModelDriven<Object> {
@Autowired
WebHookService webHookService;
Object retModal;
List<OurModel> messagesList;
List<OurModel> ack;
public HttpHeaders create() throws IOException {
System.out.println(messagesList.toString());
System.out.println(ack.toString());
return new DefaultHttpHeaders("create");
}
public Object getModel() {
return model;
}
//setter and getter
}
if you assume you could print line the messagesList and ack property in create method, you are wrong. i am stuck a day to figure it out. this is my explanation about how to post json to our strtus2 rest plugin.
first of all, the rest plugin in struts2 only read the object (property name, also object as a type) when they retrieve and return object to the outside restful url.
by then, i create my class (pojo) to wrap my list. the class looks like this
public class WrapperModel implements Serializable {
private List<YourObject> messages;
private List<YourObject> ack;
//setter getter
}
you will notice, our json structure looks like the same with this class. so this is my complete and successful cotroller class that could get json and binding to our property properly.
@ParentPackage("api-pesan")
public class RetrieveMessageApi implements ModelDriven<Object> {
@Autowired
WebHookService webHookService;
Object model = new WrapperModel();
public HttpHeaders create() throws IOException {
System.out.println(model.toString());
return new DefaultHttpHeaders("create");
}
public Object getModel() {
return model;
}
}
then try to hit and send json via http://localhost:8080/app/api/api-pesan/retrieve-message.json and method post, also your json as body.
the result, you will see on log console the string of list in our property that source from our json.
Thank you, hope this help you guys....