当前位置: 首页 > news >正文

电子商务网站开发概述asp网站没有数据库

电子商务网站开发概述,asp网站没有数据库,做qq群头像网站,第一ppt官网入口Spring4新特性——集成Bean Validation 1.1(JSR-349)到 SpringMVC Bean Validation 1.1当前实现是Hibernate validator 5#xff0c;且spring4才支持。接下来我们从以下几个方法 讲解Bean Validation 1.1#xff0c;当然不一定是新特性#xff1a; 1. 集成Bean Valida…Spring4新特性——集成Bean Validation 1.1(JSR-349)到 SpringMVC Bean Validation 1.1当前实现是Hibernate validator 5且spring4才支持。接下来我们从以下几个方法 讲解Bean Validation 1.1当然不一定是新特性 1. 集成Bean Validation 1.1到SpringMVC 2. 分组验证、分组顺序及级联验证 3. 消息中使用EL表达式 4. 方法参数/返回值验证 5. 自定义验证规则 6. 类级别验证器 7. 脚本验证器 8. cross-parameter跨参数验证 9. 混合类级别验证器和跨参数验证器 10. 组合多个验证注解 11. 本地化 因为大多数时候验证都配合web框架使用而且很多朋友都咨询过如分组/跨参数验证所以本文介绍下 1、集成Bean Validation 1.1到SpringMVC 1.1、项目搭建 首先添加hibernate validator 5依赖 bean classorg.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMa pping/ bean classorg.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAda pter dependency groupIdorg.hibernate/groupId artifactIdhibernate-validator/artifactId version5.0.2.Final/version /dependency如果想在消息中使用EL表达式请确保EL表达式版本是 2.2或以上如使用Tomcat6请到Tomcat7中 拷贝相应的EL jar包到Tomcat6中。 dependency groupIdjavax.el/groupId artifactIdjavax.el-api/artifactId version2.2.4/version scopeprovided/scope /dependency 请确保您使用的Web容器有相应版本的el jar包。 对于其他POM依赖请下载附件中的项目参考。 1.2、Spring MVC配置文件spring-mvc.xml !-- 指定自己定义的validator -- mvc:annotation-driven validatorvalidator/ !-- 以下 validator ConversionService 在使用 mvc:annotation-driven 会 自动注册- - bean idvalidator classorg.springframework.validation.beanvalidation.LocalValidatorFactoryBean property nameproviderClass valueorg.hibernate.validator.HibernateValidator/ !-- 如果不加默认到 使用classpath下的 ValidationMessages.properties -- property namevalidationMessageSource refmessageSource/ /bean !-- 国际化的消息资源文件本系统中主要用于显示/错误消息定制 -- bean idmessageSource classorg.springframework.context.support.ReloadableResourceBundleMessageSource property namebasenames list !-- 在web环境中一定要定位到classpath 否则默认到当前web应用下找 -- valueclasspath:messages/value valueclasspath:org/hibernate/validator/ValidationMessages/value /list /property property nameuseCodeAsDefaultMessage valuefalse/ property namedefaultEncoding valueUTF-8/ property namecacheSeconds value60/ /bean 此处主要把bean validation的消息查找委托给spring的messageSource。 1.3、实体验证注解public class User implements Serializable { NotNull(message {user.id.null}) private Long id; NotEmpty(message {user.name.null}) Length(min 5, max 20, message {user.name.length.illegal}) Pattern(regexp [a-zA-Z]{5,20}, message {user.name.illegal}) private String name; NotNull(message {user.password.null}) private String password; } 对于验证规则可以参考官方文档或者《第七章 注解式控制器的数据验证、类型转换及格式化》。 1.4、错误消息文件messages.properties user.id.null用户编号不能为空 user.name.null用户名不能为空 user.name.length.illegal用户名长度必须在5到20之间 user.name.illegal用户名必须是字母 user.password.null密码不能为空 1.5、控制器 Controller public class UserController { RequestMapping(/save) public String save(Valid User user, BindingResult result) { if(result.hasErrors()) { return error; } return success; } } 1.6、错误页面 spring:hasBindErrors nameuser c:if test${errors.fieldErrorCount 0} 字段错误br/ c:forEach items${errors.fieldErrors} varerror spring:message varmessage code${error.code} arguments${error.arguments} text${error.defaultMessage}/ ${error.field}------${message}br/ /c:forEach /c:if c:if test${errors.globalErrorCount 0}大家以后可以根据这个做通用的错误消息显示规则。比如我前端页面使用validationEngine显示错误消 息那么我可以定义一个tag来通用化错误消息的显示showFieldError.tag。 1.7、测试 输入如http://localhost:9080/spring4/save?name123 我们得到如下错误 基本的集成就完成了。 如上测试有几个小问题 1、错误消息顺序大家可以看到name的错误消息顺序不是按照书写顺序的即不确定 2、我想显示如用户名【zhangsan】必须在5到20之间其中我们想动态显示用户名、minmax 而不是写死了 3、我想在修改的时候只验证用户名其他的不验证怎么办。 接下来我们挨着试试吧。 2、分组验证及分组顺序 如果我们想在新增的情况验证id和name而修改的情况验证name和password怎么办 那么就需要 分组了。 首先定义分组接口 分组接口就是两个普通的接口用于标识类似于java.io.Serializable。 全局错误br/ c:forEach items${errors.globalErrors} varerror spring:message varmessage code${error.code} arguments${error.arguments} text${error.defaultMessage}/ c:if test${not empty message} ${message}br/ /c:if /c:forEach /c:if /spring:hasBindErrors name------用户名必须是字母 name------用户名长度必须在5到20之间 password------密码不能为空 id------用户编号不能为空 public interface First { } public interface Second { }接着我们使用分组接口标识实体 public class User implements Serializable { NotNull(message {user.id.null}, groups {First.class}) private Long id; Length(min 5, max 20, message {user.name.length.illegal}, groups {Second.class}) Pattern(regexp [a-zA-Z]{5,20}, message {user.name.illegal}, groups {Second.class}) private String name; NotNull(message {user.password.null}, groups {First.class, Second.class}) private String password; } 验证时使用如 RequestMapping(/save) public String save(Validated({Second.class}) User user, BindingResult result) { if(result.hasErrors()) { return error; } return success; } 即通过Validate注解标识要验证的分组如果要验证两个的话可以这样Validated({First.class, Second.class})。 接下来我们来看看通过分组来指定顺序还记得之前的错误消息吗 user.name会显示两个错误消息 而且顺序不确定如果我们先验证一个消息如果不通过再验证另一个怎么办可以通过 GroupSequence指定分组验证顺序 GroupSequence({First.class, Second.class, User.class}) public class User implements Serializable { private Long id; Length(min 5, max 20, message {user.name.length.illegal}, groups {First.class}) Pattern(regexp [a-zA-Z]{5,20}, message {user.name.illegal}, groups {Second.class}) private String name; private String password; }通过GroupSequence指定验证顺序先验证First分组如果有错误立即返回而不会验证Second分 组接着如果First分组验证通过了那么才去验证Second分组最后指定User.class表示那些没有分组 的在最后。这样我们就可以实现按顺序验证分组了。 另一个比较常见的就是级联验证 如 1、级联验证只要在相应的字段上加Valid即可会进行级联验证ConvertGroup的作用是当验证o 的分组是First时那么验证o的分组是Second即分组验证的转换。 3、消息中使用EL表达式 假设我们需要显示如用户名[NAME]长度必须在[MIN]到[MAX]之间此处大家可以看到我们不想把 一些数据写死如NAME、MIN、MAX此时我们可以使用EL表达式。 如 错误消息 其中我们可以使用{验证注解的属性}得到这些值如{min}得到Length中的min值其他的也是类似 的。 到此我们还是无法得到出错的那个输入值如namezhangsan。此时就需要EL表达式的支持首先 确定引入EL jar包且版本正确。然后使用如 使用如EL表达式${validatedValue}得到输入的值如zhangsan。当然我们还可以使用如${min 1 ? 大于1 : 小于等于1}及在EL表达式中也能拿到如Length的min等数据。 另外我们还可以拿到一个java.util.Formatter类型的formatter变量进行格式化 public class User { Valid ConvertGroup(fromFirst.class, toSecond.class) private Organization o; } Length(min 5, max 20, message {user.name.length.illegal}, groups {First.class}) user.name.length.illegal用户名长度必须在{min}到{max}之间 user.name.length.illegal用户名[${validatedValue}]长度必须在5到20之间4、方法参数/返回值验证 这个可以参考《Spring3.1 对Bean Validation规范的新支持(方法级别验证)》概念是类似的具体可以 参考Bean Validation 文档。 5、自定义验证规则 有时候默认的规则可能还不够有时候还需要自定义规则比如屏蔽关键词验证是非常常见的一个功 能比如在发帖时帖子中不允许出现admin等关键词。 1、定义验证注解 ${formatter.format(%04d, min)} package com.sishuok.spring4.validator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; /** * pUser: Zhang Kaitao * pDate: 13-12-15 * pVersion: 1.0 */ Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) Retention(RUNTIME) //指定验证器 Constraint(validatedBy ForbiddenValidator.class) Documented public interface Forbidden { //默认错误消息 String message() default {forbidden.word}; //分组 Class?[] groups() default { }; //负载 Class? extends Payload[] payload() default { }; //指定多个时使用 Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) Retention(RUNTIME) Documented interface List { Forbidden[] value(); }} 2、 定义验证器 package com.sishuok.spring4.validator; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidator ContextImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.io.Serializable; /** * pUser: Zhang Kaitao * pDate: 13-12-15 * pVersion: 1.0 */ public class ForbiddenValidator implements ConstraintValidatorForbidden, String { private String[] forbiddenWords {admin}; Override public void initialize(Forbidden constraintAnnotation) { //初始化得到注解数据 } Override public boolean isValid(String value, ConstraintValidatorContext context) { if(StringUtils.isEmpty(value)) { return true; } for(String word : forbiddenWords) { if(value.contains(word)) { return false;//验证失败 } } return true; } } 验证器中可以使用spring的依赖注入如注入Autowired private ApplicationContext ctx; 3、使用public class User implements Serializable { Forbidden() private String name; } 4、当我们在提交name中含有admin的时候会输出错误消息 forbidden.word您输入的数据中有非法关键词 问题来了哪个词是非法的呢bean validation 和 hibernate validator都没有提供相应的api提供这个 数据怎么办呢通过跟踪代码发现一种不是特别好的方法我们可以覆盖 org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl实现即复制一份代 码放到我们的src中然后覆盖buildAnnotationParameterMap方法 private MapString, Object buildAnnotationParameterMap(Annotation annotation) { …… //将Collections.unmodifiableMap( parameters );替换为如下语句 return parameters; } 即允许这个数据可以修改然后在ForbiddenValidator中 for(String word : forbiddenWords) { if(value.contains(word)) { ((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttribute s().put(word, word); return false;//验证失败 } } 通过 ((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttributes().put(word, word);添加自己的属性放到attributes中的数据可以通过${} 获取。然后消息就可以变成 forbidden.word您输入的数据中有非法关键词【{word}】 这种方式不是很友好但是可以解决我们的问题。 典型的如密码、确认密码的场景非常常用如果没有这个功能我们需要自己写代码来完成而且经常 重复自己。接下来看看bean validation 1.1如何实现的。6、类级别验证器 6.1、定义验证注解 6.2、 定义验证器 package com.sishuok.spring4.validator; import javax.validation.Constraint; import javax.validation.Payload; import javax.validation.constraints.NotNull; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; /** * pUser: Zhang Kaitao * pDate: 13-12-15 * pVersion: 1.0 */ Target({ TYPE, ANNOTATION_TYPE}) Retention(RUNTIME) //指定验证器 Constraint(validatedBy CheckPasswordValidator.class) Documented public interface CheckPassword { //默认错误消息 String message() default ; //分组 Class?[] groups() default { }; //负载 Class? extends Payload[] payload() default { }; //指定多个时使用 Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) Retention(RUNTIME) Documented interface List { CheckPassword[] value(); } } package com.sishuok.spring4.validator; import com.sishuok.spring4.entity.User; import org.springframework.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * pUser: Zhang Kaitao* pDate: 13-12-15 * pVersion: 1.0 */ public class CheckPasswordValidator implements ConstraintValidatorCheckPassword, User { Override public void initialize(CheckPassword constraintAnnotation) { } Override public boolean isValid(User user, ConstraintValidatorContext context) { if(user null) { return true; } //没有填密码 if(!StringUtils.hasText(user.getPassword())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate({password.null}) .addPropertyNode(password) .addConstraintViolation(); return false; } if(!StringUtils.hasText(user.getConfirmation())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate( {password.confirmation.null}) .addPropertyNode(confirmation) .addConstraintViolation(); return false; } //两次密码不一样 if (!user.getPassword().trim().equals(user.getConfirmation().trim())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate( {password.confirmation.error}) .addPropertyNode(confirmation) .addConstraintViolation(); return false; } return true; } } 其中我们通过disableDefaultConstraintViolation禁用默认的约束然后通过 buildConstraintViolationWithTemplate(消息模板)/addPropertyNode(所属属 性)/addConstraintViolation定义我们自己的约束。 6.3、使用放到类头上即可。 7、通过脚本验证 通过脚本验证是非常简单而且强大的lang指定脚本语言请参考javax.script.ScriptEngineManager JSR-223alias是在脚本验证中User对象的名字但是大家会发现一个问题错误消息怎么显示呢 在springmvc 中会添加到全局错误消息中这肯定不是我们想要的我们改造下吧。 7.1、定义验证注解 CheckPassword() public class User implements Serializable { } ScriptAssert(script _this.password_this.confirmation, lang javascript, alias _this, message {password.confirmation.error}) public class User implements Serializable { } package com.sishuok.spring4.validator; import org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; Target({ TYPE }) Retention(RUNTIME) Constraint(validatedBy {PropertyScriptAssertValidator.class}) Documented public interface PropertyScriptAssert { String message() default {org.hibernate.validator.constraints.ScriptAssert.message}; Class?[] groups() default { }; Class? extends Payload[] payload() default { }; String lang(); String script();String alias() default _this; String property(); Target({ TYPE }) Retention(RUNTIME) Documented public interface List { PropertyScriptAssert[] value(); } } 和ScriptAssert没什么区别只是多了个property用来指定出错后给实体的哪个属性。 7.2、验证器 package com.sishuok.spring4.validator; import javax.script.ScriptException; import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import com.sishuok.spring4.validator.PropertyScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluator; import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluatorFactory; import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; public class PropertyScriptAssertValidator implements ConstraintValidatorPropertyScriptAssert, Object { private static final Log log LoggerFactory.make(); private String script; private String languageName; private String alias; private String property; private String message; public void initialize(PropertyScriptAssert constraintAnnotation) { validateParameters( constraintAnnotation ); this.script constraintAnnotation.script(); this.languageName constraintAnnotation.lang(); this.alias constraintAnnotation.alias(); this.property constraintAnnotation.property(); this.message constraintAnnotation.message(); }public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { Object evaluationResult; ScriptEvaluator scriptEvaluator; try { ScriptEvaluatorFactory evaluatorFactory ScriptEvaluatorFactory.getInstance(); scriptEvaluator evaluatorFactory.getScriptEvaluatorByLanguageName( languageName ); } catch ( ScriptException e ) { throw new ConstraintDeclarationException( e ); } try { evaluationResult scriptEvaluator.evaluate( script, value, alias ); } catch ( ScriptException e ) { throw log.getErrorDuringScriptExecutionException( script, e ); } if ( evaluationResult null ) { throw log.getScriptMustReturnTrueOrFalseException( script ); } if ( !( evaluationResult instanceof Boolean ) ) { throw log.getScriptMustReturnTrueOrFalseException( script, evaluationResult, evaluationResult.getClass().getCanonicalName() ); } if(Boolean.FALSE.equals(evaluationResult)) { constraintValidatorContext.disableDefaultConstraintViolation(); constraintValidatorContext .buildConstraintViolationWithTemplate(message) .addPropertyNode(property) .addConstraintViolation(); } return Boolean.TRUE.equals( evaluationResult ); } private void validateParameters(PropertyScriptAssert constraintAnnotation) { Contracts.assertNotEmpty( constraintAnnotation.script(), MESSAGES.parameterMustNotBeEmpty( script ) ); Contracts.assertNotEmpty( constraintAnnotation.lang(), MESSAGES.parameterMustNotBeEmpty( lang ) ); Contracts.assertNotEmpty( constraintAnnotation.alias(), MESSAGES.parameterMustNotBeEmpty( alias ) ); Contracts.assertNotEmpty( constraintAnnotation.property(), MESSAGES.parameterMustNotBeEmpty( property ) ); Contracts.assertNotEmpty( constraintAnnotation.message(), MESSAGES.parameterMustNotBeEmpty( message ) ); } }和之前的类级别验证器类似就不多解释了其他代码全部拷贝自 org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator。 7.3、使用 和之前的区别就是多了个property用来指定出错时给哪个字段。 这个相对之前的类级别验证器更通用 一点。 8、cross-parameter跨参数验证 直接看示例 8.1、首先注册MethodValidationPostProcessor起作用请参考《Spring3.1 对Bean Validation规范的 新支持(方法级别验证)》 8.2、Service 通过Validated注解UserService表示该类中有需要进行方法参数/返回值验证 CrossParameter注 解方法表示要进行跨参数验证即验证password和confirmation是否相等。 8.3、验证注解 PropertyScriptAssert(property confirmation, script _this.password_this.confirmation, lang javascript, alias _this, message {password.confirmation.error}) bean classorg.springframework.validation.beanvalidation.MethodValidationPostProcess or property namevalidator refvalidator/ /bean Validated Service public class UserService { CrossParameter public void changePassword(String password, String confirmation) { } } package com.sishuok.spring4.validator; //省略importConstraint(validatedBy CrossParameterValidator.class) Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE }) Retention(RUNTIME) Documented public interface CrossParameter { String message() default {password.confirmation.error}; Class?[] groups() default { }; Class? extends Payload[] payload() default { }; } 8.4、验证器 package com.sishuok.spring4.validator; //省略import SupportedValidationTarget(ValidationTarget.PARAMETERS) public class CrossParameterValidator implements ConstraintValidatorCrossParameter, Object[] { Override public void initialize(CrossParameter constraintAnnotation) { } Override public boolean isValid(Object[] value, ConstraintValidatorContext context) { if(value null || value.length ! 2) { throw new IllegalArgumentException(must have two args); } if(value[0] null || value[1] null) { return true; } if(value[0].equals(value[1])) { return true; } return false; } } 其中SupportedValidationTarget(ValidationTarget.PARAMETERS)表示验证参数 value将是参数列 表。 8.5、使用调用userService.changePassword方法如果验证失败将抛出ConstraintViolationException异常然 后得到ConstraintViolation调用getMessage即可得到错误消息然后到前台显示即可。 从以上来看不如之前的使用方便需要自己对错误消息进行处理。 下一节我们也写个脚本方式的跨参 数验证器。 9、混合类级别验证器和跨参数验证器 9.1、验证注解 此处我们通过Constraint指定了两个验证器一个类级别的一个跨参数的。validationAppliesTo指 定为ConstraintTarget.IMPLICIT表示隐式自动判断。 9.2、验证器 RequestMapping(/changePassword) public String changePassword( RequestParam(password) String password, RequestParam(confirmation) String confirmation, Model model) { try { userService.changePassword(password, confirmation); } catch (ConstraintViolationException e) { for(ConstraintViolation violation : e.getConstraintViolations()) { System.out.println(violation.getMessage()); } } return success; } package com.sishuok.spring4.validator; //省略import Constraint(validatedBy { CrossParameterScriptAssertClassValidator.class, CrossParameterScriptAssertParameterValidator.class }) Target({ TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE }) Retention(RUNTIME) Documented public interface CrossParameterScriptAssert { String message() default error; Class?[] groups() default { }; Class? extends Payload[] payload() default { }; String script(); String lang(); String alias() default _this; String property() default ; ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT; }请下载源码查看 9.3、使用 9.3.1、类级别使用 指定property即可其他和之前的一样。 9.3.2、跨参数验证 通过args[0]args[1] 来判断是否相等。 这样我们的验证注解就自动适应两种验证规则了。 10、组合验证注解 有时候可能有好几个注解需要一起使用此时就可以使用组合验证注解 这样我们验证时只需要 简洁多了。 CrossParameterScriptAssert(property confirmation, script _this.password_this.confirmation, lang javascript, alias _this, message {password.confirmation.error}) CrossParameterScriptAssert(script args[0] args[1], lang javascript, alias args, message {password.confirmation.error}) public void changePassword(String password, String confirmation) { } Target({ FIELD}) Retention(RUNTIME) Documented NotNull(message {user.name.null}) Length(min 5, max 20, message {user.name.length.illegal}) Pattern(regexp [a-zA-Z]{5,20}, message {user.name.length.illegal}) Constraint(validatedBy { }) public interface Composition { String message() default ; Class?[] groups() default { }; Class? extends Payload[] payload() default { }; } Composition() private String name;11、本地化 即根据不同的语言选择不同的错误消息显示。 1、本地化解析器 此处使用cookie存储本地化信息当然也可以选择其他的如Session存储。 2、设置本地化信息的拦截器 即请求参数中通过language设置语言。 3、消息文件 4、 浏览器输入 http://localhost:9080/spring4/changePassword?password1confirmation2languageen_US 到此我们已经完成大部分Bean Validation的功能实验了。对于如XML配置、编程式验证API的使用等 对于我们使用SpringMVC这种web环境用处不大所以就不多介绍了有兴趣可以自己下载官方文档学 习。 bean idlocaleResolver classorg.springframework.web.servlet.i18n.CookieLocaleResolver property namecookieName valuelocale/ property namecookieMaxAge value-1/ property namedefaultLocale valuezh_CN/ /bean mvc:interceptors bean classorg.springframework.web.servlet.i18n.LocaleChangeInterceptor property nameparamName valuelanguage/ /bean /mvc:interceptors
http://www.w-s-a.com/news/487016/

相关文章:

  • 成县建设局网站中国建筑有几个工程局
  • 网站打不开被拦截怎么办单页面网站制作
  • 关于协会网站建设的建议设计公司名字参考
  • 怎样申请做p2p融资网站页面设计时最好使用一种颜色
  • 一般做网站上传的图片大小网站软件设计
  • 用来网站备案注册什么公司好wordpress怎么搜索中文主题
  • 网站开发 打标签深圳软件公司排名
  • 邯郸的网站建设电子网站怎么做的
  • 中国企业信用网四川游戏seo整站优化
  • 下载站推广wordpress扩展字段
  • 网站建设这个工作怎么样免费电子版个人简历模板
  • 移动网站设计与制作网站开发接私活
  • 视频制作素材网站wordpress mysql 被删
  • 静态网站 模板公司一般都用什么邮箱
  • 做网站效果图是用ps还是ai泰安人才网最新招聘信息2022年
  • 免费建站网站一级大录像不卡在线看网页郑州网站关键
  • 做网站 然后百度推广哈尔滨建筑网
  • 章丘营销型网站建设网站测评必须做
  • 营销者网站怎么把网站黑了
  • 律师事务所手机网站校园网站设计
  • 网站案例展示分类网站响应速度优化
  • 风景网站的制作网站ip地址查询域名
  • 怎样看网站是谁做的马鞍山什么房产网站做的好
  • 西安推荐企业网站制作平台软装设计方案ppt
  • 网站静态页模板专业网站设计开发公司
  • 手机免费在线搭建网站短网址生成防红
  • 天津网站设计网站制作如何新建wordpress
  • 山东省建设备案网站审批国际新闻最新消息10条简短
  • 成都市建设网扬尘监控网站短域名转换
  • 怎么做手机网站潍坊建设银行网站