【转】Web API核查表:设计、测试、发布API时需思考的43件事

转自csdn: http://www.csdn.net/article/2013-04-22/2814983-43-Things-To-Think-About-When-Designing-Testing-and-Releasing-your-API

 

作者张红月编译

Web APIAPI开放平台开发经验

摘要:API设计并非易事,从设计到测试以至最终的发布需要经历一个漫长的过程,本文将主要探讨Web API从设计到最终发布,开发者可能忽略或者应该注意的事情,希望对你有所帮助。

当设计、测试或发布一个新的Web API时,你是在一个原有的复杂系统上构建新的系统。那么至少,你也要建立在HTTP上,而HTTP则是基于TCP/IP创建的、TCP/IP建立在一系列的管道上。当然,你也需要考虑Web服务器、应用程序框架或者是API框架。

API从设计到测试以至最终的发布需要经历一个漫长的过程,本文将主要探讨Web API从设计到最终发布,开发者可能忽略或者应该注意的东西。

HTTP篇

HTTP 1.1规范 RFC2616是一个非常大的文档,下面我们节选了一些可能会对API产生影响的内容分享给大家:

1.Idempotent方法:GET、HEAD、PUT、DELETE、OPTIONS以及TRACE都属于idempotent操作;也就是说,“the side-effects of N > 0 identical requests is the same as for a single request.” (RFC2616 §9.1.2

2.验证:用户访问API需要进行识别和验证,HTTP所提供的Authorization头文件就是出于此目的(RFC2616 §14.8 )。 RFC2617则指定了具体的验证计划,包括了最常见的HTTP基本验证。

3.201 Created:使用“201 Created”响应代码表示请求成功,并且创建了一个新资源。201响应可以包含本地头文件中的新资源URI。( RFC2616 §10.2.2

4.202 Accepted:使用“202 Accepted”响应代码表示该请求是有效的,将会被处理,但还未完成。一般情况下是用在服务器后台队列可能出现的地方。( RFC2616 §10.2.3

5.4XX和5XX状态代码:4XX状态代码与5XX状态代码有一个非常重要的区别:4XX代码旨在表明客户端错误,而5XX则是表明服务端错误。( RFC2616 §6.1.1

6.410 Gone:“410 Gone”响应代码是一个很少使用的响应式代码,其主要是通知客户端资源出现在URL中,但实际上并没有。这个用在API里可以指明被删除、存档或过期的项目。( RFC2616 §10.4.11

7.Expect::100-continue:如果API客户端打算发送一个大型的实体请求,像POST、PUT或PATCH,它可以发送“Expect: 100-continue”到HTTP头文件里,在发送实体之前等待“100 continue”响应。这就允许API在返回错误响应信息之前,可以验证那些合理的请求(例如401或者403)。使用它可以提高API的响应能力以及在某些情景下减少宽带。( RFC2616 §8.2.3

8.保持连接畅通:与API服务器保持连接,对于多API请求是个非常大的性能提升。如果配置正确,每个Web服务器应该支持keep-alive连接。

9.HTTP压缩:HTTP压缩可以同时用于响应体(Accept-Encoding: gzip)和请求体(Content-Encoding: gzip),用来提升HTTP API的网络性能。

10.HTTP缓存:在API响应时提供一个Cache-Control头文件。( RFC2616 §14.9

11.缓存验证:如果你有缓存API,那么在响应时,你应该提供Last-Modified或Etag头文件,然后支持IF-Modified-Since或者If-None-Match请求头文件用于有条件的请求。这将允许客户端检查它们的缓存副本是否仍然有效,并且当没有请求时,阻止一个完整的资源下载。如果实现得当,那么条件请求要比普通请求更有效。( RFC2616 §13.3

12.条件修改:ETag头文件也可以用于条件修改资源。( RFC2616 §14.24

13.绝对重定向:这是一个鲜为人知的HTTP/1.1要求,重定向(如。201、301、302、303、307响应代码)应该包含一个绝对URI本地响应头文件。许多客户端在本地支持相对URI,但是如果你想让API兼容更多客户端,你应该在重定向时使用绝对URI。( RFC2616 §14.30

14.链接响应头文件:在RESTful API中,经常需要提供转向其他资源的链接,甚至响应的内容类型无法提供一种自然方式链接(例如,PDF或图像)。 RFC5988在响应头文件中指定了一个链接提供方法。

15.规范URL:对于多资源URL,RFC6596定义了统一的方法来规范网址链接。

16.块传输编码:如果响应内容太大,传输编码:分块(Chunked)是一种很好的流响应到客户端方式,它将会减少服务器和中间服务器的内存使用需求(尤其是对实现HTTP压缩),并且提供更快的首字节响应。

17.块传输编码里的错误处理:在实现块传输编码之前,弄清如何处理发生在中间请求时产生的错误是非常重要的。一旦对响应进行流处理,就无法改变HTTP的状态代码。

18. X-HTTP-Method-Override:有些HTTP客户端不支持任何GET和POST,但你可以通过POST开通其他HTTP方法,使用约定成俗的标准X-HTTP-Method-Overrider头文件去定义“真正”的HTTP方法。

19.URL长度:如果API支持复杂或任意的过滤项作为GET参数,那么记住,无论是客户端还是服务器端都可能会因为超过2000字节的URL长度带来兼容性问题。

API设计篇

20.无状态:没有人希望API能够存储状态,即使是在你的应用程序服务器端。保持应用程序服务器状态自由,可以做到很轻易和很轻松地扩展。

21.内容协商:让你的资源支持多种表现方式,你可以使用内容协商(content negotiation,例如Accept头文件),或者使用不同的URL(例如……?format=json),或者可以让你的内容协商重定向到具体的格式。

22.URI模板: URI模板是一个定义良好的机制,用来提供URI组合能力到客户端,或者定义URL访问终端用户模式。

23.Design for Intent:不要仅通过API来暴露内部业务对象,设计API语义意味着要与用户案例相匹配。更好地介绍,你可以阅读Darrel Miller写的 API Craft

24.版本:理论上讲,一个设计良好的API是无需创建兼容的。而对于实用主义者,它们会把版本放入到API的URL中(例如:a/v1/path),所以,除非是处在一个安全的网络状态下,否则API可能不会按照预期那样工作。

25.授权:记住,当设计API时,并不是所有的用户都可以访问里面的任何对象。

26.批量操作:发送较少的请求来获取或修改更多的数据,最好的方法就是在你的API里使用批量操作。

27.标记页数:API中使用分页服务主要有两大目的:一个是减少不必要的数据传送到客户端;一个是减少应用服务器端不必要的操作。

28.统一的字符编码:在设计和测试API时,Web服务需要支持更多的英文字符。如果你在URL中把Unicode字节作为自然键使用,它将会非常有趣(例如:/users/jimbob/ becomes /users/싸이/)。

29.错误日志:在设计API时,创建错误日志也是非常重要的。实践时最好创建两种日志记录,一个是服务器端,一个是客户端。

内容篇

30.内容类型:关于内容类型(Content Type)可以写整本书,就个人而言,我比较喜欢重用他人开发的内容类型,像 AtomCollection+JSONJSON HAL或者XHTML。定义一套属于自己的内容类型会比你期望的更好。

31.HATEOAS:超媒体作为应用程序状态引擎是一个REST约束,简单点说就是你的内容应该通知客户端下面要做的事情,可以通过链接或表单来通知。

32.日期/时间:当你在API里提供日期/时间值时,应该使用一种格式,包括时区信息。 RFC3339是ISO8601的一个子集,是最简单的日期时间格式。

安全篇

33.SSL:无论你的API是否支持HTTP或HTTPS,你都应该考虑HTTPS这种访问方式,它的增长趋势日益明显。

34.跨站请求伪造(CSRF):如果使用API的交互式用户与普通用户都使用相同的验证,那么你的API很有可能会遭受CSRF攻击。

35.Throttling:如果一个API用户的请求数超过了规定,那么你应该提供一个带Retry-After header的503响应。

36.婉转的拒绝服务:Throttling可以阻止你用最简单的方式进行攻击,但这里还有其他更机智的攻击方式。例如 SlowlorisBillion laughsReDoS,它们都不会占用太多资源,但是它们可以让你的API在瞬间耗尽所有资源。

客户端

无论你是否给用户提供测试代码或者是SDK开发包,都应该给他们提供一个客户端,并且遵循下面这几个步骤:

37.保持连接畅通:一些HTTP客户端需要做一些额外的工作来保持连接持久,持久的连接对感知API性能有着非常重要的影响。

38.授权之前的401:HTTP的另一个怪癖是,它们会在解决一个授权问题之前发出“401 Unauthorized”响应。这样就会延长API的请求时间。

39.Expect: 100-continue:至少有一个API客户端默认使用“Expect: 100-continue”,如果它没有接受“100 Continue”响应,在3秒的超时后会继续发送请求。如果API不支持“100 Continue”,或许会产生另一个性能缺陷,导致客户端禁用。

其它

40.文档:编写API文档是令人厌烦的,但是手写的API文档通常是最好的。编写时一定要包含这些内容:一些可运行的代码或者curl命令行,方便查阅。你也可以参考一些文档工具,例如: apiary.ioMashery I/O DocsSwagger

41.设计与客户:不要在真空中设计API,要与客户打交道或者一起来设计API,参考用户用例。

42.反馈:在设计API时,应提供一个通道供用户进行反馈,

43.自动化测试:API测试是最简单的事情。它最好是自动化的,毕竟,需要好好利用它。

上面提供的这份列表有趣吗?对你是否有帮助呢?欢迎与我们一起讨论。

来自: Mathieu Fenniak

Continue reading 【转】Web API核查表:设计、测试、发布API时需思考的43件事

github 替代

现在喜欢上了git,还是不错的嘛,github现在国内也可以用了,速度很快。但是它的收费可就不便宜了,5个私人仓库要7美刀每月。组织用户起步价是25美刀每月。

 

这里介绍几个替代方案:

 

https://codeplane.com/

九美刀每月,就这一种价格

 

https://unfuddle.com/tour 

 

 

http://repositoryhosting.com

Unlimited Repositories

Unlimited Trac Projects

Unlimited Users

Custom Domains

Agile Workflows

SSL Secured

WebDAV Shared Drives

Scheduled Backups

2GB Storage

$1/GB Additional Storage

 

 

 

 

 

 

 

 

 

 

 

支持svn,git,hg 这个站似乎很简单,靠谱么?

 

 

最后是 https://bitbucket.org

这个是免费的(5个合作者),之所以这么优惠的东东放在最后,是因为我之前用它还是蛮痛苦的,为什么呢,速度非常慢,提交个2k的东西要几分钟,甚至超时,连它的web界面也经常登陆不了。不过最近似乎有所改善,而且支持git,难道被github刺痛了?

 

这个还得保留观察一下。

Continue reading github 替代

heroku使用笔记

 

这两天将一个项目转到了heroku上面,哎呀我的个神,真心不好用:

 

Java:

https://devcenter.heroku.com/articles/java-support

默认不支持mysql而是支持protress,但是可用addon--cleardb来集成mysql,cleardb也有免费的级别。

但是即使是免费级别,必须要通过身份验证,也就是验证你的信用卡,否则无法添加addon。

 

http://www.ibm.com/developerworks/cn/java/j-javadev2-21/

maven不是必需的,可以直接发布war包 https://devcenter.heroku.com/articles/war-deployment

 

eclipse插件安装:

https://devcenter.heroku.com/articles/getting-started-with-heroku-eclipse

这个安装总是报错,尝试几次才成功,要看人品了。有了这个才差不多,否则那个控制台工具真不好用。

 

不存在永久的文件系统

https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem

http://stackoverflow.com/questions/12123050/no-permanent-filesystem-for-heroku

只能两个可写文件夹https://devcenter.heroku.com/articles/read-only-filesystem

./tmp ./log

 

servletContext.getRealPath();返回null问题,这是因为使用的webrunner跑的是未展开的war包。

解决办法是:

pom里面配置为展开war文件夹:

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.2</version>
				<executions>
				  <execution>
				    <phase>package</phase>
				    <goals><goal>exploded</goal></goals>
				    <configuration>
				      <webappDirectory>${build.directory}/webapp</webappDirectory>
				    </configuration>
				  </execution>
				</executions>				
			</plugin>

然后在Procfile里面指定webruner的目标(procfile是heroku添加参数的方法):

web: java $JAVA_OPTS -Dspring.profiles.active=prod -Dmidware.path.conf="distributed" -jar target/dependency/webapp-runner.jar --port $PORT target/webapp

 

这其实就是配置webrunner跑文件夹。

注:procfile的注释也是#号。

这样就可以得到webapp运行目录了,而且,此目录下的文件也是可写的!

但是要注意保持这个概念,都是临时性的文件系统,你用的时候要考虑这一点。

 

这里面讲了大部分问题:

https://devcenter.heroku.com/articles/java-faq

是直接发布war包还是用maven+嵌入的容器?

后者更容易发布,修改代码后直接构建测试,和heroku环境更接近。

而且源码方式更容易改配置,对于开发来说,每次改一个文件要传半天是很麻烦的。

项目目录下有个Procfile文件,heroku会依据其来启动,你可以修改它来添加系统参数。

https://devcenter.heroku.com/articles/procfile#deploying-to-heroku

本地运行可参见项目下的readme.md

例如:$java -jar target/dependency/webapp-runner.jar target/*.war

这个使用的jsimone项目,可在eclipse里面调试:

https://github.com/jsimone/webapp-runner

添加引用:

<dependency> 

 <groupId>com.github.jsimone</groupId> 

 <artifactId>webapp-runner</artifactId> 

 <version>7.0.34.1</version> 

 <scope>provided</scope> 

</dependency> 

运行配置:

Right-click on your project and choose 'Debug As -> Debug Configurations...'

From the 'Debug Configuration' window create a new 'Java Application' launch configuration by double-clicking on 'Java Application' in the left hand list or right-clicking on it and selecting 'New' 类型为java工程

Give your launch configuration a sensible name. Then enter the name of your project in the 'Project' box

Enter 'webapp.runner.launch.Main' in the 'Main Class' box 启动类为webapp.runner.launch.Main;--path /abc --port 10080 (--path是指context)

java -jar target/dependency/webapp-runner.jar --help 参见所有参数

Click on the 'Arguments' tab and enter './src/main/webapp' in the 'Program Arguments' box 添加程序参数./src/main/webapp

Click 'Apply' and then 'Run'

 

需要注意的几点:

1:tomcat7较高版本有自带的servletapi依赖,如果你配置了自己的servletapi则可能造成冲突,例如报错:

java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()Ljavax/servlet/SessionCookieConfig

说明你配置的servletapi版本太低。这就需要将你配置的servletapi去掉而依赖jsimone的包。在打包时这个不应该包含在包中,因为实际环境的容器都应该有自己的servlet包。

 

由于是分布式结构,不存在任何共享数据(数据库,memcache这些是另外的概念除外),所以实例之间的线程是互不可见的。

 

mysql:cleardb:

怎样查看信息,远程连接:

http://stackoverflow.com/questions/9822313/remote-connect-to-cleardb-heroku-database

运行heroku config --app 你app的名字就可看到信息。

通过此信息可以远程连接,例如heidisql可直接连,但是官方推荐的是用ssl连接:

https://devcenter.heroku.com/articles/cleardb#using-ssl-with-cleardb-and-rails

默认的cleardb信息是在环境变量里的,可以依据此变量来,

https://devcenter.heroku.com/articles/cleardb#using-cleardb-in-a-play-framework-app

当然你可以copy出变量信息直接用在程序中。

 

 

 

这里吐槽一下,那个自带的工具真是垃圾,不能粘贴,不能复制,有时还显示错行,powercommand里面怎么也运行不正确,所以还是eclipse插件靠谱。

例如查看程序信息,可得到cleardb的参数:

Snap1

 

可导入已存在项目,也可以创建项目,提交到git上后heroku会自动重启。

可直接通过console查看日志,很方便。

 

 

再来吐槽一下它的客服:

慢!问了个问题24小时之后才得到回复,而且由于是java相关问题,他又转到了编程人员,这样第三天才得到回复。

 

它的GIT经常访问不了,理由你懂得. http://ruby-china.org/topics/10813

    Continue reading heroku使用笔记

    Freemarker 笔记

    几点实用的:

     

    转义$的写法:

    <div class="span${'$'}{span}">

     

    则最终输出为

    <div class="dashboard-item span${span}">

     

     

    安全判断是否为空

    <#if (a.b.c.d)??>hi</#if>

    这样即使a都不存在也不会报错。

    Continue reading Freemarker 笔记

    【转】office 启动总是配置 解决办法

    (改注册表的办法是生效的,其他没试验,应该也有效)

    解决方法: 1. 注册  如若是Office 2003的对应问题,在运行对话框中输入: reg add HKCU\Software\Microsoft\Office\11.0\Word\Options /v NoReReg /t REG_DWORD /d 1 回车即可  若是Office 2007的对应问题,在运行对话框中输入: reg add HKCU\Software\Microsoft\Office\12.0\Word\Options /v NoReReg /t REG_DWORD /d 1 回车即可 

    2. 改文件,也最简单  打开“我的电脑”——“C盘”——“Program Files\Common Files\Microsoft Shared\OFFICE12\Office Setup Controller”——找到一个“SETUP.EXE”的文件,对此文件重命名  3. 改注册表  开始-》运行-》输入regedit-》打开注册列表,找到HKEY_LOCAL_MACHINE\SOFTWARE\Classes,右键—》权限—》高级,选下面第二张图中第二个选项CREATOR OWNER ,点编辑,在完全控制,允许那边打钩!确定!这时可能防火墙会跳出警告,你点放过,重启,OK!解决问题!

     

    注册表测试可用

    Continue reading 【转】office 启动总是配置 解决办法

    Soundmanager2 使用

    Soundmanager2目前比较流行,功能也比较强大。主页 http://www.schillmania.com/projects/soundmanager2/

     

    其文档很详细,主要需要两个文件soundmanager2-jsmin.js

    如果使用falsh则需要指明flash路径:

    soundManager.setup({
    	  url: '/js/soundmanager2/swf/'
    	});

    例如想播放某个声音:

    soundManager.onready(function() {
    					alertsms = soundManager.createSound({
    								id : 'alert', // required
    								url : '/assets/j/alert.mp3'
    							});
    				});

     

    这里创建了一个alertsms对象(Sound Object),那么可对此对象进行播放暂停等动作。

     

    另外一种用途是整个页面上的音乐资源自动渲染成播放器,这个在其文档中有demo:

    demo/mp3-player-button/index.html 就是比较酷的例子,基于html5:

    能显示音乐下载进度以及播放帧,使用也很简单,需要引用

    <link rel="stylesheet" type="text/css" href="360player.css" />

    <script type="text/javascript" src="script/berniecode-animator.js"></script>

    <script type="text/javascript" src="script/360player.js"></script>

    然后需要css对应的图片(在demo文件夹中)

     

    html代码中只需要这样的结构:

    <div class="ui360">
     <a href="/path/to/an.mp3">play "an.mp3"</a>
    </div>

    注意是以扩展名来分析类型的。

    脚本中仍然需要设置好soundmanager。

     

    这样就可以了,参看效果:

    Snap1

    Continue reading Soundmanager2 使用

    win7,windows was unable to connect to this network问题

    换了个联通的无线网络环境,ipod和android都可以连上去,win7找得到网络,但是点击连接后,密码都没让输就报错:windows was unable to connect to this network.

     

    这种问题可通过手动创建连接来解决:

    打开网络与共享中心/添加一连接或网络/手动连接到无线网络,然后输入连接信息,成功。

    Continue reading win7,windows was unable to connect to this network问题

    乐理基础

    电子琴资源:http://bbs.cndzq.com/forum.php

     

    大叔级菜鸟弹琴课程:http://www.pianohome.com

     

    这个课程不错,遇到不懂的问题可以看看:

    http://www.ccnt.com.cn/music/huod/yykt/ketang.htm?file=1

     

    当你觉得自己的手指太短了的时候,往往是你的指法有问题,快看曲谱,(说实话我的手指确实比较短)。

     

    和弦转位:根音与三音五音并不是上下关系,而是音程关系。

    Continue reading 乐理基础

    rapheal 与 d3.js 以及svg以及android 内置浏览器

    目前似乎d3比较火,细想一下,我觉得是其在android浏览器上兼容性上占优势,为什么呢?

     

    原来android4.0之前的内置浏览器居然不是chrome,他们只是共享了些代码,具体是叫什么内核,我是没找到。参见 https://developers.google.com/chrome/mobile/docs/overview

     

    而糟糕的是此浏览器居然不支持svg而只支持canvas。所以d3这方面就能兼容了。

     

    在ios 是safari,那是自然支持svg得了,参见这里可看到svg支持表:http://caniuse.com/#search=svg .

     

    那么你想找到一种支持所有主流浏览器的绘图方案还居然是不可能的,rapheal使用svg,vxml,但是它不能兼容andriod4.0之前的版本,你说这个android可恨不可恨。

     

     

     

    参见:

    http://coding.smashingmagazine.com/2012/02/22/web-drawing-throwdown-paper-processing-raphael/

    http://stackoverflow.com/questions/8985682/svg-support-on-android-current-status

    Continue reading rapheal 与 d3.js 以及svg以及android 内置浏览器

    Godaddy域名被误删的经历

    原来的域名ig2net.info和ig2net.com被我误删了! 其原因是godaddy那误导性的界面还有我的手贱!

     

    本来我是买了两年的一直到2015年,花了50刀,当看到信用卡上已经出了账单时我以为已经购买成功,因此为安全原因就想把我的信用卡信息从godaddy上删掉。删的过程中,godaddy提示有两个item被绑定(ig2net.info和ig2net.com),我当时想款都已经付了,应该没问题吧。结果就点了确认,居然没有出错,居然就这样删了。结果就是ig2net.info和ig2net.com域名被删。我的博客马上就访问不了。

     

    一身冷汗!赶紧找Godaddy客服,不好意思没有在线客服,除非你打电话直接联系,发了邮件说是18小时内回复,其实是第二天快24小时候才回个邮件说可以从domain可以恢复:

     

    1. Log in to your Account Manager. 登陆godaddy账户
    2. Next to Domains, click Launch. 点击Domains,点击Launch
    3. From the Domains menu, select Recover Expired Domains. 从Domains菜单中选择Recover Expired Domains
    4. Select the domain name(s) you want to recover. 选择你想恢复的域名
    5. Click Recover. 点击Recover
    6. Select the renewal length for the domain name(s), and then click Next.

      NOTE: Renewal lengths for multiple domain names can vary depending on maximum renewal lengths allowed by each domain name registry.

    7. (Optional) Select the registration and website options you want to apply to your domain name renewals.
    8. Click Next, and then complete the checkout process.

    一路下来居然要我300美刀,有没有搞错啊!什么附加项目也没选,居然要这么多。

    再联系客服,还是一天后回复:

     


    Days after expirationAction
    Day 1We make the first of three billing attempts to renew the domain name. If the billing fails, the domain name expires. The domain name can be renewed by the registrant at no extra cost.
    Day 5We make the second billing attempt. If the billing fails again, the domain name is parked. The domain name can still be renewed by the registrant at no extra cost.
    Day 12We make the third and final attempt to renew the domain name. The domain name can still be renewable by the registrant at no extra cost.
    Day 19
    The domain name can be renewed by the registrant for the cost of a one-year renewal plus an $80.00 redemption fee.
    Day 26We add the domain name to an expired domain name auction.
    Day 36The expired domain name auction ends. If there are no backorders and no bidders in the expired domain name auction, we list the domain name in a closeout auction.
    Day 41The closeout auction ends.
    Day 43We assign the domain name to the winner of the expired domain name auction, backorder, or closeout. If there are no bidders, we return the domain name to the registry.


    我还是不明白,明明还没过期,怎么就到Day19了?

    然后再看看Recovery,居然要我500刀,有没有搞错!

     

    看来恢复无望,看能不能把钱要回来。再问客服,还是一天后恢复:

    可以,要回复此邮件我们将退款给你。

     

    这还差不多,不至于域、财两空。

     

    只好再从name.com买了个域名kazge.com。

     

    godaddy用了几年了,界面真是烂,一点也不fashion. 客服反映慢,邮件都是一天后才回复,我明明是他给我邮件后马上回复的。

    不过它支持支付宝这点对国内来说还是比较方便的。

     

    name.com是别人推荐的,目前用起来还没什么问题。不过传说的whois免费是没碰到的。

     

    买域名要想能自动renew,那还得信用卡。否则,到时再续费就行了。

     

     

    换了域名,原来许多文章指向站内链接也需要换,这片文章介绍了个插件Velvet Blues Update URLs

    http://www.wpdaxue.com/wordpress-change-domain-update-urls.html

    还行,但是不能更新评论中的链接。

    Continue reading Godaddy域名被误删的经历

    Pagination


    Total views.

    © 2013 - 2022. All rights reserved.

    Powered by Hydejack v6.6.1