Windchill REST
一、作用
REST(Representational State Transfer,表述性状态转移) 是一种软件架构风格。REST提出了一组架构约束条件和原则,任何满足 REST 约束条件和原则的架构,都称为 RESTful 架构。RESTful 用于替代Webservice,实现系统间的集成。无需生成客户端代码,直接使用HTTP方式进行系统间的通讯。
二、定义RESTful接口
2.1 接口通用类
2.1.1 接口基础返回信息相关类
package xxx.xxx.integration.rest.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement()
@ApiModel(value = "BaseReturnObject", description = "基础接口返回对象")
public interface BaseReturnObject {
@XmlElement
@ApiModelProperty(value = "操作者", example = "wcadmin", required = true, position = 10)
String getOperator();
@XmlElement
@ApiModelProperty(value = "返回状态,成功或失败", example = "success", required = true, position = 20)
String getStatus();
@XmlElement
@ApiModelProperty(value = "返回信息,一般是失败时返回", example = "调用失败:xxx", required = true, position = 30)
String getMessage();
}
package xxx.xxx.integration.rest.bean;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
@XStreamAlias("BaseReturnObjectImpl")
public class BaseReturnObjectImpl implements BaseReturnObject {
@XStreamAsAttribute
private String operator;
@XStreamAsAttribute
private String status;
@XStreamAsAttribute
private String message;
/**
* 实例化
*
* @param operator 操作者
* @param status 返回状态
* @param message 返回信息
* @return
*/
public static BaseReturnObjectImpl newInstance(String operator, String status, String message) {
BaseReturnObjectImpl impl = new BaseReturnObjectImpl();
impl.setOperator(operator);
impl.setStatus(status);
impl.setMessage(message);
return impl;
}
@Override
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
@Override
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
如果需要实现文件传输,则需要一个专门处理文件流的类:
package xxx.xxx.integration.rest.bean;
import org.apache.xmlbeans.impl.common.IOUtil;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ApplicationDataFileOutput implements StreamingOutput {
private InputStream inputStream;
private String mimeType;
private String fileName;
public ApplicationDataFileOutput(InputStream inputStream, String mimeType, String fileName) {
this.inputStream = inputStream;
this.mimeType = mimeType;
this.fileName = fileName;
}
@Override
public void write(OutputStream outPutStream) throws IOException, WebApplicationException {
IOUtil.copyCompletely(inputStream, outPutStream);
}
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public InputStream getInputStream() {
return inputStream;
}
}
2.1.2 HTTP通信返回信息相关类
package xxx.xxx.integration.rest.base;
public interface RestResourceAware {
BaseRestResource<?> getRestResource();
void setRestResource(BaseRestResource resource);
}
package xxx.xxx.integration.rest.base;
import xxx.xxx.integration.rest.bean.BaseReturnObject;
import xxx.xxx.integration.rest.bean.BaseReturnObjectImpl;
import com.ptc.windchill.rest.utility.error.RestMessage;
import org.springframework.web.servlet.support.RequestContextUtils;
import wt.session.SessionHelper;
import wt.util.WTException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import java.util.Locale;
public class BaseRestResourceAware implements RestResourceAware {
public static final String STATUS_SUCCESS = "SUCCESS";
public static final String STATUS_FAILURE = "FAILURE";
protected BaseRestResource<?> restResource;
@Override
public BaseRestResource<?> getRestResource() {
return restResource;
}
@Override
public void setRestResource(BaseRestResource resource) {
this.restResource = resource;
}
public Response buildErrorResponse(int httpCode, String erroCode, String errorMessage) {
RestMessage message = new RestMessage();
message.setCode(erroCode);
message.setText(errorMessage);
ResponseBuilder responseBuilder = Response.status(httpCode).entity(message);
return responseBuilder.build();
}
public Response buildExceptionResponse(String errorMessage) {
BaseReturnObject result = null;
try {
result = BaseReturnObjectImpl.newInstance(SessionHelper.getPrincipal().getName(), STATUS_FAILURE, errorMessage);
} catch (WTException e) {
result = BaseReturnObjectImpl.newInstance("Adminstrator", STATUS_FAILURE, errorMessage);
}
ResponseBuilder responseBuilder = Response.status(200).entity(result);
return responseBuilder.build();
}
public Response buildEntityResponse(int httpCode, Object entity) {
ResponseBuilder responseBuilder = Response.status(httpCode).entity(entity);
return responseBuilder.build();
}
public Response buildSuccessResponse(int httpCode, String successCode, String successMessage) {
RestMessage message = new RestMessage();
message.setCode(successCode);
message.setText(successMessage);
ResponseBuilder responseBuilder = Response.status(httpCode).entity(message);
return responseBuilder.build();
}
/**
* 获取header中的用户名并设置到session
*
* @return
*/
public boolean setUserFromHeader(String userName) {
boolean setted = false;
if (userName != null && !userName.isEmpty()) {
try {
SessionHelper.manager.setPrincipal(userName);
setted = true;
} catch (WTException e) {
setted = false;
}
}
return setted;
}
public Locale getLocale() {
Locale locale = RequestContextUtils.getLocale(this.getRestResource().getHttpServletRequest());
return locale;
}
}
package xxx.xxx.integration.rest.base;
import com.ptc.core.rest.AbstractResource;
import com.ptc.xworks.util.ObjectUtils;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
public abstract class BaseRestResource<ServiceImpl extends RestResourceAware> extends AbstractResource {
@Context
protected UriInfo uriInfo;
@Context
protected Request request;
@Context
protected HttpServletRequest servletRequest;
@Context
protected HttpServletResponse servletResponse;
@Context
protected ServletConfig servletConfig;
public UriInfo getUriInfo() {
return uriInfo;
}
public Request getRequest() {
return request;
}
public HttpServletRequest getHttpServletRequest() {
return servletRequest;
}
public HttpServletResponse getHttpServletResponse() {
return servletResponse;
}
public ServletConfig getServletConfig() {
return servletConfig;
}
public SecurityContext getSecurityContext() {
return this.securityContext;
}
public ServletContext getServletContext() {
return this.servletContext;
}
public HttpHeaders getHttpHeaders() {
return this.httpHeaders;
}
public abstract Class<ServiceImpl> getServiceImplClass();
public ServiceImpl getServiceImpl() {
ServiceImpl serviceImpl = ObjectUtils.createNewInstance(this.getServiceImplClass());
serviceImpl.setRestResource(this);
return serviceImpl;
}
}
2.1.3 接口通用入参类
2.1.3.1 用户名
package xxx.xxx.integration.rest.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement()
@ApiModel(value = "UsernameParam", description = "每次请求带的用户名信息")
public interface UsernameParam {
@XmlElement
@ApiModelProperty(value = "用户名", example = "wcadmin", required = true, position = 10)
String getUsername();
}
2.1.3.1 来源系统
package xxx.xxx.integration.rest.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement()
@ApiModel(value = "TargetSystemParam", description = "每次请求带的调用系统标识信息")
public interface TargetSystemParam {
@XmlElement
@ApiModelProperty(value = "调用系统", example = "OA", required = true, position = 10)
String getResource();
}
2.2 接口专用类
以用户登陆(GET)和部件信息查询(POST)接口为例:
2.2.1 接口专用返回信息类
2.2.1.1 部件信息
package xxx.xxx.integration.rest.test.bean;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement()
@ApiModel(value = "PartInfo", description = "部件信息")
public interface PartInfo {
@XmlElement
@ApiModelProperty(value = "部件名称", example = "显示器", required = false, position = 10)
String getPartName();
@XmlElement
@ApiModelProperty(value = "状态", example = "已发布", required = false, position = 10)
String getLifeCycleState();
@XmlElement
@ApiModelProperty(value = "创建者", example = "张三", required = false, position = 10)
String getCreator();
@XmlElement
@ApiModelProperty(value = "默认单位", example = "台", required = false, position = 10)
String getUnit();
}
2.2.1.2 部件信息实现
package xxx.xxx.integration.rest.test.bean;
import wt.part.WTPart;
import wt.util.WTException;
public class PartInfoImpl implements PartInfo {
private String partName;
private String lifeCycleState;
private String creator;
private String unit;
public static PartInfo newPartInfo(WTPart part) throws WTException {
PartInfoImpl impl = new PartInfoImpl();
if (part != null) {
impl.setPartName(part.getName());
impl.setLifeCycleState(part.getLifeCycleState().getDisplay());
impl.setCreator(part.getCreatorFullName());
impl.setUnit(part.getMaster().getDefaultUnit().getDisplay());
}
return impl;
}
@Override
public String getPartName() {
return partName;
}
public void setPartName(String partName) {
this.partName = partName;
}
@Override
public String getLifeCycleState() {
return lifeCycleState;
}
public void setLifeCycleState(String lifeCycleState) {
this.lifeCycleState = lifeCycleState;
}
@Override
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
@Override
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
}
2.2.1.3 部件查询集成返回信息
package xxx.xxx.integration.rest.test.bean;
import xxx.xxx.integration.rest.bean.BaseReturnObject;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement()
@ApiModel(value = "PartReturnObject", description = "部件查询集成返回结果信息")
public interface PartReturnObject extends BaseReturnObject {
@XmlElement
@ApiModelProperty(value = "部件信息", example = "", required = false, position = 10)
PartInfo getPartInfo();
}
2.2.1.4 部件查询集成返回信息实现
package xxx.xxx.integration.rest.test.bean;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
public class PartReturnObjectImpl implements PartReturnObject {
@XStreamAsAttribute
private String operator;
@XStreamAsAttribute
private String status;
@XStreamAsAttribute
private String message;
@XStreamAsAttribute
private PartInfo partInfo;
public static PartReturnObject newPartReturnObject(String operator, String status, String message, PartInfo partInfo) {
PartReturnObjectImpl impl = new PartReturnObjectImpl();
impl.setOperator(operator);
impl.setStatus(status);
impl.setMessage(message);
impl.setPartInfo(partInfo);
return impl;
}
@Override
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
@Override
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public PartInfo getPartInfo() {
return partInfo;
}
public void setPartInfo(PartInfo partInfo) {
this.partInfo = partInfo;
}
}
2.2.2 接口专用入参类
2.2.2.1 部件查询集成入参
package xxx.xxx.integration.rest.test.param;
import xxx.xxx.integration.rest.param.TargetSystemParam;
import xxx.xxx.integration.rest.param.UsernameParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement()
@ApiModel(value = "QueryPartParam", description = "用于查询部件的参数")
public interface QueryPartParam extends UsernameParam, TargetSystemParam {
@XmlElement
@ApiModelProperty(value = "部件编码", example = "2000012300", required = true, position = 10)
String getPartNumber();
}
@ApiModelProperty注解中参数position用于排序,参数allowableValues用于入参的合法值校验;
2.2.2.2 部件查询集成入参实现
package xxx.xxx.integration.rest.test.param;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class QueryPartParamImpl implements QueryPartParam {
private String username;
private String resource;
private String partNumber;
@JsonCreator
public QueryPartParamImpl(@JsonProperty("username") String username, @JsonProperty("resource") String resource,
@JsonProperty("partNumber") String partNumber) {
this.username = username;
this.resource = resource;
this.partNumber = partNumber;
}
@Override
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPartNumber() {
return partNumber;
}
public void setPartNumber(String partNumber) {
this.partNumber = partNumber;
}
}
2.2.3 接口配置文件
TestIntegrationConfiguratorRest-configs.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testIntegrationServiceImpl"
class="xxx.xxx.integration.rest.test.service.TestIntegrationServiceImpl"
scope="prototype">
</bean>
<bean id="testIntegrationRestServiceImpl"
class="xxx.xxx.integration.rest.test.service.TestIntegrationRestServiceImpl"
scope="prototype">
<property name="service" ref="testIntegrationServiceImpl" />
</bean>
</beans>
说明:该配置文件需要部署至应用服务器codebase目录下,一般情况会在对应的项目包下,路径会在接口配置文件读取类中引用。
2.2.4 接口配置文件读取类
package xxx.xxx.integration.rest.test.util;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIntegrationApplicationContextUtils {
private static final ClassPathXmlApplicationContext context;
static {
context = new ClassPathXmlApplicationContext("xxx/xxx/integration/rest/test/config/TestIntegrationConfiguratorRest-configs.xml");
}
/**
* 根据名称获得一个Spring容器中的bean的实例
*
* @param beanName bean名
* @param <T>
* @return
*/
public static <T> T getBean(String beanName) {
return (T) context.getBean(beanName);
}
}
2.2.5 接口主逻辑及实现类
2.2.5.1 测试接口服务
package xxx.xxx.integration.rest.test.service;
import xxx.xxx.integration.rest.bean.BaseReturnObject;
import xxx.xxx.integration.rest.test.param.QueryPartParamImpl;
import wt.util.WTException;
public interface TestIntegrationService {
/**
* 查询部件信息
*
* @param param 入参
* @return
* @throws WTException
*/
BaseReturnObject queryPartInfo(QueryPartParamImpl param) throws WTException;
/**
* 用户登陆
*
* @param username 用户名
* @param password 密码
* @return
* @throws WTException
*/
BaseReturnObject userLogin(String username, String password) throws WTException;
}
2.2.5.2 测试接口服务实现
关于LDAP URL的获取,请参考用户登陆验证的API
package xxx.xxx.integration.rest.test.service;
import xxx.xxx.integration.rest.base.BaseRestResourceAware;
import xxx.xxx.integration.rest.bean.BaseReturnObject;
import xxx.xxx.integration.rest.bean.BaseReturnObjectImpl;
import xxx.xxx.integration.rest.test.bean.PartInfo;
import xxx.xxx.integration.rest.test.bean.PartInfoImpl;
import xxx.xxx.integration.rest.test.bean.PartReturnObject;
import xxx.xxx.integration.rest.test.bean.PartReturnObjectImpl;
import xxx.xxx.integration.rest.test.param.QueryPartParamImpl;
import org.springframework.util.StringUtils;
import wt.fc.PersistenceHelper;
import wt.fc.QueryResult;
import wt.part.WTPart;
import wt.query.QuerySpec;
import wt.query.SearchCondition;
import wt.util.WTException;
import wt.vc.config.LatestConfigSpec;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.Hashtable;
public class TestIntegrationServiceImpl implements TestIntegrationService {
@Override
public BaseReturnObject queryPartInfo(QueryPartParamImpl param) throws WTException {
String partNumber = param.getPartNumber();
WTPart part = findWTPart(partNumber);
if (part != null) {
PartInfo partInfo = PartInfoImpl.newPartInfo(part);
PartReturnObject object = PartReturnObjectImpl.newPartReturnObject(param.getUsername(), BaseRestResourceAware.STATUS_SUCCESS, null, partInfo);
return object;
} else {
return BaseReturnObjectImpl.newInstance(param.getUsername(), BaseRestResourceAware.STATUS_FAILURE, "系统中未查询到部件:" + partNumber);
}
}
@Override
public BaseReturnObject userLogin(String username, String password) throws WTException {
boolean flag = false;
String result = "";
String status = "";
String ldapUrl = "ldap://plm.xxx.com"; // 实际的LDAP URL
try {
flag = validateLogin(username, password, ldapUrl);
} catch (WTException e) {
result = "登录失败,请检查用户名和密码或检查服务器状况";
status = BaseRestResourceAware.STATUS_FAILURE;
}
if (!StringUtils.hasText(result)) {
if (flag) {
result = "登陆成功";
status = BaseRestResourceAware.STATUS_SUCCESS;
} else {
result = "未登录,原因未知";
status = BaseRestResourceAware.STATUS_FAILURE;
}
}
return BaseReturnObjectImpl.newInstance(username, status, result);
}
public static WTPart findWTPart(String partNumber) throws WTException {
WTPart part = null;
if (StringUtils.hasText(partNumber)) {
QuerySpec queryspec = new QuerySpec(WTPart.class);
SearchCondition searchCondition = new SearchCondition(WTPart.class, WTPart.NUMBER, SearchCondition.EQUAL, partNumber);
queryspec.appendSearchCondition(searchCondition);
QueryResult queryresult = PersistenceHelper.manager.find(queryspec);
LatestConfigSpec configSpec = new LatestConfigSpec();
queryresult = configSpec.process(queryresult);
if (queryresult != null && queryresult.hasMoreElements()) {
part = (WTPart) queryresult.nextElement();
}
}
return part;
}
public static boolean validateLogin(String username, String password, String ldapUrl) throws WTException {
Hashtable<String, String> table = new Hashtable<>();
boolean flag = false;
/**
* 补全用户名信息
*/
username = username + ",ou=people,cn=AdministrativeLdap,cn=Windchill_11.0,o=ptc";
username = "uid=" + username;
table.put(Context.PROVIDER_URL, ldapUrl);
table.put(Context.SECURITY_AUTHENTICATION, "simple");
table.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
table.put(Context.SECURITY_PRINCIPAL, username);
table.put(Context.SECURITY_CREDENTIALS, password);
LdapContext context = null;
try {
context = new InitialLdapContext(table, null);
} catch (NamingException e) {
throw new WTException(e);
}
if (context != null) {
flag = true;
}
return flag;
}
}
2.2.5.3 测试接口REST服务
package xxx.xxx.integration.rest.test.service;
import xxx.xxx.integration.rest.test.param.QueryPartParamImpl;
import javax.ws.rs.core.Response;
public interface TestIntegrationRestService {
/**
* 查询部件信息
*
* @param param 入参
* @return
*/
Response queryPartInfo(QueryPartParamImpl param);
/**
* 用户登陆
*
* @param username 用户名
* @param password 密码
* @return
*/
Response userLogin(String username, String password);
}
2.2.5.4 测试接口REST服务实现
package xxx.xxx.integration.rest.test.service;
import xxx.xxx.integration.rest.base.BaseRestResourceAware;
import xxx.xxx.integration.rest.bean.BaseReturnObject;
import xxx.xxx.integration.rest.test.param.QueryPartParamImpl;
import org.springframework.util.StringUtils;
import wt.util.WTException;
import javax.ws.rs.core.Response;
public class TestIntegrationRestServiceImpl extends BaseRestResourceAware implements TestIntegrationRestService{
protected TestIntegrationService service;
public void setService(TestIntegrationService service) {
this.service = service;
}
@Override
public Response queryPartInfo(QueryPartParamImpl param) {
try {
if (param == null || !StringUtils.hasText(param.getPartNumber())) {
return this.buildExceptionResponse("缺失参数值!");
}
/**
* 设置操作的用户
*/
boolean flag = this.setUserFromHeader(param.getUsername());
if (flag) {
BaseReturnObject result = service.queryPartInfo(param);
return this.buildEntityResponse(200, result);
} else {
return this.buildExceptionResponse("设置用户时发生错误,请检查用户名是否存在!");
}
} catch (WTException e) {
// 记录至同步记录表
return this.buildExceptionResponse("请注意,操作失败。详细异常信息请查看PLM系统日志,参考异常原因:" + e.getLocalizedMessage());
}
}
@Override
public Response userLogin(String username, String password) {
try {
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
return this.buildExceptionResponse("缺失参数值!");
}
BaseReturnObject result = service.userLogin(username, password);
return this.buildEntityResponse(200, result);
} catch (WTException e) {
return this.buildExceptionResponse("请注意,操作失败。详细异常信息请查看PLM系统日志,参考异常原因:" + e.getLocalizedMessage());
}
}
}
2.2.5.5 测试接口REST服务封装
package xxx.xxx.integration.rest.test.service;
import xxx.xxx.integration.rest.base.BaseRestResource;
import xxx.xxx.integration.rest.bean.PartReturnObject;
import xxx.xxx.integration.rest.test.param.QueryPartParamImpl;
import xxx.xxx.integration.rest.test.util.TestIntegrationApplicationContextUtils;
import com.ptc.windchill.rest.utility.error.RestMessage;
import com.ptc.windchill.rest.utility.interceptors.Logged;
import io.swagger.annotations.*;
import wt.session.SessionServerHelper;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/test/")
@Logged
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
@Api(value = "/", description = "测试示例接口", tags = {"测试示例接口"})
public class TestIntegrationRestServiceResource extends BaseRestResource<TestIntegrationRestServiceImpl> implements TestIntegrationRestService {
@Override
public Class<TestIntegrationRestServiceImpl> getServiceImplClass() {
return TestIntegrationRestServiceImpl.class;
}
@Override
public TestIntegrationRestServiceImpl getServiceImpl() {
TestIntegrationRestServiceImpl impl = TestIntegrationApplicationContextUtils.getBean("testIntegrationRestServiceImpl");
impl.setRestResource(this);
return impl;
}
@POST
@Path("queryPartInfo")
@ApiOperation(value = "查询部件信息", notes = "使用部件编码进行部件信息查询")
@HeaderParam("CSRF_NONCE")
@ApiImplicitParams({
@ApiImplicitParam(name = "CSRF_NONCE", paramType = "header", dataType = "java.lang.String",
value = "为防止跨站脚本攻击而需要设置的Token",
example = "k8apbqxpRAT4RJtnq4zBDdsPCVaoMMgy8o3iG8UTB23Offwr1pXxA+EkKEiqN+Ih6umRA+RCJznCdaxUqv6fWJpaczHNdKE2/oPvBck6cn2UIexQoKfkKdoTA3SPeaY=",
required = true, allowEmptyValue = false
)
})
@ApiResponses(value = {
@ApiResponse(code = 200, response = PartReturnObject.class, message = "操作成功"),
@ApiResponse(code = 400, response = RestMessage.class, responseContainer = "List", message = "客户端提供的参数错误,无法完成操作"),
@ApiResponse(code = 500, response = RestMessage.class, responseContainer = "List", message = "服务端处理时出现异常,无法完成操作")
})
@Override
public Response queryPartInfo(@ApiParam(value = "查询部件信息参数", required = true, allowEmptyValue = false)
QueryPartParamImpl param) {
boolean enforced = SessionServerHelper.manager.setAccessEnforced(false);
try {
return this.getServiceImpl().queryPartInfo(param);
} finally {
SessionServerHelper.manager.setAccessEnforced(enforced);
}
}
@GET
@Path("userLogin")
@ApiOperation(value = "用户登录认证", notes = "用户使用用户名和密码进行登录认证")
@ApiResponses(value = {@ApiResponse(code = 200, response = BaseReturnObject.class, message = "操作成功"),
@ApiResponse(code = 400, response = RestMessage.class, responseContainer = "List", message = "客户端提供的参数错误,无法完成操作"),
@ApiResponse(code = 500, response = RestMessage.class, responseContainer = "List", message = "服务端处理时出现异常,无法完成操作")})
@Override
public Response userLogin(@QueryParam("username") @ApiParam(value = "登录认证时的用户名", required = true,
allowEmptyValue = false, example = "000001") String username,
@QueryParam("password") @ApiParam(value = "登录认证时的密码", required = true,
allowEmptyValue = false, example = "123456") String password) {
boolean enforced = SessionServerHelper.manager.setAccessEnforced(false);
try {
return this.getServiceImpl().userLogin(username, password);
} finally {
SessionServerHelper.manager.setAccessEnforced(enforced);
}
}
}
GET类型的接口一般用于查询,该接口无需Token;
POST类型的接口一般用于更新或者创建对象,该接口需要Token,故需要在接口入口处配置CSRF_NONC
2.3 注册REST服务
REST服务注册在文件codebase/com/ptc/windchill/rest/rest.properties中,一般不建议直接修改该文件(防止内容被覆盖),而是添加至xconf文件中,执行命令传播至该文件。
在项目定制化的.xconf文件或site.xconf文件中,添加如下配置:
<!-- 注册REST接口服务 -->
<Property name="test.rest.resources"
overridable="true"
targetFile="codebase/com/ptc/windchill/rest/rest.properties"
default="xxx.xxx.integration.rest.test.service.TestIntegrationRestServiceResource"/>
执行命令:xconfmanager -p
部署相关代码,重启服务
三、注解说明
注解 | 说明 |
---|---|
@Path | 类开头的path用于定义注册的所有接口的url地址统一前缀; 方法开头的path加上类开头的path,构成整个rest接口的完整路径 |
@Logged | 记录REST API的请求和返回值输出到日志文件 |
@Produces | HTTP返回报文的报文类型,常用的:MediaType.APPLICATION_JSON、MediaType.TEXT_PLAIN |
@Consumes | HTTP接收报文的报文类型,常用的:MediaType.APPLICATION_JSON、MediaType.TEXT_PLAIN |
@Api | 接口说明 |
@GET @POST @PUT @DELETE | 表示被注解修饰的方法将处理(响应)来自HTTP的对应类型的请求 |
@ApiOperation | 接口方法说明 |
@ApiResponses | 接口返回状态码说明 |
@PathParam | 标注方法的参数来自于请求的URL路径,参数的名称和@Path注解中定义的变量名对应 |
@ApiParam | 参数说明 |
@HeaderParam | HTTP请求的请求头参数 |
@ApiImplicitParams | 用在请求的方法上,包含一组参数说明 |
@ApiImplicitParam | 用在 @ApiImplicitParams 注解中,指定一个请求参数的配置信息 |
四、接口调试
4.1 swagger调试
站点>>实用程序>>业务管理>>首选项管理,找到“客户端自定义”,右键,设置首选项,设置为是。
此时就可以在导航栏中看到Customization:
Customization>>Documentation>>API>>REST APIs (Legacy)
swagger页面
用户登陆(GET)接口调试:
查询部件信息(POST)接口调试:
Model页签中会显示定义的ApiModelProperty注解:
由于查询部件信息接口设置了Token,所以要先获取Token才能进行接口的调试。
4.1.1 获取Token
可以通过Windchill自带的接口中获取:
nonce对应的值就是Token
4.2 第三方工具调试
以Reqable为例,无论哪种类型方式的调用,首先要设置授权认证
用户登陆(GET)接口调试,设置参数:
查询部件信息(POST)接口调试,设置Token:
4.3 测试类调试
4.3.1 HttpURLConnection调用工具类
工具类请参考基于HTTP请求的接口调用
4.3.2 用户登陆(GET)接口调试
package test.integration.plm;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import test.integration.util.HttpClientHelper;
public class PLMTest {
public static void main(String[] args) throws Exception {
String username = "000001";
String password = "123456";
String credentials = username + ":" + password;
credentials = "Basic " + Base64.encodeBase64String(credentials.getBytes());
Map<String, Object> map = new HashMap<>();
map.put("Authorization", credentials);
String baseUrlPath = "http://xxx.xxx.com:80/Windchill/servlet/rest/test/userLogin";
Map<String, Object> params = new HashMap<>();
params.put("username", "000002");
params.put("password", "456789");
String suffix = getURLParam(params);
String login_url = baseUrlPath + "?" + suffix;
String login_result = HttpClientHelper.doGet(login_url, map);
System.out.println(">>>>login_result=" + login_result);
}
public static String getURLParam(Map<String, Object> params) {
StringBuffer result = new StringBuffer();
if (!params.isEmpty()) {
for (String key : params.keySet()) {
Object value = params.get(key);
if (!result.toString().isEmpty()) {
result.append("&");
}
result.append(key);
result.append("=");
result.append(value);
}
}
return result.toString();
}
}
4.3.3 查询部件信息(POST)接口调试
先调用token获取接口,然后再调用查询部件信息接口
package test.integration.plm;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import test.integration.util.HttpClientHelper;
public class PLMTest {
public static void main(String[] args) throws Exception {
String username = "000001";
String password = "123456";
String credentials = username + ":" + password;
credentials = "Basic " + Base64.encodeBase64String(credentials.getBytes());
Map<String, Object> map = new HashMap<>();
map.put("Authorization", credentials);
String token_url = "http://xxx.xxx.com:80/Windchill/servlet/rest/security/csrf";
String token_result = IntegrationHelper.doGet(token_url, map);
System.out.println(">>>>result_token=" + token_result);
Map<String, String> tokenMap = getTokenResultMap(token_result);
String status = tokenMap.get("status");
String result = tokenMap.get("result");
if ("success".equals(status)) {
map.put("CSRF_NONCE", result);
String partInfo_url = "http://xxx.xxx.com:80/Windchill/servlet/rest/test/queryPartInfo";
JSONObject data = new JSONObject();
data.put("username", "000002");
data.put("resource", "OA");
data.put("partNumber", "2000012300");
String partInfo_result = IntegrationHelper.doPost(partInfo_url, data.toString(), map);
System.out.println(">>>>login_result=" + partInfo_result);
} else {
System.out.println(">>>>token获取异常=" + result);
}
}
public static Map<String, String> getTokenResultMap(String param) {
Map<String, String> map = new HashMap<>();
try {
JSONObject jsonObject = JSONObject.parseObject(param);
JSONArray items = (JSONArray)jsonObject.get("items");
for (Object item : items) {
JSONObject object = (JSONObject)item;
JSONObject attributes = (JSONObject)object.get("attributes");
String token = (String)attributes.get("nonce");
map.put("status", "success");
map.put("result", token);
}
} catch (Exception e) {
map.put("status", "failure");
map.put("result", e.getLocalizedMessage());
}
return map;
}
}