最近手里头忙着写一些REST服务,以前用CXF写了一年多的Webservice服务,但是总觉得有点重量型,并且WSDL那语法看起来也累,虽然JAVA有很多工具去动态生成代理类,和动态生成服务类,但是总觉得太重量级,前几天项目忙着部署聚石塔,看了看淘宝SDK的写法,自己也就模仿着写了写,项目中用的struts2,正好有个plugin来完成其构建,虽然这个plugin被人骂了很多回,但是以前老的项目还是struts2用的多。也就将就着用了。
http://www.ibm.com/developerworks/cn/java/j-lo-struts2rest/ 这里是ibm里一章介绍这个怎么用的文章可以参考
里边写了很多,我也不在这做那些详细概念的综述了,我只是讲讲我的看法,前面说这个东西被人骂了很多回,就是因为他这个plugin逻辑是侵入式的,会被原来的actionMapper给搞乱掉,需要手动设置一些struts一些系统级参数才能正常跑的动。在struts.properties参数设置如下
struts.mapper.class=org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper
struts.mapper.prefixMapping=/rest:rest,/default:struts
其中/rest:rest,表明URL /rest由RESTACTIONMAPPER去解析,其它还交给default去解析
下面就是代码设计基类了,由于一个接口可以有PUT,POST等等请求,RESTFUL设计的API就是尽量用把该用的都用起来,不要总是一味的POST和GET请求,错误返回参数可以选择HTTP的错误状态来表达其系统级别的错误,自定义返回错误表达其业务级别的返回错误
/**
*
* @ClassName: BaseRestController
* @Description:TODO
* @param <T>
*
* @param <T>
*/
public abstract class BaseRestController<T> extends RestActionSupport implements
ModelDriven<T>, Serializable, Preparable {
protected Logger logger = Logger.getLogger(this.getClass());
private static final long serialVersionUID = 7449478681110941532L;
protected T entity;
// REST服务ID
protected String id;
protected ObjectMapper objectMapper = new ObjectMapper();
// 版本
private String version;
private String appKey;
// 流的字符集
private String format;
// 签名方法 MD5还是HMAC
private String signMethod;
// 签名后字符
private String sign;
// 操作时间
private String timestamp;
// 访问token
protected String session;
protected String restStr;
// 返回参数类
protected ObjectResponse objResponse;
protected int page = 1;
protected int rows = 80;
/**
* GET: /{model}
*/
public HttpHeaders index() {
boolean flag = check();
try {
if (flag) {
prepare();
query();
} else {
return new DefaultHttpHeaders("success").disableCaching();
}
} catch (Exception e) {
logger.error("error", e);
return new DefaultHttpHeaders("success").disableCaching();
}
return new DefaultHttpHeaders("success").disableCaching();
}
protected abstract String query();
/**
* GET: /{model}/{id}
*/
public HttpHeaders show() {
try {
boolean flag = check();
if (flag) {
prepare();
query(id);
} else {
return new DefaultHttpHeaders("error");
}
return new DefaultHttpHeaders("show");
} catch (Exception e) {
logger.error("error", e);
return new DefaultHttpHeaders("error");
}
}
protected abstract String query(String id);
/**
* POST: /{model}
*/
public HttpHeaders create() {
boolean flag = check();
String idnew;
try {
if (flag) {
prepare();
idnew = addNew();
} else {
return new DefaultHttpHeaders("success").disableCaching();
}
} catch (Exception e) {
logger.error("error", e);
return new DefaultHttpHeaders("success").disableCaching();
}
return new DefaultHttpHeaders("success").setLocationId(idnew);
}
protected abstract String addNew();
/**
* PUT: /{model}/{id}
*/
public String update() {
boolean flag = check();
try {
if (flag) {
prepare();
update(id);
} else {
return SUCCESS;
}
} catch (Exception e) {
logger.error("error", e);
return SUCCESS;
}
return SUCCESS;
}
protected abstract void update(String id);
/**
* DELETE: /{model}/{id}
*/
public String destroy() {
boolean flag = check();
try {
if (flag) {
prepare();
delete(id);
} else {
return SUCCESS;
}
} catch (Exception e) {
logger.error("error", e);
return SUCCESS;
}
return SUCCESS;
}
protected abstract String delete(String id);
/**
* 子类通过重写此方法初始化实体类
*/
@Override
public abstract void prepare();
@Override
public T getModel() {
return entity;
}
/**
*
* @Description:签名验证方法和校验Appkey的方法
*
* @return
*/
protected boolean check() {
if (appKey != null && !("").equals(appKey)) {
analyzeRestStr();
return true;
} else {
return false;
}
}
/**
*
* @Description:解析流数据
*
*/
protected void analyzeRestStr() {
StringBuilder builder = new StringBuilder();
ServletInputStream inputStream;
try {
inputStream = Struts2Utils.getRequest().getInputStream();
try (BufferedReader in = new BufferedReader(new InputStreamReader(
inputStream, "UTF-8"))) {
String str = null;
while ((str = in.readLine()) != null) {
builder.append(str);
}
}
restStr = URLDecoder.decode(builder.toString(), "UTF-8");
} catch (IOException e) {
logger.error("解析restStr出错", e);
}
}
@SuppressWarnings("unchecked")
/**
*
* @Description:解析JSON数据
*
* @param restStr
* @param class1
*/
protected RestRequestObject convJson2EicRequest(String restStr,
Class<? extends RestRequestObject> class1) {
String msg = "JSON 转换异常,请检查被处理字符串";
String msgDetail = msg + "【" + restStr + "】";
RestRequestObject request = null;
if (!StringUtils.isEmpty(restStr))
try {
request = (RestRequestObject) objectMapper.readValue(restStr,
class1);
} catch (JsonParseException e) {
logger.info(msgDetail, e);
} catch (JsonMappingException e) {
logger.info(msgDetail, e);
} catch (IOException e) {
logger.info(msgDetail, e);
}
return request;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Logger getLogger() {
return logger;
}
public void setLogger(Logger logger) {
this.logger = logger;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getAppKey() {
return appKey;
}
public void setAppKey(String appKey) {
this.appKey = appKey;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public String getSignMethod() {
return signMethod;
}
public void setSignMethod(String signMethod) {
this.signMethod = signMethod;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getRestStr() {
return restStr;
}
public void setRestStr(String restStr) {
this.restStr = restStr;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public String getSession() {
return session;
}
public void setSession(String session) {
this.session = session;
}
这里只是简单的做个出来,也没有按照刚才的说的那样做出来,尤其是错误代码的返回,这里学问真的很多,自己还在摸索阶段,但是大概思路是这样的。泛型用于解释ID属于哪个entity的主键,请求参数为系统级请求参数,和业务级别请求参数。系统级别参数直接URL后面跟着.业务级别参数在流中,读取流后解析json转变成request对象。REST服务也是一个请求一个响应的服务。在该基类里最终返回值永远是ObjectResponse,该类是所有响应类应类的基类。但对于一个实体类来说,可能需要多种功能多个请求,也就有多个响应类,所以可以将 request和response绑定,保证一对一的请求,然后实体类里对应多个request,保证多种不同的方法。
public abstract class RestResponseObject implements Serializable {
/**
*
*/
private static final long serialVersionUID = -3262674499269885280L;
/**
* 错误代码
*/
private String errorCode;
/**
* 错误信息
*/
private String msg;
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public abstract class RestRequestObject<T extends RestResponseObject> {
private T restResponseObject;
/**
* @return the restResponseObject
*/
public T getRestResponseObject() {
return restResponseObject;
}
/**
* @param restResponseObject the restResponseObject to set
*/
public void setRestResponseObject(T restResponseObject) {
this.restResponseObject = restResponseObject;
}
}
例子如下
public class User {
private String id;
private String name;
private String number;
private UpdateUserRequest updateUserRequest;
/**
* @return the id
*/
public String getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the number
*/
public String getNumber() {
return number;
}
/**
* @param number the number to set
*/
public void setNumber(String number) {
this.number = number;
}
/**
* @return the updateUserRequest
*/
public UpdateUserRequest getUpdateUserRequest() {
return updateUserRequest;
}
/**
* @param updateUserRequest the updateUserRequest to set
*/
public void setUpdateUserRequest(UpdateUserRequest updateUserRequest) {
this.updateUserRequest = updateUserRequest;
}
}
public class UpdateUserRequest extends RestRequestObject<UpdateUserResponse>{
private String number;
private String name;
/**
* @return the number
*/
public String getNumber() {
return number;
}
/**
* @param number the number to set
*/
public void setNumber(String number) {
this.number = number;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
public class UpdateUserResponse extends RestResponseObject{
/**
*
*/
private static final long serialVersionUID = 7966959734648504088L;
private Date time;
private boolean success;
/**
* @return the time
*/
public Date getTime() {
return time;
}
/**
* @param time the time to set
*/
public void setTime(Date time) {
this.time = time;
}
/**
* @return the success
*/
public boolean isSuccess() {
return success;
}
/**
* @param success the success to set
*/
public void setSuccess(boolean success) {
this.success = success;
}
}
这样就把各个类之间的关系搭好了,创建一个ACTION做REST服务应该是不错的。这仅仅是个人一些看法,也是在很短的时间之内想出来的方法。或许有很多漏洞,请指教