Windchill开发-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的请求和返回值输出到日志文件
@ProducesHTTP返回报文的报文类型,常用的:MediaType.APPLICATION_JSON、MediaType.TEXT_PLAIN
@ConsumesHTTP接收报文的报文类型,常用的:MediaType.APPLICATION_JSON、MediaType.TEXT_PLAIN
@Api接口说明
@GET
@POST
@PUT
@DELETE
表示被注解修饰的方法将处理(响应)来自HTTP的对应类型的请求
@ApiOperation接口方法说明
@ApiResponses接口返回状态码说明
@PathParam标注方法的参数来自于请求的URL路径,参数的名称和@Path注解中定义的变量名对应
@ApiParam参数说明
@HeaderParamHTTP请求的请求头参数
@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;
    }

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值