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

wordpress站长邮箱哪里可以接做ppt的网站

wordpress站长邮箱,哪里可以接做ppt的网站,吉林省 网站建设,北京环保网站建设一、前言 web相关知识探索二中研究了请求是如何映射到具体接口#xff08;方法#xff09;中的#xff0c;本次文章主要研究请求中所带的参数是如何映射到接口参数中的#xff0c;也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、…一、前言 web相关知识探索二中研究了请求是如何映射到具体接口方法中的本次文章主要研究请求中所带的参数是如何映射到接口参数中的也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。本次主要研究注解方式以及Servlet API方式。 二、 注解方式 接口参数绑定主要涉及的一下注解 PathVariable、RequestHeader、ModelAttribute、RequestParam、CookieValue、RequestBody、MatrixVariable 其中MatrixVariable矩阵变量注解实际开发中基本不用跳过研究。主要研究一下springmvc是如何将请求中的参数绑定到下面一个个注解修饰的参数中的。 一、测试用例  RestController public class MvcTestController {/**** param id* param name* param pv 这个map可以接受url上的所有值* key是idvalueurl路径上的值* key是namevalue* param userAgent* param header* param age* param list* param params* param _ga* param cookie* return*/GetMapping(/demo/{id}/test/{name})public MapString,Object test(PathVariable(id) Integer id,PathVariable(name) String name,PathVariable MapString,String pv,RequestHeader(User-Agent) String userAgent,RequestHeader MapString,String header,RequestParam(age) Integer age,RequestParam(list) ListString list,RequestParam MapString,String params,CookieValue(_ga) String _ga,CookieValue(_ga) Cookie cookie){MapString,Object map new HashMap();map.put(id,id);map.put(name,name);map.put(pv,pv);map.put(userAgent,userAgent);map.put(headers,header);map.put(age,age);map.put(list,list);map.put(params,params);map.put(_ga,_ga);return map;}GetMapping(/test/servlet)public Map testServlet(HttpServletRequest request){MapString,Object map new HashMap();map.put(request,request);return map;}PostMapping(/test/method)public Map testPostMethod(RequestBody String content){MapString,Object map new HashMap();map.put(content,content);return map;} }首先请求进来统一是有前端控制器中央处理器也即是DispatcherServlet这个类进行处理。然后中央处理器就会去调用处理器映射器也即是handlerMappinghandlerMapping通过请求方式以及请求路径找到匹配的handler也就是我们常说的接口也是具体的处理方法之后DispatcherServlet调用处理器适配器也就是handlerAdatper根据具体的handler规则去执行对应的handler。 二·、原理 get请求http://localhost:8080/demo/1/test/zhangsan进入到上一章研究到的通过handlerMapping获取到具体的handler地方。 1、HandlerMapping中找到能处理请求的HandlerController.method() 2、然后为当前Handler 找一个适配器 HandlerAdapter这个适配器一般就是RequestMappingHandlerAdapter 3、适配器执行目标方法并确定方法参数的每一个值 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest request;HandlerExecutionChain mappedHandler null;boolean multipartRequestParsed false;WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv null;Exception dispatchException null;try {processedRequest this.checkMultipart(request);multipartRequestParsed processedRequest ! request;// 这里就是上一张研究到获取合适的handler的地方mappedHandler this.getHandler(processedRequest);if (mappedHandler null) {this.noHandlerFound(processedRequest, response);return;} // 这里就是获取到合适的处理器适配器的地方源码在下方HandlerAdapter ha this.getHandlerAdapter(mappedHandler.getHandler());String method request.getMethod();boolean isGet GET.equals(method);if (isGet || HEAD.equals(method)) {long lastModified ha.getLastModified(request, mappedHandler.getHandler());if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 这里是利用处理器适配器执行具体的handler     mv ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception var20) {dispatchException var20;} catch (Throwable var21) {dispatchException new NestedServletException(Handler dispatch failed, var21);}this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException(Handler processing failed, var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler ! null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}} } 一、找到合适的处理器适配器  // 找到合适的HandlerAdapter protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {// 遍历所有的处理器适配器直到找到合适的 总共会有4种 if (this.handlerAdapters ! null) {Iterator var2 this.handlerAdapters.iterator();while(var2.hasNext()) {HandlerAdapter adapter (HandlerAdapter)var2.next();// 这里的handler刚好就是HandlerMethod 对象具体可以查看上一张获取handler。if (adapter.supports(handler)) {return adapter;}}}throw new ServletException(No adapter for handler [ handler ]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler);}// 判断是否支持当前handlerpublic final boolean supports(Object handler) {return handler instanceof HandlerMethod this.supportsInternal((HandlerMethod)handler);}0--支持方法上标注RequestMapping、GetMpping等注解的处理器适配器 1--支持函数式编程的 二、利用处理器适配器执行具体的handler 从mv ha.handle(processedRequest, response, mappedHandler.getHandler());方法进入 一、参数解析器--HandlerMethodArgumentResolver 1、参数解析器主要是确定将要执行的目标方法的每一个参数的值是什么 2、SpringMVC目标方法能写多少种参数类型。取决于参数解析器 例如目标方法controller中的方法也即是具体接口使用了RequestParam注解springmvc就会使用RequestParamMethodArgumentResolvers这个参数解析器进行解析其他注解同理会用其他解析器解析。 // 这是参数解析器接口具体实现有27种 public interface HandlerMethodArgumentResolver {// 当前解析器是否支持解析这种参数boolean supportsParameter(MethodParameter var1);// 支持就调用 resolveArgumentNullableObject resolveArgument(MethodParameter var1, Nullable ModelAndViewContainer var2, NativeWebRequest var3, Nullable WebDataBinderFactory var4) throws Exception; }二、返回值处理器--HandlerMethodReturnValueHandler 返回值处理器主要就是处理返回值类型的有多少种返回值处理器就能处理多少种返回值类型。这期主要研究参数解析器返回值处理器另外再研究。 public interface HandlerMethodReturnValueHandler {boolean supportsReturnType(MethodParameter var1);void handleReturnValue(Nullable Object var1, MethodParameter var2, ModelAndViewContainer var3, NativeWebRequest var4) throws Exception; }三、将参数解析器、返回值处理器包装到执行handler对象中 Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest new ServletWebRequest(request, response);ModelAndView var15;try {WebDataBinderFactory binderFactory this.getDataBinderFactory(handlerMethod);ModelFactory modelFactory this.getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod this.createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) {// invocableMethod这个对象就是执行具体的handler的把参数解析器包装进去 invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);} // invocableMethod这个对象就是执行具体的handler的把返回值处理器包装进去if (this.returnValueHandlers ! null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);ModelAndViewContainer mavContainer new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);AsyncWebRequest asyncWebRequest WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);Object result;if (asyncManager.hasConcurrentResult()) {result asyncManager.getConcurrentResult();mavContainer (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(this.logger, (traceOn) - {String formatted LogFormatUtils.formatValue(result, !traceOn);return Resume with async result [ formatted ];});// 执行具体的handler也就是controller中的接口方法invocableMethod invocableMethod.wrapConcurrentResult(result);}invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);if (asyncManager.isConcurrentHandlingStarted()) {result null;return (ModelAndView)result;}var15 this.getModelAndView(mavContainer, modelFactory, webRequest);} finally {webRequest.requestCompleted();}return var15;} 四、真正执行目标方法 真正执行目标方法是在这个类ServletInvocableHandlerMethod里面的invokeAndHandle方法里的        Object returnValue this.invokeForRequest(webRequest, mavContainer, providedArgs);这个方法 真正执行目标方法有三个重要的步骤 1、将目标方法中的参数与请求进来的参数进行绑定以及判断是否能否绑定等等。也即是对请求参数进行解析这里就需要用到参数解析器了 2、通过反射调用目标方法进行执行 3、将目标方法执行后的数据绑定也就是对返回值进行处理这里就用到返回值处理器了 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {//这里是真正执行目标方法执行这个方法就会到controller里面的接口方法下图可见Object returnValue this.invokeForRequest(webRequest, mavContainer, providedArgs);this.setResponseStatus(webRequest);if (returnValue null) {if (this.isRequestNotModified(webRequest) || this.getResponseStatus() ! null || mavContainer.isRequestHandled()) {this.disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}} else if (StringUtils.hasText(this.getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers ! null, No return value handlers);try {this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);} catch (Exception var6) {if (logger.isTraceEnabled()) {logger.trace(this.formatErrorForReturnValue(returnValue), var6);}throw var6;}} // 执行目标方法源码 Nullablepublic Object invokeForRequest(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 这里就是将请求参数与目标方法中的参数进行绑定Object[] args this.getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace(Arguments: Arrays.toString(args));}// 通过反射执行目标方法return this.doInvoke(args);} 一、将请求参数与目标方法上的参数进行绑定 1、判断是否支持解析当前参数如果有一个不支持就会报错 // 这段代码主要就是确定 controllerr中具体接口方法上的参数的值。也就是把请求参数的值与目标方法参数进行绑定protected Object[] getMethodArgumentValues(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 这里就是获取controllerr中具体接口方法上的参数、以及对应的类型参数的位置等等也就是获取参数声明信息MethodParameter[] parameters this.getMethodParameters();// 如果目标方法没有参数也就不需要绑定直接返回if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;} else {// 创建一个数组长度是目标方法参数的个数Object[] args new Object[parameters.length];// 循环遍历目标方法参数for(int i 0; i parameters.length; i) {MethodParameter parameter parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] findProvidedArgument(parameter, providedArgs);if (args[i] null) {// 判断当前解析器是否支持当前接口方法中的这个参数类型if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, No suitable resolver));}try {// 这里将进行参数解析解析绑定后将进行下一个参数的解析与绑定args[i] this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);} catch (Exception var10) {if (logger.isDebugEnabled()) {String exMsg var10.getMessage();if (exMsg ! null !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw var10;}}}return args;}}// 判断解析器是否支持当前参数类型的解析解析器上面有讲过有27中解析器public boolean supportsParameter(MethodParameter parameter) {return this.getArgumentResolver(parameter) ! null;}Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {// this.argumentResolverCache刚进来的时候是空的HandlerMethodArgumentResolver result (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);if (result null) {// 第一次是空的会进到这里然后 挨个遍历27个解析器Iterator var3 this.argumentResolvers.iterator();while(var3.hasNext()) {HandlerMethodArgumentResolver resolver (HandlerMethodArgumentResolver)var3.next();// 这里判断是否支持具体源码在下方if (resolver.supportsParameter(parameter)) {result resolver;// 解析出来后放入本地缓存中下次进来就不用再判断了this.argumentResolverCache.put(parameter, resolver);break;}}}return result;}// 判断解析器是否支持当前参数解析这里只拿PathVariable注解解析器源码举例public boolean supportsParameter(MethodParameter parameter) {// 判断当前参数是否使用了PathVariable注解没使用就不支持解析if (!parameter.hasParameterAnnotation(PathVariable.class)) {return false;// 到了这里就说明当前参数一定是PathVariable注解修饰的然后判断是不是map如果不是map那么就能够支持} else if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {return true;} else {// 到这里就说明参数是map类型的然后还需要做其他判断也就是PathVariable注解中的value一定要有值如果没有值就说明这个注解的map想要全部接受url// 上的参数这种就需要用另外一个解析器进行处理了如果有值就说明url上的参数是map类型的名字是PathVariable上的value属性的值。然后会将这个mapp参数值映射到// 这个参数上PathVariable pathVariable (PathVariable)parameter.getParameterAnnotation(PathVariable.class);return pathVariable ! null StringUtils.hasText(pathVariable.value());}} 2、通过了参数解析判断后将进行真正的参数解析。  // 这里主要是进行参数解析这个方法是在HandlerMethodArgumentResolverComposite这个类下Nullablepublic Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {// 拿到所有参数解析器 HandlerMethodArgumentResolver resolver this.getArgumentResolver(parameter);if (resolver null) {throw new IllegalArgumentException(Unsupported parameter type [ parameter.getParameterType().getName() ]. supportsParameter should be called first.);} else {// 调用参数解析器解析参数的方法return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}}// 这里是拿到这个参数对应的解析器例如是PathVariable注解修饰的参数那就会拿到PathVariable的参数解析器Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {// 这里首先会从缓存中获取由于之前在做判断是否支持这种类型的参数时有做过缓存处理所以这里可以直接获取到这个参数类型对应的参数解析器HandlerMethodArgumentResolver result (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);if (result null) {Iterator var3 this.argumentResolvers.iterator();while(var3.hasNext()) {HandlerMethodArgumentResolver resolver (HandlerMethodArgumentResolver)var3.next();if (resolver.supportsParameter(parameter)) {result resolver;this.argumentResolverCache.put(parameter, resolver);break;}}}return result;}// 真正开始解析参数这个方法是在AbstractNamedValueMethodArgumentResolver这个类里面应该是一个公共方法类似于模板方法的公共方法处理公共逻辑Nullablepublic final Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {// 这里将注解信息封装成一个对象例如注解里标识这个参数以什么样的名字绑定是否是必须得以及默认值。例如PathVariable(value id,required false)这里面的属性NamedValueInfo namedValueInfo this.getNamedValueInfo(parameter);MethodParameter nestedParameter parameter.nestedIfOptional();// 这里就是从封装对象解析出接口方法要绑定的名字例如这个注解PathVariable(id)解析出来就是id这个名字Object resolvedName this.resolveEmbeddedValuesAndExpressions(namedValueInfo.name);if (resolvedName null) {throw new IllegalArgumentException(Specified name must not resolve to null: [ namedValueInfo.name ]);} else {// 这里开始将url上对应的值与id这个名字进行绑定也即是对id进行赋值这里将参数解析绑定后下面逻辑可以不用看了Object arg this.resolveName(resolvedName.toString(), nestedParameter, webRequest);if (arg null) {if (namedValueInfo.defaultValue ! null) {arg this.resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);} else if (namedValueInfo.required !nestedParameter.isOptional()) {this.handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}arg this.handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());} else if (.equals(arg) namedValueInfo.defaultValue ! null) {arg this.resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);}if (binderFactory ! null) {WebDataBinder binder binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);try {arg binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);} catch (ConversionNotSupportedException var11) {throw new MethodArgumentConversionNotSupportedException(arg, var11.getRequiredType(), namedValueInfo.name, parameter, var11.getCause());} catch (TypeMismatchException var12) {throw new MethodArgumentTypeMismatchException(arg, var12.getRequiredType(), namedValueInfo.name, parameter, var12.getCause());}if (arg null namedValueInfo.defaultValue null namedValueInfo.required !nestedParameter.isOptional()) {this.handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}}this.handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);return arg;}}// 获取参数注解信息private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {// 先从缓存中获取NamedValueInfo namedValueInfo (NamedValueInfo)this.namedValueInfoCache.get(parameter);if (namedValueInfo null) {// 这个方法是从注解中获取注解属性信息namedValueInfo this.createNamedValueInfo(parameter);// 这里其实就是重新更新一下namedValueInfo值中的defaultValue属性。namedValueInfo this.updateNamedValueInfo(parameter, namedValueInfo);this.namedValueInfoCache.put(parameter, namedValueInfo);}return namedValueInfo;}// 从注解中获取注解属性信息,拿PathVariable注解举例protected AbstractNamedValueMethodArgumentResolver.NamedValueInfo createNamedValueInfo(MethodParameter parameter) {// 这里获取的是一个注解PathVariable ann (PathVariable)parameter.getParameterAnnotation(PathVariable.class);Assert.state(ann ! null, No PathVariable annotation);// 这里会把注解传入这个对象里面会把属性保存到这个对象中return new PathVariableNamedValueInfo(ann);}// 把属性保存到这个对象中private static class PathVariableNamedValueInfo extends AbstractNamedValueMethodArgumentResolver.NamedValueInfo {public PathVariableNamedValueInfo(PathVariable annotation) {// 父类NamedValueInfosuper(annotation.name(), annotation.required(), \n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n);}}protected static class NamedValueInfo {private final String name;private final boolean required;Nullableprivate final String defaultValue;public NamedValueInfo(String name, boolean required, Nullable String defaultValue) {this.name name;this.required required;this.defaultValue defaultValue;}}// 这里是对id进行赋值的源码,name就是接口方法中使用注解属性的值也就是PathVariable(id)里面的idNullableprotected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {/***HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE的值为org.springframework.web.servlet.HandlerMapping.uriTemplateVariables*request请求信息里面在org.springframework.web.servlet.HandlerMapping.uriTemplateVariables这个key下保存了url上的请求参数*从请求中获取到数据后保存在map当中其中key为注解属性的idvalue为请求携带的值就拿PathVariable(id)这个注解来说吧key-idvalue-请求请来的值。GetMapping(/demo/{id}/test/{name})是从这里面取到的id、name为key这里是由于请求一进来会有一个UrlPathHelper类里面的方法将url里面的路径变量全部解析出来*然后提前保存到请求域当中。所以uriTemplateVars是请求域中的值但是并未绑定到接口参数上*/MapString, String uriTemplateVars (Map)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0);// 这里就是从请求域中的值里面获取到id对应的value return uriTemplateVars ! null ? uriTemplateVars.get(name) : null;}三、Servlet API方式  当我们的接口参数是WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId这些类型时springmvc会有专门的处理器进行处理。也就是ServletRequestMethodArgumentResolver 这个处理器。主要源码如下。 Overridepublic boolean supportsParameter(MethodParameter parameter) {Class? paramType parameter.getParameterType();return (WebRequest.class.isAssignableFrom(paramType) ||ServletRequest.class.isAssignableFrom(paramType) ||MultipartRequest.class.isAssignableFrom(paramType) ||HttpSession.class.isAssignableFrom(paramType) ||(pushBuilder ! null pushBuilder.isAssignableFrom(paramType)) ||Principal.class.isAssignableFrom(paramType) ||InputStream.class.isAssignableFrom(paramType) ||Reader.class.isAssignableFrom(paramType) ||HttpMethod.class paramType ||Locale.class paramType ||TimeZone.class paramType ||ZoneId.class paramType);} 一、判断是否支持HttpservletRequest类型参数 二、解析出参数
http://www.dnsts.com.cn/news/277964.html

相关文章:

  • 哪个网站买域名好网站优化哪家最专业
  • 做食品网站需要什么条件国家建设协会工程质量分会网站
  • 专做特产的网站wordpress读者墙不显示
  • 北京建公司网站价格建网站电脑版和手机版怎么做
  • 什么公司做的网站好西安百度seo推广电话
  • 黄山家居网站建设怎么样郑州编程培训机构
  • 网站什么认证对做电商好珠海建设局网站查公司业绩
  • vs 手机网站开发商城是什么平台
  • 包头网站 建设服务器网站绑定域名网站建设
  • 网站登录和权限怎么做设计类比赛网站
  • 做阿里巴巴网站深圳网站开发antnw
  • 最专业的网站建设网站上的信息可以做证据吗
  • 北京南站属于哪个街道凡科网做的网站在百度上能找到吗
  • 如何使用微信公众号做网站湖北建设企业网站价格
  • 高端品销售网站宿迁网络公司
  • 网站建设 服务内容app推广接单发布平台
  • 网站建设优化兼职在家为什么我的电脑有些网站打不开
  • 做PPT素材图片网站 知乎技术开发合同模板
  • 东城网站开发网站建设文化公司
  • 西安网站建设和推广软件开发工资怎么样
  • 专门做杂志的网站有哪些网页升级访问永久你懂的
  • 德阳做网站的互联网公司两网站会员同步
  • 上海cms建站系统电子会员卡系统哪个好
  • 深圳网站开发专业团队广州番禺怎么样
  • 网站建设与维护 电子版比一网站建设
  • 网站怎么更改布局建材装修网站建设
  • 百度网站的总结提升学历教育
  • 网站数据丢失景区门户网站建设方案
  • 手机网站模板更改vi设计公司 北京
  • 做家电网站好做58网站空调维修接单怎么样