axis2 使用json格式互動
因為工作原因,最近對axis2框架進行一些的研究。因此對框架使用中的一些問題進行記錄,方便以後回顧。
系統環境
window7
axis2 1.7.8 / 1.7.4
jdk 1.8
gson 2.8.2
maven 3.5.0
通過官網介紹,axis2原生支援json的方式有兩種
一種是基於XML Stream API(這種不符合需求,就沒有多做了解)
一種是使用純json格式,請求與響應都是json格式,也是此次學習的目標。
首先需要在axis2.xml配置
<messageBuilder contentType="application/json" class="org.apache.axis2.json.gson.JsonBuilder" /> <messageFormatter contentType="application/json" class="org.apache.axis2.json.gson.JsonFormatter" />
然後在services.xml配置
<messageReceivers> <messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="org.apache.axis2.json.gson.rpc.JsonRpcMessageReceiver"/> <messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" class="org.apache.axis2.json.gson.rpc.JsonInOnlyRPCMessageReceiver"/> </messageReceivers>
啟動專案,按照官方案例進行測試,會發現gson格式化出錯。
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 14
測試請求資料
{"echoUser":{"user":{"name":"My_Name","surname":"MY_Surname","middleName":"My_MiddleName","age":123,"address":{"country":"My_Country","city":"My_City","street":"My_Street","building":"My_Building","flat":"My_Flat","zipCode":"My_ZipCode"}}}}
檢視原始碼發現axis2 是使用gson庫來進行解析,對json格式是有要求的,官方案例或許是太久沒有更新了
jsonReader.beginObject(); String messageName=jsonReader.nextName(); // get message name from input json stream jsonReader.beginArray(); int i = 0; for (Class paramType : paramClasses) { jsonReader.beginObject(); argNames[i] = jsonReader.nextName(); methodParam[i] = gson.fromJson(jsonReader, paramType); // gson handle all types well and return an object from it jsonReader.endObject(); i++; } jsonReader.endArray(); jsonReader.endObject();
重新設定json資料
{"request":[{"echoUser":{"user":{"name":"My_Name","surname":"MY_Surname","middleName":"My_MiddleName","age":123.0,"address":{"country":"My_Country","city":"My_City","street":"My_Street","building":"My_Building","flat":"My_Flat","zipCode":"My_ZipCode"}}}}]}
這個時候json已經可以正常解析了,但是還是返回異常。
java.lang.NullPointerException
檢視日誌
對於這種錯誤,在各種Google後也並沒有找到相關資料後,只能自己檢視原始碼。
對比原始碼 JsonRpcMessageReceiver 和 RPCMessageReceiver
可以看到 在設定響應資料時 outMessage有個屬性沒有賦值
找到原因後,就好辦了,重新實現JsonRpcMessageReceiver
services.xml重新配置為自己實現類
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="com.axis2.common.receiver.SimpleJsonRpcMessageReceiver" />
重新進行測試
可以看到已經可以正常返回了json資料了
{"response":{"surname":"admin","age":28}}
注: response這個引數是axis2 框架自動新增的,如果不喜歡可以自行實現JsonFormatter,
同理如果不喜歡請求json格式的限制,可自行實現Jsonutils,按照自己的風格定製。
以下是修改後的程式碼
public class JsonUtils {
public static Object invokeServiceClass(JsonReader jsonReader,
Object service,
Method operation ,
Class[] paramClasses ,
int paramCount ) throws InvocationTargetException,
IllegalAccessException, IOException {
Object[] methodParam = new Object[paramCount];
Gson gson = new Gson();
String[] argNames = new String[paramCount];
if( ! jsonReader.isLenient()){
jsonReader.setLenient(true);
}
int i = 0;
for (Class paramType : paramClasses) {
methodParam[i] = gson.fromJson(jsonReader, paramType);
i++;
}
return operation.invoke(service, methodParam);
}
public static Method getOpMethod(String methodName, Method[] methodSet) {
for (Method method : methodSet) {
String mName = method.getName();
if (mName.equals(methodName)) {
return method;
}
}
return null;
}
}
/**
* @author: yc
*/
public class SimpleJsonRpcMessageReceiver extends JsonRpcMessageReceiver {
private static final Log log = LogFactory.getLog(SimpleJsonRpcMessageReceiver.class);
@Override
public void invokeBusinessLogic(MessageContext inMessage, MessageContext outMessage) throws AxisFault {
Object tempObj = inMessage.getProperty(JsonConstant.IS_JSON_STREAM);
boolean isJsonStream;
if (tempObj != null) {
isJsonStream = Boolean.valueOf(tempObj.toString());
} else {
// if IS_JSON_STREAM property is not set then it is not a JSON request
isJsonStream = false;
}
if (isJsonStream) {
Object o = inMessage.getProperty(JsonConstant.GSON_XML_STREAM_READER);
if (o != null) {
GsonXMLStreamReader gsonXMLStreamReader = (GsonXMLStreamReader)o;
JsonReader jsonReader = gsonXMLStreamReader.getJsonReader();
if (jsonReader == null) {
throw new AxisFault("JsonReader should not be null");
}
Object serviceObj = getTheImplementationObject(inMessage);
AxisOperation op = inMessage.getOperationContext().getAxisOperation();
String operation = op.getName().getLocalPart();
invokeService(jsonReader, serviceObj, operation , outMessage, inMessage);
} else {
throw new AxisFault("GsonXMLStreamReader should be put as a property of messageContext " +
"to evaluate JSON message");
}
} else {
// call RPCMessageReceiver if inputstream is null
super.invokeBusinessLogic(inMessage, outMessage);
}
}
public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name,
MessageContext outMes, MessageContext inMessage) throws AxisFault {
String msg;
Class implClass = serviceObj.getClass();
Method[] allMethods = implClass.getDeclaredMethods();
Method method = JsonUtils.getOpMethod(operation_name, allMethods);
Class[] paramClasses = method.getParameterTypes();
try {
int paramCount = paramClasses.length;
//20180601 修改 不限制json格式,只要是json就可以
Object retObj = JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount);
SOAPFactory fac = getSOAPFactory(inMessage);
SOAPEnvelope envelope = fac.getDefaultEnvelope();
// handle response
outMes.setProperty(JsonConstant.RETURN_OBJECT, retObj);
outMes.setProperty(JsonConstant.RETURN_TYPE, method.getReturnType());
//20180601 修改 原框架程式碼不賦值 會導致空指標,
outMes.setEnvelope(envelope);
} catch (IllegalAccessException e) {
msg = "Does not have access to " +
"the definition of the specified class, field, method or constructor";
log.error(msg, e);
throw AxisFault.makeFault(e);
} catch (InvocationTargetException e) {
msg = "Exception occurred while trying to invoke service method " +
(method != null ? method.getName() : "null");
log.error(msg, e);
throw AxisFault.makeFault(e);
} catch (IOException e) {
msg = "Exception occur while encording or " +
"access to the input string at the JsonRpcMessageReceiver";
log.error(msg, e);
throw AxisFault.makeFault(e);
}
}
測試程式碼
/**
* @author: yc
*/
public class ClientTest {
private static String url = "http://localhost:8082/xxx/services/xxx/xxx";
private static String contentType = "application/json";
private static String charSet = "UTF-8";
private static Gson gson = new Gson();
public static void main(String[] args) throws IOException {
Map<String, String> map = new HashMap<String, String>();
map.put("name", "admin");
map.put("pw", "123456");
String req = gson.toJson(map);
post(req);
}
public static String post(String message) throws UnsupportedEncodingException {
String response = "";
PostMethod post = new PostMethod(url);
RequestEntity entity = new StringRequestEntity(message , contentType, charSet);
post.setRequestEntity(entity);
HttpClient httpclient = new HttpClient();
try {
int result = httpclient.executeMethod(post);
System.out.println("Response status code: " + result);
System.out.println("Response head: " + gson.toJson(post.getResponseHeaders()));
System.out.println("Response body: ");
response = new String(post.getResponseBody(), "utf-8");
System.out.println(response);
return response;
} catch (HttpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
post.releaseConnection();
}
return response;
}
}