REST的一点心得

     最近手里头忙着写一些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服务应该是不错的。这仅仅是个人一些看法,也是在很短的时间之内想出来的方法。或许有很多漏洞,请指教





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值