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

胶州城乡建设局网站郑州网站建设 服务创业

胶州城乡建设局网站,郑州网站建设 服务创业,山东省东营市建设局网站,有一个网站是做釆购的是什么网目录 参考 主要内容 关于Web 创建项目 为什么要用框架 Gin框架介绍 Gin框架安装与使用 安装 第一个Gin示例#xff1a; RESTful API Gin渲染 HTML渲染 自定义模板函数 静态文件处理 使用模板继承 补充文件路径处理 JSON渲染 XML渲染 YMAL渲染 protobuf渲染…目录 参考 主要内容 关于Web 创建项目 为什么要用框架 Gin框架介绍 Gin框架安装与使用 安装 第一个Gin示例 RESTful API Gin渲染 HTML渲染 自定义模板函数 静态文件处理 使用模板继承 补充文件路径处理 JSON渲染 XML渲染 YMAL渲染 protobuf渲染 获取参数 获取querystring参数 获取form参数 获取path参数 参数绑定 文件上传 单个文件上传 多个文件上传 重定向 HTTP重定向 路由重定向 Gin路由 普通路由 路由组 路由原理 Gin中间件 定义中间件 注册中间件 中间件注意事项 运行多个服务 参考 参考博客和文档 Gin框架介绍及使用Gin中文文档 主要内容 本教程主要从下面几个方面来进行讲解 Gin框架基本使用GORM基本使用Web开发项目实战 关于Web Web是基于HTTP协议进行交互的应用网络Web就是通过使用浏览器/APP访问的各种资源一个请求对应一个响应以淘宝网为例我们输入一个url就会返回一个页面 创建项目 首先我们使用Goland创建一个Go项目 创建完成后打开命令窗口输入下面的命令创建一个依赖管理 go mod init gin_demo 然后打开setting页面勾选这个选项【不勾选会导致go.mod依赖爆红】 我们创建一个main.go文件然后使用go代码实现一个请求和响应 package mainimport (fmtnet/http )// http.ResponseWriter代表响应传递到前端的 // *http.Request表示请求从前端传递过来的 func sayHello(w http.ResponseWriter, r *http.Request) {_, _ fmt.Fprintln(w, hello Golang!); }func main() {http.HandleFunc(/hello, sayHello)err : http.ListenAndServe(:9090, nil)if err ! nil {fmt.Println(http server failed, err:%v \n, err)return} } 在浏览器访问如下地址 http://localhost:9090/hello 就能打开我们的hello golang页面了 我们可以给文字添加色彩 // http.ResponseWriter代表响应传递到前端的 // *http.Request表示请求从前端传递过来的 func sayHello(w http.ResponseWriter, r *http.Request) {_, _ fmt.Fprintln(w, h1 stylecolor:redhello Golang!h1); } 然后重启后在刷新 我们还可以把里面的字符串放在一个文件里我们定义一个 hello.html文件 htmltitlehello golang/titlebodyh1 stylecolor:redhello Golang!/h1h1hello gin!/h1img srchttps://timgsa.baidu.com/timg?imagequality80sizeb9999_10000sec1600011052622di9aeee5de695a40c8d469f0c3980c2d48imgtype0srchttp%3A%2F%2Fa4.att.hudong.com%2F22%2F59%2F19300001325156131228593878903.jpg/body /html 然后修改刚刚的main.go使用 ioutil解析文件 package mainimport (fmtio/ioutilnet/http )// http.ResponseWriter代表响应传递到前端的 // *http.Request表示请求从前端传递过来的 func sayHello(w http.ResponseWriter, r *http.Request) {html, _ : ioutil.ReadFile(./template/hello.html)_, _ fmt.Fprintln(w, string(html)); }func main() {http.HandleFunc(/hello, sayHello)err : http.ListenAndServe(:9090, nil)if err ! nil {fmt.Println(http server failed, err:%v \n, err)return} } 最后刷新我们的页面就出来这样的效果了这就是我们通过golang开发的一个Web页面 为什么要用框架 我们通过上面的http包就能够实现一个web的开发那为什么还要用gin呢 其实框架的好处就是别人帮我们搭建了一个舞台同时提供了很多现成的轮子让我们专注于业务的开发同时让开发效率更高。 Gin框架介绍 Gin是一个用Go语言编写的web框架。它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter速度提高了近40倍。 如果你是性能和高效的追求者, 你会爱上Gin。 Go世界里最流行的Web框架Github上有32Kstar。 基于httprouter开发的Web框架。 中文文档齐全简单易用的轻量级框架。 Gin框架安装与使用 安装 下载并安装Gin: go get -u github.com/gin-gonic/gin 第一个Gin示例 package mainimport (github.com/gin-gonic/gin )func main() {// 创建一个默认的路由引擎r : gin.Default()// GET请求方式/hello请求的路径// 当客户端以GET方法请求/hello路径时会执行后面的匿名函数r.GET(/hello, func(c *gin.Context) {// c.JSON返回JSON格式的数据c.JSON(200, gin.H{message: Hello world!,})})// 启动HTTP服务默认在0.0.0.0:8080启动服务r.Run() } 将上面的代码保存并编译执行然后使用浏览器打开127.0.0.1:8080/hello就能看到一串JSON字符串。 RESTful API REST与技术无关代表的是一种软件架构风格REST是Representational State Transfer的简称中文翻译为“表征状态转移”或“表现层状态转化”。 推荐阅读阮一峰 理解RESTful架构 简单来说REST的含义就是客户端与Web服务器之间进行交互的时候使用HTTP协议中的4个请求方法代表不同的动作。 GET用来获取资源POST用来新建资源PUT用来更新资源DELETE用来删除资源。 只要API程序遵循了REST风格那就可以称其为RESTful API。目前在前后端分离的架构中前后端基本都是通过RESTful API来进行交互。 例如我们现在要编写一个管理书籍的系统我们可以查询对一本书进行查询、创建、更新和删除等操作我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。按照经验我们通常会设计成如下模式 请求方法URL含义GET/book查询书籍信息POST/create_book创建书籍记录POST/update_book更新书籍信息POST/delete_book删除书籍信息 同样的需求我们按照RESTful API设计如下 请求方法URL含义GET/book查询书籍信息POST/book创建书籍记录PUT/book更新书籍信息DELETE/book删除书籍信息 Gin框架支持开发RESTful API的开发。 func main() {r : gin.Default()r.GET(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: GET,})})r.POST(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: POST,})})r.PUT(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: PUT,})})r.DELETE(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: DELETE,})})// 启动HTTP服务默认在0.0.0.0:8080启动服务r.Run() } 开发RESTful API的时候我们通常使用Postman来作为客户端的测试工具。 Gin渲染 HTML渲染 我们首先定义一个存放模板文件的templates文件夹然后在其内部按照业务分别定义一个posts文件夹和一个users文件夹。 posts/index.html文件的内容如下 {{define posts/index.html}} !DOCTYPE html html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0meta http-equivX-UA-Compatible contentieedgetitleposts/index/title /head body{{.title}} /body /html {{end}} users/index.html文件的内容如下{{define users/index.html}} !DOCTYPE html html langen headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0meta http-equivX-UA-Compatible contentieedgetitleusers/index/title /head body{{.title}} /body /html {{end}} Gin框架中使用LoadHTMLGlob()或者LoadHTMLFiles()方法进行HTML模板渲染。 func main() {r : gin.Default()r.LoadHTMLGlob(templates/**/*)//r.LoadHTMLFiles(templates/posts/index.html, templates/users/index.html)r.GET(/posts/index, func(c *gin.Context) {c.HTML(http.StatusOK, posts/index.html, gin.H{title: posts/index,})})r.GET(users/index, func(c *gin.Context) {c.HTML(http.StatusOK, users/index.html, gin.H{title: users/index,})})r.Run(:8080) } 自定义模板函数 定义一个不转义相应内容的safe模板函数如下 func main() {router : gin.Default()router.SetFuncMap(template.FuncMap{safe: func(str string) template.HTML{return template.HTML(str)},})router.LoadHTMLFiles(./index.tmpl)router.GET(/index, func(c *gin.Context) {c.HTML(http.StatusOK, index.tmpl, a hrefhttps://liwenzhou.com李文周的博客/a)})router.Run(:8080) } 在index.tmpl中使用定义好的safe模板函数 !DOCTYPE html html langzh-CN headtitle修改模板引擎的标识符/title /head body div{{ . | safe }}/div /body /html 为了让index.tmpl文件有语法显示我们还需要配置一下 然后我们加入 *.tmpl保存即可 静态文件处理 当我们渲染的HTML文件中引用了静态文件时我们只需要按照以下方式在渲染页面前调用gin.Static方法即可。 func main() {r : gin.Default()r.Static(/static, ./static)r.LoadHTMLGlob(templates/**/*)// ...r.Run(:8080) } 使用模板继承 Gin框架默认都是使用单模板如果需要使用block template功能可以通过github.com/gin-contrib/multitemplate库实现具体示例如下 首先假设我们项目目录下的templates文件夹下有以下模板文件其中home.tmpl和index.tmpl继承了base.tmpl templates ├── includes │ ├── home.tmpl │ └── index.tmpl ├── layouts │ └── base.tmpl └── scripts.tmpl 然后我们定义一个loadTemplates函数如下 func loadTemplates(templatesDir string) multitemplate.Renderer {r : multitemplate.NewRenderer()layouts, err : filepath.Glob(templatesDir /layouts/*.tmpl)if err ! nil {panic(err.Error())}includes, err : filepath.Glob(templatesDir /includes/*.tmpl)if err ! nil {panic(err.Error())}// 为layouts/和includes/目录生成 templates mapfor _, include : range includes {layoutCopy : make([]string, len(layouts))copy(layoutCopy, layouts)files : append(layoutCopy, include)r.AddFromFiles(filepath.Base(include), files...)}return r } 我们在main函数中 func indexFunc(c *gin.Context){c.HTML(http.StatusOK, index.tmpl, nil) }func homeFunc(c *gin.Context){c.HTML(http.StatusOK, home.tmpl, nil) }func main(){r : gin.Default()r.HTMLRender loadTemplates(./templates)r.GET(/index, indexFunc)r.GET(/home, homeFunc)r.Run() } 补充文件路径处理 关于模板文件和静态文件的路径我们需要根据公司/项目的要求进行设置。可以使用下面的函数获取当前执行程序的路径。 func getCurrentPath() string {if ex, err : os.Executable(); err nil {return filepath.Dir(ex)}return ./ } JSON渲染 func main() {r : gin.Default()// gin.H 是map[string]interface{}的缩写r.GET(/someJSON, func(c *gin.Context) {// 方式一自己拼接JSONc.JSON(http.StatusOK, gin.H{message: Hello world!})})r.GET(/moreJSON, func(c *gin.Context) {// 方法二使用结构体var msg struct {Name string json:userMessage stringAge int}msg.Name 小王子msg.Message Hello world!msg.Age 18c.JSON(http.StatusOK, msg)})r.Run(:8080) } XML渲染 注意需要使用具名的结构体类型。 func main() {r : gin.Default()// gin.H 是map[string]interface{}的缩写r.GET(/someXML, func(c *gin.Context) {// 方式一自己拼接JSONc.XML(http.StatusOK, gin.H{message: Hello world!})})r.GET(/moreXML, func(c *gin.Context) {// 方法二使用结构体type MessageRecord struct {Name stringMessage stringAge int}var msg MessageRecordmsg.Name 小王子msg.Message Hello world!msg.Age 18c.XML(http.StatusOK, msg)})r.Run(:8080) } YMAL渲染 r.GET(/someYAML, func(c *gin.Context) {c.YAML(http.StatusOK, gin.H{message: ok, status: http.StatusOK}) }) protobuf渲染 r.GET(/someProtoBuf, func(c *gin.Context) {reps : []int64{int64(1), int64(2)}label : test// protobuf 的具体定义写在 testdata/protoexample 文件中。data : protoexample.Test{Label: label,Reps: reps,}// 请注意数据在响应中变为二进制数据// 将输出被 protoexample.Test protobuf 序列化了的数据c.ProtoBuf(http.StatusOK, data) }) 获取参数 获取querystring参数 querystring指的是URL中?后面携带的参数例如/user/search?username小王子address沙河。 获取请求的querystring参数的方法如下 func main() {//Default返回一个默认的路由引擎r : gin.Default()r.GET(/user/search, func(c *gin.Context) {// 可以添加默认值username : c.DefaultQuery(username, 小王子)//username : c.Query(username)address : c.Query(address)//输出json结果给调用方c.JSON(http.StatusOK, gin.H{message: ok,username: username,address: address,})})r.Run() } 我们输入对应的URL就能获取到对应的参数了 http://localhost:9090/web?username小王子address沙河 获取form参数 请求的数据通过form表单来提交例如向/user/search发送一个POST请求获取请求数据的方式如下 func main() {//Default返回一个默认的路由引擎r : gin.Default()r.POST(/user/search, func(c *gin.Context) {// DefaultPostForm取不到值时会返回指定的默认值//username : c.DefaultPostForm(username, 小王子)username : c.PostForm(username)address : c.PostForm(address)//输出json结果给调用方c.JSON(http.StatusOK, gin.H{message: ok,username: username,address: address,})})r.Run(:8080) } 获取path参数 请求的参数通过URL路径传递例如/user/search/小王子/沙河。 获取请求URL路径中的参数的方式如下。 func main() {//Default返回一个默认的路由引擎r : gin.Default()r.GET(/user/search/:username/:address, func(c *gin.Context) {username : c.Param(username)address : c.Param(address)//输出json结果给调用方c.JSON(http.StatusOK, gin.H{message: ok,username: username,address: address,})})r.Run(:8080) } 参数绑定 为了能够更方便的获取请求相关参数提高开发效率我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON、XML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能它能够基于请求自动提取JSON、form表单和QueryString类型的数据并把值绑定到指定的结构体对象。 // Binding from JSON type Login struct {User string form:user json:user binding:requiredPassword string form:password json:password binding:required }func main() {router : gin.Default()// 绑定JSON的示例 ({user: q1mi, password: 123456})router.POST(/loginJSON, func(c *gin.Context) {var login Loginif err : c.ShouldBind(login); err nil {fmt.Printf(login info:%#v\n, login)c.JSON(http.StatusOK, gin.H{user: login.User,password: login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{error: err.Error()})}})// 绑定form表单示例 (userq1mipassword123456)router.POST(/loginForm, func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err : c.ShouldBind(login); err nil {c.JSON(http.StatusOK, gin.H{user: login.User,password: login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{error: err.Error()})}})// 绑定QueryString示例 (/loginQuery?userq1mipassword123456)router.GET(/loginForm, func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err : c.ShouldBind(login); err nil {c.JSON(http.StatusOK, gin.H{user: login.User,password: login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{error: err.Error()})}})// Listen and serve on 0.0.0.0:8080router.Run(:8080) } ShouldBind会按照下面的顺序解析请求中的数据完成绑定 如果是 GET 请求只使用 Form 绑定引擎query。如果是 POST 请求首先检查 content-type 是否为 JSON 或 XML然后再使用 Formform-data。 文件上传 单个文件上传 文件上传前端页面代码 !DOCTYPE html html langzh-CN headtitle上传文件示例/title /head body form action/upload methodpost enctypemultipart/form-datainput typefile namef1input typesubmit value上传 /form /body /html 后端gin框架部分代码 func main() {router : gin.Default()// 处理multipart forms提交文件时默认的内存限制是32 MiB// 可以通过下面的方式修改// router.MaxMultipartMemory 8 20 // 8 MiBrouter.POST(/upload, func(c *gin.Context) {// 单个文件file, err : c.FormFile(f1)if err ! nil {c.JSON(http.StatusInternalServerError, gin.H{message: err.Error(),})return}log.Println(file.Filename)dst : fmt.Sprintf(C:/tmp/%s, file.Filename)// 上传文件到指定的目录c.SaveUploadedFile(file, dst)c.JSON(http.StatusOK, gin.H{message: fmt.Sprintf(%s uploaded!, file.Filename),})})router.Run() } 多个文件上传 func main() {router : gin.Default()// 处理multipart forms提交文件时默认的内存限制是32 MiB// 可以通过下面的方式修改// router.MaxMultipartMemory 8 20 // 8 MiBrouter.POST(/upload, func(c *gin.Context) {// Multipart formform, _ : c.MultipartForm()files : form.File[file]for index, file : range files {log.Println(file.Filename)dst : fmt.Sprintf(C:/tmp/%s_%d, file.Filename, index)// 上传文件到指定的目录c.SaveUploadedFile(file, dst)}c.JSON(http.StatusOK, gin.H{message: fmt.Sprintf(%d files uploaded!, len(files)),})})router.Run() } 重定向 HTTP重定向 HTTP 重定向很容易。 内部、外部重定向均支持。 r.GET(/test, func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, http://www.sogo.com/) }) 路由重定向 路由重定向使用HandleContext r.GET(/test, func(c *gin.Context) {// 指定重定向的URLc.Request.URL.Path /test2r.HandleContext(c) }) r.GET(/test2, func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{hello: world}) }) Gin路由 普通路由 r.GET(/index, func(c *gin.Context) {...}) r.GET(/login, func(c *gin.Context) {...}) r.POST(/login, func(c *gin.Context) {...}) 此外还有一个可以匹配所有请求方法的Any方法如下 r.Any(/test, func(c *gin.Context) {...}) 为没有配置处理函数的路由添加处理程序默认情况下它返回404代码下面的代码为没有匹配到路由的请求都返回views/404.html页面。 r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, views/404.html, nil)}) 路由组 我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性一对{}包裹同组的路由这只是为了看着清晰你用不用{}包裹功能上没什么区别。 func main() {r : gin.Default()userGroup : r.Group(/user){userGroup.GET(/index, func(c *gin.Context) {...})userGroup.GET(/login, func(c *gin.Context) {...})userGroup.POST(/login, func(c *gin.Context) {...})}shopGroup : r.Group(/shop){shopGroup.GET(/index, func(c *gin.Context) {...})shopGroup.GET(/cart, func(c *gin.Context) {...})shopGroup.POST(/checkout, func(c *gin.Context) {...})}r.Run() } 路由组也是支持嵌套的例如 shopGroup : r.Group(/shop){shopGroup.GET(/index, func(c *gin.Context) {...})shopGroup.GET(/cart, func(c *gin.Context) {...})shopGroup.POST(/checkout, func(c *gin.Context) {...})// 嵌套路由组xx : shopGroup.Group(xx)xx.GET(/oo, func(c *gin.Context) {...})} 通常我们将路由分组用在划分业务逻辑或划分API版本时。 路由原理 Gin框架中的路由使用的是httprouter这个库。 其基本原理就是构造一个路由地址的前缀树。 Gin中间件 Gin框架允许开发者在处理请求的过程中加入用户自己的钩子Hook函数。这个钩子函数就叫中间件中间件适合处理一些公共的业务逻辑比如登录认证、权限校验、数据分页、记录日志、耗时统计等。 定义中间件 Gin中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。 // StatCost 是一个统计耗时请求耗时的中间件 func StatCost() gin.HandlerFunc {return func(c *gin.Context) {start : time.Now()c.Set(name, 小王子) // 可以通过c.Set在请求上下文中设置值后续的处理函数能够取到该值// 调用该请求的剩余处理程序c.Next()// 不调用该请求的剩余处理程序// c.Abort()// 计算耗时cost : time.Since(start)log.Println(cost)} } 注册中间件 在gin框架中我们可以为每个路由添加任意数量的中间件。 中间的这个通过 Abort() 可以阻止执行 为全局路由注册 func main() {// 新建一个没有任何默认中间件的路由r : gin.New()// 注册一个全局中间件r.Use(StatCost())r.GET(/test, func(c *gin.Context) {name : c.MustGet(name).(string) // 从上下文取值log.Println(name)c.JSON(http.StatusOK, gin.H{message: Hello world!,})})r.Run() } 为某个路由单独注册 // 给/test2路由单独注册中间件可注册多个r.GET(/test2, StatCost(), func(c *gin.Context) {name : c.MustGet(name).(string) // 从上下文取值log.Println(name)c.JSON(http.StatusOK, gin.H{message: Hello world!,})}) 为路由组注册中间件 为路由组注册中间件有以下两种写法。 写法1shopGroup : r.Group(/shop, StatCost()) {shopGroup.GET(/index, func(c *gin.Context) {...})... } 写法2shopGroup : r.Group(/shop) shopGroup.Use(StatCost()) {shopGroup.GET(/index, func(c *gin.Context) {...})... } 中间件注意事项 gin默认中间件 gin.Default()默认使用了Logger和Recovery中间件其中 Logger中间件将日志写入gin.DefaultWriter即使配置了GIN_MODErelease。Recovery中间件会recover任何panic。如果有panic的话会写入500响应码。 如果不想使用上面两个默认的中间件可以使用gin.New()新建一个没有任何默认中间件的路由。 gin中间件中使用goroutine 当在中间件或handler中启动新的goroutine时不能使用原始的上下文c *gin.Context必须使用其只读副本c.Copy()。 运行多个服务 我们可以在多个端口启动服务例如 package mainimport (lognet/httptimegithub.com/gin-gonic/gingolang.org/x/sync/errgroup )var (g errgroup.Group )func router01() http.Handler {e : gin.New()e.Use(gin.Recovery())e.GET(/, func(c *gin.Context) {c.JSON(http.StatusOK,gin.H{code: http.StatusOK,error: Welcome server 01,},)})return e }func router02() http.Handler {e : gin.New()e.Use(gin.Recovery())e.GET(/, func(c *gin.Context) {c.JSON(http.StatusOK,gin.H{code: http.StatusOK,error: Welcome server 02,},)})return e }func main() {server01 : http.Server{Addr: :8080,Handler: router01(),ReadTimeout: 5 * time.Second,WriteTimeout: 10 * time.Second,}server02 : http.Server{Addr: :8081,Handler: router02(),ReadTimeout: 5 * time.Second,WriteTimeout: 10 * time.Second,}// 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务g.Go(func() error {return server01.ListenAndServe()})g.Go(func() error {return server02.ListenAndServe()})if err : g.Wait(); err ! nil {log.Fatal(err)} }
http://www.dnsts.com.cn/news/156747.html

相关文章:

  • 网站建设公司广东目录和文章wordpress
  • 微商的自己做网站叫什么名字深圳 网站设计
  • 做网站上海配置网站开发环境
  • 网络推广内容深圳网站做优化哪家公司好
  • 方案查一查网站重庆网站开发 公司
  • 如何找人做网站网站策划报价模板
  • 广东哪里有网站建设有引导页的网站
  • 网站描述应该怎么写网站开发哪些
  • 怎么看公司网站是哪里做的汕头兼职网站建设
  • 做网站需要哪些准备html大学设计论文
  • 婚恋网站建设项目创业计划书三大门户网站哪家做的最好
  • 网络营销的网站设计网站推荐html
  • 做网站都需要准备什么班徽logo设计图片
  • 专做PPP项目网站wordpress轻语博客
  • 网站的管理与维护wordpress谷歌网站地图
  • 网站平台建设费用做搜狗网站优化快速排
  • 最新网站模板企业营销型网站类型
  • 响应式网站管理系统桐梓县工程建设交易网站
  • 简述站点推广有哪些方式上海排名优化推广工具
  • 南宁做网站找哪家公司怎么注册域名免费
  • 4.1进行网站建设与推广网站蓝色和红色搭配
  • 网站咋做推广展厅设计素材网站
  • 手机网站设计公司立找亿企邦创建网站代码
  • 做网站的服务器哪个系统好网站服务器的作用和功能有哪些
  • 国展网站建设做u盘的老外网站
  • 自己怎么做微网站科技园
  • 流媒体网站开发教程wordpress 分页数
  • 安溪网站制作劳务工程信息平台
  • ?]后台的网站可以备案吗北京企业名录大全
  • 网站备案幕布psd制作网站费用分类