Python开发动态网页基础(三)

我们解释了如何建立一个 Django项目并启动 Django 开发服务器。当然,那个网站实际并没有干什么有用的事情,它所做的只是显示 It worked!消息。让我们来做些改变。本章将介绍如何使用 Django创建动态网页。

第一份视图:动态内容

我们的第一个目标是创建一个显示当前日期和时间的网页。这是一个不错的动态网页范例,因为该页面的内容不是静态的。相反,其内容是随着计算(本例中是对当前时间的计算)的结果而变化的。这个简单的范例既不涉及数据库,也不需要任何用户输入,仅输出服务器的内部时钟。

我们将编写一个视图函数以创建该页面。所谓的视图函数(或视图),只不过是一个接受 Web 请求并返回 Web 响应的 Python 函数。实际上,该响应可以是一份网页的 HTML内容、一次重定向、一条 404错误、一份 XML 文档、一幅图片,或其它任何东西。视图本身包含返回该响应所需的任意逻辑。该段代码可以随意放置,只要在 Python的路径设置中就可以了。没有其它要求——也可以说是没有任何奇特之处。为了给这些代码一个存身之处,让我们在上一章所创建的mysite目录中新建一份名为views.py的文件。


以下是一个以HTML 方式返回当前的日期与时间的视图 (view),:

from django.http import HttpResponse

import datetime

def current_datetime(request):

now = datetime.datetime.now()

html = "<html><body>It is now %s.</body></html>" % now

return HttpResponse(html)

我们逐行逐句地分析一遍这段代码:

首先,我们从django.http模块导入(importHttpResponse类。参阅附录 H 了解更多关于HttpRequestHttpResponse的细节。

然后我们从 Python标准库(Python 自带的实用模块集合)中导入(import)datetime模块。datetime模块包含几个处理日期和时间的函数(functions)和类(classes),其中就包括返回当前时间的函数。

接下来,我们定义了一个叫做current_datetime的函数。这就是所谓的视图函数(view function)。每个视图函数都以一个HttpRequest对象为第一个参数,该参数通常命名为request


注意视图函数的名称并不重要;并不一定非得以某种特定的方式命名才能让 Django识别它。此处,我们称之为current_datetime,只是因为该名字明确地指出了它的功能,而它也可以被命名为super_duper_awesome_current_time或者其它同样莫名其妙的名字。Django并不关心其名字。下一节将解释 Django如何查找该函数。

函数中的第一行代码计算当前日期和时间,并以datetime.datetime对象的形式保存为局部变量now

函数的第二行代码用 Python的格式化字符串(format-string)功能构造了一段 HTML响应。字符串里面的%s是占位符,字符串之后的百分号表示使用变量now的值替换%s(是的,这段 HTML 不合法,但我们只不过是想让范例尽量保持简短而已。)

最后,该视图返回一个包含所生成响应的HttpResponse对象。每个视图函数都负责返回一个HttpResponse对象。(也有例外,但是我们稍后才会接触到。)

Django 时区 (Time Zone)

Django 包含一个默认为America/ChicagoTIME_ZONE设置。这可能不是你所居住的时区,因此你可以在settings.py文件中修改它。请参阅附录 E 了解更多细节。

URL映射到视图

那么概括起来,该视图函数返回了包含当前日期和时间的一段 HTML页面。但是如何告诉 Django使用这段代码呢?这就是URLconfs粉墨登场的地方了。

URLconf就像是 Django所支撑网站的目录。它的本质是 URL模式以及要为该 URL 模式调用的视图函数之间的映射表。你就是以这种方式告诉 Django,对于这个 URL调用这段代码,对于那个 URL调用那段代码。但必须记住的是视图函数必须位于 Python搜索路径之中。

Python 搜索路径

Python 搜索路径就是使用import语句时,Python所查找的系统目录清单。

举例来说,假定你将 Python路径设置为['','/usr/lib/python2.4/site-packages','/home/username/djcode/']。如果执行代码from foo import barPython将会首先在当前目录查找foo.py模块( Python路径第一项的空字符串表示当前目录)。如果文件不存在,Python将查找/usr/lib/python2.4/site-packages/foo.py文件。如果文件也不存在,它将尝试/home/username/djcode/foo.py。最后,如果这个文件还不存在,它将引发ImportError异常。

如果对了解 Python搜索路径值感兴趣,可以启动 Python交互式解释程序,输入import sys,接着输入print sys.path

通常,你不必关心 Python搜索路径的设置。Python Django 会在后台自动帮你处理好。(如果有兴趣了解的话,Python搜索路径的设置工作是manage.py文件的职能之一。)

前一章中执行django-admin.py startproject时,该脚本会自动为你建了一份 URLconf(即urls.py文件)。让我们编辑一下这份文件。缺省情况下它是下面这个样子:

from django.conf.urls.defaults import *

urlpatterns = patterns('',

# Example:

# (r'^mysite/', include('mysite.apps.foo.urls.foo')),

# Uncomment this for admin:

# (r'^admin/', include('django.contrib.admin.urls')),

)

让我们逐行逐句分析一遍这段代码:

§ 第一行从django.conf.urls.defaults模块引入了所有的对象,其中包括了叫做patterns的函数。

§ 第二行调用patterns()函数并将返回结果保存到urlpatterns变量。patterns()函数只传入了一个空字符串参数。其他代码行都被注释掉了。 (该字符串可用作视图函数的通用前缀,但目前我们将略过这种高级用法。)

当前应该注意是urlpatterns变量, Django期望能从ROOT_URLCONF模块中找到它。该变量定义了 URL以及用于处理这些 URL 的代码之间的映射关系。

默认情况下,URLconf所有内容都被注释起来了——Django应用程序还是白版一块。(旁注:这也就是上一章中 Django显示“It worked!”页面的原因。如果 URLconf为空,Django 会认定你才创建好新项目,因此也就显示那种信息。)

现在编辑该文件以展示我们的current_datetime视图:

from django.conf.urls.defaults import *

from mysite.views import current_datetime

urlpatterns = patterns('',

(r'^time/$', current_datetime),

)

我们做了两处修改。首先,我们从模块 ( Python import 语法中,mysite/views.py转译为mysite.views)中引入了current_datetime视图。接着,我们加入了(r'^time/$', current_datetime),这一行。该行就是所谓的URLpattern ,它是一个 Python元组,其第一个元素是简单的正则表达式,第二个元素是为该模式应用的视图函数。

简单来说,我们只是告诉 Django,所有指向 URL/time/的请求都应由current_datetime这个视图函数来处理。

下面是一些需要注意的地方:

注意,该例中,我们将current_datetime视图函数作为对象传递,而不是调用它。这是Python (及其它动态语言的)的一个重要特性:函数是一级对象(first-class objects),也就是说你可以像传递其它变量一样传递它们。很酷吧?

r'^time/$'中的r表示'^time/$'是一个原始字符串。这样一来就可以避免正则表达式有过多的转义字符。

不必在'^time/$'前加斜杠(/)来匹配/time/因为 Django 会自动在每个表达式前添加一个斜杠。乍看起来,这好像有点奇怪,但是 URLconfs可能由其它的 URLconfs所引用, 所以不加前面的斜杠可让事情简单一些。这一点在第 8章中将有进一步阐述。

上箭头^和美元符号$符号非常重要。上箭头要求表达式对字符串的头部进行匹配,美元符号则要求表达式对字符串的尾部进行匹配。

最好还是用范例来说明一下这个概念。如果我们用'^time/'(结尾没有$),那么以time/开始的任意URL都会匹配,比如/time/foo/time/bar不仅仅是/time/。同样的,如果我们去掉最前面的 ^ ('time/$'), Django一样会匹配由time/结束的任意URL/time/,比如/foo/bar/time/因此,我们必须同时用上 ^ $ 来精确匹配 URL/time/。不能多也不能少。

你可能想如果有人请求/time也可以同样处理。如果APPEND_SLASH设置是True的话,系统会重定向到/time/,这样就可以一样处理了。 (有关内容请查看附录 E )

启动Django开发服务器来测试修改好的 URLconf,运行命令行python manage.py runserver (如果你让它一直运行也可以,开发服务器会自动监测代码改动并自动重新载入,所以不需要手工重启)开发服务器的地址是http://127.0.0.1:8000/,打开你的浏览器访问http://127.0.0.1:8000/time/你就可以看到输出结果了。

万岁!你已经创建了第一个Djangoweb页面。

正则表达式

正则表达式(regexes ) 是通用的文本模式匹配的方法。Django URLconfs允许你使用任意的正则表达式来做强有力的URL映射,不过通常你实际上可能只需要使用很少的一部分功能。下面就是一些常用通用模式:

符号

匹配

. (dot)

任意字符

\d

任意数字

[A-Z]

任意字符, A-Z (大写)

[a-z]

任意字符, a-z (小写)

[A-Za-z]

任意字符, a-z (不区分大小写)

+

匹配一个或更多 (例如, \d+匹配一个或 多个数字字符)

[^/]+

不是/的任意字符

*

匹配0个或更多 (例如, \d*匹配0个 或更多数字字符)

{1,3}

匹配1个到3个(包含)2

有关正则表达式的更多内容,请访问http://www.djangoproject.com/r/python/re-module/.

Django是怎么处理请求的

我们必须对刚才所发生的几件事情进行一些说明。它们是运行Django开发服务器和构造Web页面请求的本质所在。

命令python manage.py runserver从同一目录载入文件settings.py该文件包含了这个特定的Django实例所有的各种可选配置,其中一个最重要的配置就是ROOT_URLCONFROOT_URLCONF告诉Django哪个Python模块应该用作本网站的 URLconf

还记得django-admin.py startproject创建的文件settings.pyurls.py吗?这时系统自动生成的settings.pyROOT_URLCONF默认设置是urls.py

当访问 URL/time/时,Django根据ROOT_URLCONF的设置装载 URLconf然后按顺序逐个匹配URLconf里的URLpatterns,直到找到一个匹配的。当找到这个匹配URLpatterns就调用相关联的view函数,并把HttpRequest对象作为第一个参数。(稍后再给出HttpRequest的更多信息)

view函数负责返回一个HttpResponse对象。

你现在知道了怎么做一个 Django-powered页面了,真的很简单,只需要写视图函数并用 URLconfs把它们和URLs对应起来。你可能会认为用一系列正则表达式将URLs映射到函数也许会比较慢,但事实却会让你惊讶。

Django如何处理请求:完整细节

除了刚才所说到的简明URL-to-view映射方式之外,Django在请求处理方面提供了大量的灵活性。

通过 URLconf解析到哪个视图函数来返回HttpResponse可以通过中间件(middleware)来短路或者增强。关于中间件的细节将在第十五章详细谈论,这里给出3-1让你先了解大体概念.


3-1Django请求回应流程

当服务器收到一个HTTP请求以后,一个服务器特定的handler 会创建HttpRequest并传递给下一个组件并处理。

这个 handler然后调用所有可用的Request或者View中间件。这些类型的中间件通常是用来增强HttpRequest对象来对一些特别类型的request做些特别处理。只要其中有一个返回HttpResponse,系统就跳过对视图的处理。


即便是最棒的程序员也会有出错的时候,这个时候异常处理中间件(exception middleware可以帮你的大忙。如果一个视图函数抛出异常,控制器会传递给异常处理中间件处理。如果这个中间件没有返回HttpResponse,意味着它不能处理这个异常,这个异常将会再次抛出。

即便是这样,你也不用担心。Django包含缺省的视图来生成友好的404 500 回应(response)。

最后, response middleware 做发送HttpResponse给浏览器之前的后处理或者清除请求用到的相关资源。

URL配置和松耦合

现在是好时机来指出DjangoURL配置背后的哲学:松耦合原则。简单的说,松耦合是一个重要的保证互换性的一个软件开发方法。如果两段代码是松耦合的,那么改动其中一段代码不会影响另一段代码,或者只有很少的一点影响。

DjangoURL配置就是一个很好的例子。在Django的应用程序中,URL的定义和视图函数之间是松耦合的,换句话说,决定URL返回哪个视图函数和实现这个视图函数是在两个不同的地方。这使得开发人员可以修改一块而不会影响另一块。

相比之下,其他的Web开发平台紧耦合和URL到代码中。在典型的PHP (http://www.php.net/)应用,URL的设计是通过放置代码的目录来实现。在早期的 CherryPy Python Web framework (http://www.cherrypy.org/)中,URL对应处理的的方法名。这可能在短期看起来是便利之举,但是长期会带来难维护的问题。

比方说,考虑有一个以前写的视图函数,这个函数显示当前日期和时间。如果我们想把它的URL从原来的/time/改变到/currenttime/,我们只需要快速的修改一下URL配置即可,不用担心这个函数的内部实现。同样的,如果我们想要修改这个函数的内部实现也不用担心会影响到对应的URL。此外,如果我们想要输出这个函数到一些URL我们只需要修改URL配置而不用去改动视图的代码。

Django大量应用松耦合。我们将在本书中继续给出这一重要哲学的相关例子。

404 错误

在我们当前的这个URL配置中,我们之定义了一个URL模式:处理URL/time/当请求其他URL会怎么样呢?

让我们试试看,运行Django开发服务器并访问类似http://127.0.0.1:8000/hello/或者http://127.0.0.1:8000/does-not-exist/,甚至http://127.0.0.1:8000/(网站根目录)。你将会看到一个 “Page not found” 页面(图 32)。(挺漂亮的,是吧?你会喜欢上我们的配色方案的;-)如果请求的URL没有在URL配置里设置,Django就会显示这个页面。

3-2. Django 404页面

这个页面的功能不只是显示404的基本错误信息,它同样精确的告诉你Django使用了哪个URL配置和这个配置里的每一个模式。这样,你应该能了解到为什么这个请求会抛出404错误。

当然,这些敏感的信息应该只呈现给你-开发者。如果是部署到了因特网上的站点就不应该暴露这些信息。出于这个考虑,这个“Page not found”页面只会在调试模式(debug mode显示。我们将在以后说明怎么关闭调试模式。现在,你只需要知道所有的Django项目在创建后都是在调试模式下的,如果关闭了调试模式,结果将会不一样。

第二个视图:动态URL

在我们的第一个视图范例中,尽管内容是动态的,但是URL/time/)是静态的。在大多数动态web应用程序,URL通常都包含有相关的参数。

让我们创建第二个视图来显示当前时间和加上时间偏差量的时间,设计是这样的:/time/plus/1/显示当前时间+1个小时的页面/time/plus/2/显示当前时间+2个小时的页面/time/plus/3/显示当前时间+3个小时的页面,以此类推。

新手可能会考虑写不同的视图函数来处理每个时间偏差量,URL配置看起来就象这样:

urlpatterns = patterns('',

(r'^time/$', current_datetime),

(r'^time/plus/1/$', one_hour_ahead),

(r'^time/plus/2/$', two_hours_ahead),

(r'^time/plus/3/$', three_hours_ahead),

(r'^time/plus/4//$', four_hours_ahead),

)


很明显,这样处理是不太妥当的。不但有很多冗余的视图函数,而且整个应用也被限制了只支持预先定义好的时间段,2小时,3小时,或者4小时。如果哪天我们要实现5 小时,我们就不得不再单独创建新的视图函数和配置URL,既重复又混乱。我们需要在这里做一点抽象,提取一些共同的东西出来。

关于漂亮URL的一点建议

如果你有其他Web开发平台的经验,例如PHP或者JAVA,你可能会想,好吧,让我们来用一个查询字符串参数来表示它们吧,例如/time/plus?hours=3,哪个时间段用hours参数代表,URL的查询字符串(query string)URL?后面的字符串。

可以Django里也这样做 (如果你真的想要这样做,我们稍后会告诉你怎么做),但是Django的一个核心理念就是URL必须看起来漂亮。URL/time/plus/3/更加清晰,更简单,也更有可读性,可以很容易的大声念出来,因为它是纯文本,没有查询字符串那么复杂。漂亮的URL就像是高质量的Web应用的一个标志。

DjangoURL配置系统可以使你很容易的设置漂亮的URL,而尽量不要考虑它的反面


带通配符的URL匹配模式

继续我们的hours_ahead范例,让我们在URL模式里使用通配符。我们前面讲到,URL模式是一个正则表达式,因此,我们可以使用正则表达式模式\d+来匹配一个或多个数字:

from django.conf.urls.defaults import *

from mysite.views import current_datetime, hours_ahead

urlpatterns = patterns('',

(r'^time/$', current_datetime),

(r'^time/plus/\d+/$', hours_ahead),

)

这个URL模式将匹配类似/time/plus/2/,/time/plus/25/,甚至/time/plus/100000000000/的任何URL。更进一步,让我们把它限制在最大允许99个小时,这样我们就只允许一个或两个数字,正则表达式的语法就是\d{1,2}:

(r'^time/plus/\d{1,2}/$', hours_ahead),

备注

在建造Web应用的时候,尽可能多考虑可能的数据输入是很重要的,然后决定哪些我们可以接受。在这里我们就设置了99个小时的时间段限制。

现在我们已经设计了一个带通配符的URL,我们需要一个方法把它传递到视图函数里去,这样我们只用一个视图函数就可以处理所有的时间段了。我们使用圆括号把参数在URL模式里标识出来。在这个例子中,我们想要把这些数字作为参数,用圆括号把\d{1,2}包围起来:

(r'^time/plus/(\d{1,2})/$', hours_ahead),

如果你熟悉正则表达式,那么你应该已经了解,正则表达式也是用圆括号来从文本里提取数据的。


最终的current_datetimeURLconf,包含我们前面的视图,看起来像这样:

from django.conf.urls.defaults import *

from mysite.views import current_datetime, hours_ahead

urlpatterns = patterns('',

(r'^time/$', current_datetime),

(r'^time/plus/(\d{1,2})/$', hours_ahead),

)

现在开始写hours_ahead视图。

编码次序

这个例子中,我们先写了URLpattern,然后是视图,但是在前面的例子中,我们先写了视图,然后是URLpattern。哪种技术更好?嗯,怎么说呢,每个开发者是不一样的。

如果你是喜欢从总体上来把握事物(注:或译为大局观)类型的人,你应该会想在项目开始的时候就写下所有的URL配置。这会给你带来一些好处,例如,给你一个清晰的to-do列表,让你更好的定义视图所需的参数。

如果你从更像是一个自底向上的开发者,你可能更喜欢先写视图,然后把它们挂接到URL上。这同样是可以的。

最后,取决与你喜欢那种技术,两种方法都是可以的。

hours_ahead和我们以前写的current_datetime很象,关键的区别在于:它多了一个额外参数,时间差。views.py修改如下:

def hours_ahead(request, offset):

offset = int(offset)

dt = datetime.datetime.now() + datetime.timedelta(hours=offset)

html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)

return HttpResponse(html)


让我们一次一行通一下这些代码:

就像我们在我们的current_datetime视图中所作的一样,我们导入django.http.HttpResponse类和datetime模块。

视图函数,hours_ahead,两个参数:requestoffset.

request是一个HttpRequest对象,就像在current_datetime中一样.再说一次好了: 每一个视图总是以一个HttpRequest对象作为它的第一个参数。


offset是从匹配的URL里提取出来的。例如:如果URL/time/plus/3/那么offset是字符串'3'如果URL/time/plus/21/,那么offset是字符串'21'注意,提取的字符串总是字符串,不是整数,即便都是数字组成,就象'21'

在这里我们命名变量为offset,你也可以任意命名它,只要符合Python的语法。变量名是无关紧要的,重要的是它的位置,它是这个函数的第二个参数 (request的后面)。你还可以使用关键字来定义它,而不是用位置。详情请看第八章。

我们在这个函数中要做的第一件事情就是在offset上调用int().这会把这个字符串值转换为整数。

注意Python可能会在你调用int()来转换一个不能转换成整数时抛出ValueError异常,例如字符串'foo'当然,在这个范例中我们不用担心这个问题,因为我们已经确定offset只包含数字字符的字符串。因为正则表达式(\d{1,2})只提取数字字符。这也是URL配置的另一个好处:提供了清晰的输入数据有效性确认。

下一行显示了我们为什么调用int()来转换offset这一行我们要计算当前时间加上这个时间差offset小时,保存结果到变量dtdatetime.timedelta函数的参数hours必须是整数类型。

这行和前面的那行的的一个微小差别就是,它使用带有两个值的Python的格式化字符串功能,而不仅仅是一个值。因此,在字符串中有两个%s符号和一个以进行插入的值的元组:(offset, dt)

最后,我们再一次返回一个HTMLHttpResponse,就像我们在current_datetime做的一样。

在完成视图函数和URL配置编写后,启动Django开发服务器,用浏览器访问http://127.0.0.1:8000/time/plus/3/来确认它工作正常。然后是http://127.0.0.1:8000/time/plus/5/。再然后是http://127.0.0.1:8000/time/plus/24/。最后,访问http://127.0.0.1:8000/time/plus/100/来检验URL配置里设置的模式是否只接受一个或两个数字;Django会显示一个 Page not found error 页面,和以前看到的 404 错误一样。访问URLhttp://127.0.0.1:8000/time/plus/(没有定义时间差)也会抛出404错误。

你现在已经注意到views.py文件中包含了两个视图,views.py看起来象这样:

from django.http import HttpResponse

import datetime

def current_datetime(request):

now = datetime.datetime.now()

html = "<html><body>It is now %s.</body></html>" % now

return HttpResponse(html)

def hours_ahead(request, offset):

offset = int(offset)

dt = datetime.datetime.now() + datetime.timedelta(hours=offset)

html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)

return HttpResponse(html)

Django 漂亮的出错页面

花几分钟时间欣赏一下我们写好的Web应用程序,然后我们再来搞点小破坏。我们故意在views.py文件中引入一项 Python 错误,注释掉hours_ahead视图中的offset = int(offset)一行。

def hours_ahead(request, offset):

#offset = int(offset)

dt = datetime.datetime.now() + datetime.timedelta(hours=offset)

html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)

return HttpResponse(html)


启动开发服务器,然后访问/time/plus/3/。你会看到一个包含大量信息的出错页,最上面的一条TypeError信息是:"unsupported type for timedelta hours component: str"

怎么回事呢?是的,datetime.timedelta函数要求hours参数必须为整型,而我们注释掉了将offset转为整型的代码。这样导致datetime.timedelta弹出TypeError异常。这是所有程序员某个时候都可能碰到的一种典型错误。

这个例子是为了展示 Django的出错页面。我们来花些时间看一看这个出错页,了解一下其中给出了哪些信息。

以下是值得注意的一些要点:

在页面顶部,你可以得到关键的异常信息:异常数据类型、异常的参数 (如本例中的"unsupported type")、在哪个文件中引发了异常、出错的行号等等。

在关键异常信息下方,该页面显示了对该异常的完整 Python追踪信息。这类似于你在 Python命令行解释器中获得的追溯信息,只不过后者更具交互性。对栈中的每一帧,Django均显示了其文件名、函数或方法名、行号及该行源代码。

点击该行代码 (以深灰色显示),你可以看到出错行的前后几行,从而得知相关上下文情况。

点击栈中的任何一帧的“Local vars”可以看到一个所有局部变量的列表,以及在出错那一帧时它们的值。这些调试信息是无价的。

注意“Traceback”下面的“Switch to copy-and-paste view”文字。点击这些字,追溯会切换另一个视图,它让你很容易地复制和粘贴这些内容。当你想同其他人分享这些异常追溯以获得技术支持时(比如在 Django IRC聊天室或邮件列表中),可以使用它。

接下来的“Request information”部分包含了有关产生错误的 Web请求的大量信息: GET POSTcookie值、元数据(象 CGI头)。在附录H里给出了request的对象的完整参考。

Request信息的下面,“Settings”列出了 Django使用的具体配置信息。同样在附录E给出了settings配置的完整参考。现在,大概浏览一下,对它们有个大致印象就好了。

Django 的出错页某些情况下有能力显示更多的信息,比如模板语法错误。我们讨论 Django模板系统时再说它们。现在,取消offset = int(offset)这行的注释,让它重新正常工作。

不知道你是不是那种使用小心放置的print语句来帮助调试的程序员?你其实可以用 Django出错页来做这些,而不用print语句。在你视图的任何位置,临时插入一个assert False来触发出错页。然后,你就可以看到局部变量和程序语句了。(还有更高级的办法来调试 Django 视图,我们后来再说,但这个是最快捷最简单的办法了。)


最后,很显然这些信息很多是敏感的,它暴露了你 Python代码的内部结构以及 Django配置,在 Internet 上公开这信息是很愚蠢的。不怀好意的人会尝试使用它攻击你的 Web应用程序,做些下流之事。因此,Django出错信息仅在 debug 模式下才会显现。我们稍后说明如何禁用 debug模式。现在,你只要知道 Django服务器在你开启它时默认运行在 debug模式就行了。(听起来很熟悉?“Page not found”错误,“404错误一节也这样描述过。)


转载请注明文章出处:http://blog.csdn.net/wolaiye320/article/details/51819151

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值