web 修改form表单并保存历史 - CSDN
精华内容
参与话题
  • 这页文档简单介绍Web 表单的基本概念和它们在Django 中是如何处理的。关于表单API 某方面的细节,请参见表单 API、表单的字段和表单和字段的检验。 除非你计划构建的网站和应用只是发布内容而不接受访问者的输入,...

    使用表单

    关于这页文档

    这页文档简单介绍Web 表单的基本概念和它们在Django 中是如何处理的。关于表单API 某方面的细节,请参见表单 API、表单的字段和表单和字段的检验

    除非你计划构建的网站和应用只是发布内容而不接受访问者的输入,否则你将需要理解并使用表单。

    Django 提供广泛的工具和库来帮助你构建表单来接收网站访问者的输入,然后处理以及响应输入。

    HTML 表单

    在HTML中,表单是位于<form>...</form> 之间的元素的集合,它们允许访问者输入文本、选择选项、操作对象和控制等等,然后将信息发送回服务器。

    某些表单的元素 —— 文本输入和复选框 —— 非常简单而且内建于HTML 本身。其它的表单会复杂些;例如弹出一个日期选择对话框的界面、允许你移动滚动条的界面、使用JavaScript 和CSS 以及HTML 表单<input> 元素来实现操作控制的界面。

    <input> 元素一样,一个表单必须指定两样东西:

    • where:响应用户输入的URL
    • how:HTTP 方法

    例如,Django Admin 站点的登录表单包含几个<input> 元素:type="text" 用于用户名,type="password" 用于密码,type="submit" 用于“Log in” 按钮。它还包含一些用户看不到的隐藏的文本字段,Django 使用它们来决定下一步的行为。

    它还告诉浏览器表单数据应该发往<form>action 属性指定的URL —— /admin/,而且应该使用method 属性指定的HTTP 方法 —— post

    当触发<input type="submit" value="Log in"> 元素时,数据将发送给/admin/

    GET 和 POST

    处理表单时候只会用到GETPOST 方法。

    Django 的登录表单使用POST 方法,在这个方法中浏览器组合表单数据、对它们进行编码以用于传输、将它们发送到服务器然后接收它的响应。

    相反,GET 组合提交的数据为一个字符串,然后使用它来生成一个URL。这个URL 将包含数据发送的地址以及数据的键和值。如果你在Django 文档中做一次搜索,你会立即看到这点,此时将生成一个https://docs.djangoproject.com/search/?q=forms&release=1 形式的URL。

    GETPOST 用于不同的目的。

    用于改变系统状态的请求 —— 例如,给数据库带来变化的请求 —— 应该使用POSTGET 只应该用于不会影响系统状态的请求。

    GET 还不适合密码表单,因为密码将出现在URL 中,以及浏览器的历史和服务器的日志中,而且都是以普通的文本格式。它还不适合数据量大的表单和二进制数据,例如一张图片。使用GET 请求作为管理站点的表单具有安全隐患:攻击者很容易模拟表单请求来取得系统的敏感数据。POST,如果与其它的保护措施结合将对访问提供更多的控制,例如Django 的CSRF 保护

    另一个方面,GET 适合网页搜索这样的表单,因为这种表示一个GET 请求的URL 可以很容易地作为书签、分享和重新提交。

    Django 在表单中的角色

    处理表单是一件很复杂的事情。考虑一下Django 的Admin 站点,不同类型的大量数据项需要在一个表单中准备好、渲染成HTML、使用一个方便的界面编辑、返回给服务器、验证并清除,然后保存或者向后继续处理。

    Django 的表单功能可以简化并自动化大部分这些工作,而且还可以比大部分程序员自己所编写的代码更安全。

    Django 会处理表单工作中的三个显著不同的部分:

    • 准备并重新构造数据
    • 为数据创建HTML 表单
    • 接收并处理客户端提交的表单和数据

    可以手工编写代码来实现,但是Django 可以帮你完成所有这些工作。

    Django 中的表单

    我们已经简短讲述HTML 表单,但是HTML的<form> 只是其机制的一部分。

    在一个Web 应用中,‘表单’可能指HTML <form>、或者生成它的Django 的Form、或者提交时发送的结构化数据、或者这些部分的总和。

    Django 的Form 类

    表单系统的核心部分是Django 的Form 类。Django 的模型描述一个对象的逻辑结构、行为以及展现给我们的方式,与此类似,Form 类描述一个表单并决定它如何工作和展现。

    模型类的字典映射到数据库的字典,与此类似,表单类的字段映射到HTML 的表单<input> 元素。(ModelForm通过一个Form 映射模型类的字段到HTML 表单的<input>元素;Django 的Admin 站点就是基于这个)。

    表单的字段本身也是类;它们管理表单的数据并在表单提交时进行验证。DateFieldFileField处理的数据类型差别很大,必须完成不同的事情。

    表单字段在浏览器中呈现给用户的是一个HTML 的“widget” —— 用户界面的一个片段。每个字段类型都有一个合适的默认Widget 类,需要时可以覆盖。

    实例化、处理和渲染表单

    在Django 中渲染一个对象时,我们通常:

    1. 在视图中获得它(例如,从数据库中获取)
    2. 将它传递给模板上下文
    3. 使用模板变量将它扩展为HTML 标记

    在模板中渲染表单和渲染其它类型的对象几乎一样,除了几个关键的差别。

    在模型实例不包含数据的情况下,在模板中对它做处理很少有什么用处。但是渲染一个未填充的表单却非常有意义 —— 我们希望用户去填充它。

    所以当我们在视图中处理模型实例时,我们一般从数据库中获取它。当我们处理表单时,我们一般在视图中实例化它。

    当我们实例化表单时,我们可以选择让它为空还是预先填充它,例如使用:

    • 来自一个保存后的模型实例的数据(例如用于编辑的管理表单)
    • 我们从其它地方获得的数据
    • 从前面一个HTML 表单提交过来的数据

    最后一种情况最令人关注,因为它使得用户可以不只是阅读一个网站,而且可以给网站返回信息。

    构建一个表单

    需要完成的工作

    假设你想在你的网站上创建一个简单的表单,以获得用户的名字。你需要类似这样的模板:

    <form action="/your-name/" method="post">
        <label for="your_name">Your name: </label>
        <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
        <input type="submit" value="OK">
    </form>

    这告诉浏览器发送表单的数据到URL /your-name/,并使用POST 方法。它将显示一个标签为”Your name:”的文本字段,和一个”OK”按钮。如果模板上下文包含一个current_name 变量,它将用于预填充your_name 字段。

    你将需要一个视图来渲染这个包含HTML 表单的模板,并提供合适的current_name 字段。

    当表单提交时,发往服务器的POST 请求将包含表单数据。

    现在你还需要一个对应/your-name/ URL 的视图,它在请求中找到正确的键/值对,然后处理它们。

    这是一个非常简单的表单。实际应用中,一个表单可能包含几十上百个字段,其中大部分需要预填充,而且我们预料到用户将来回编辑-提交几次才能完成操作。

    我们可能需要在表单提交之前,在浏览器端作一些验证。我们可能想使用非常复杂的字段,以允许用户做类似从日历中挑选日期这样的事情,等等。

    这个时候,让Django 来为我们完成大部分工作是很容易的。

    在Django 中构建一个表单

    Form 类

    我们已经计划好了我们的 HTML 表单应该呈现的样子。在Django 中,我们的起始点是这里:

    #forms.py
    
    from django import forms
    
    class NameForm(forms.Form):
        your_name = forms.CharField(label='Your name', max_length=100)

    它定义一个Form 类,只带有一个字段(your_name)。我们已经对这个字段使用一个友好的标签,当渲染时它将出现在<label> 中(在这个例子中,即使我们省略它,我们指定的label还是会自动生成)。

    字段允许的最大长度通过max_length 定义。它完成两件事情。首先,它在HTML 的<input> 上放置一个maxlength="100" (这样浏览器将在第一时间阻止用户输入多于这个数目的字符)。它还意味着当Django 收到浏览器发送过来的表单时,它将验证数据的长度。

    Form 的实例具有一个is_valid() 方法,它为所有的字段运行验证的程序。当调用这个方法时,如果所有的字段都包含合法的数据,它将:

    • 返回True
    • 将表单的数据放到cleaned_data属性中。

    完整的表单,第一次渲染时,看上去将像:

    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" maxlength="100">

    注意它不包含 <form> 标签和提交按钮。我们必须自己在模板中提供它们。

    视图

    发送给Django 网站的表单数据通过一个视图处理,一般和发布这个表单的是同一个视图。这允许我们重用一些相同的逻辑。

    当处理表单时,我们需要在视图中实例化它:

    #views.py
    
    from django.shortcuts import render
    from django.http import HttpResponseRedirect
    
    from .forms import NameForm
    
    def get_name(request):
        # if this is a POST request we need to process the form data
        if request.method == 'POST':
            # create a form instance and populate it with data from the request:
            form = NameForm(request.POST)
            # check whether it's valid:
            if form.is_valid():
                # process the data in form.cleaned_data as required
                # ...
                # redirect to a new URL:
                return HttpResponseRedirect('/thanks/')
    
        # if a GET (or any other method) we'll create a blank form
        else:
            form = NameForm()
    
        return render(request, 'name.html', {'form': form})

    如果访问视图的是一个GET 请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一个访问该URL 时预期发生的情况。

    如果表单的提交使用POST 请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它:form = NameForm(request.POST)。这叫做”绑定数据至表单“(它现在是一个绑定的表单)。

    我们调用表单的is_valid()方法;如果它不为True,我们将带着这个表单返回到模板。这时表单不再为空(未绑定),所以HTML 表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。

    如果is_valid()True,我们将能够在cleaned_data 属性中找到所有合法的表单数据。在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。

    模板

    我们不需要在name.html 模板中做很多工作。最简单的例子是:

    <form action="/your-name/" method="post">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="Submit" />
    </form>

    根据{{ form }},所有的表单字段和它们的属性将通过Django 的模板语言拆分成HTML 标记 。

    表单和跨站请求伪造的防护

    Django 原生支持一个简单易用的跨站请求伪造的防护。当提交一个启用CSRF 防护的POST 表单时,你必须使用上面例子中的csrf_token 模板标签。然而,因为CSRF 防护在模板中不是与表单直接捆绑在一起的,这个标签在这篇文档的以下示例中将省略。

    HTML5 输入类型和浏览器验证

    如果你的表单包含URLFieldEmailField 和其它整数字段类似,Django 将使用urlemailnumber 这样的HTML5 输入类型。默认情况下,浏览器可能会对这些字段进行它们自身的验证,这些验证可能比Django 的验证更严格。如果你想禁用这个行为,请设置form 标签的novalidate 属性,或者指定一个不同的字段,如TextInput

    现在我们有了一个可以工作的网页表单,它通过Django Form 描述、通过视图处理并渲染成一个HTML <form>

    这是你入门所需要知道的所有内容,但是表单框架为了提供了更多的内容。一旦你理解了上面描述的基本处理过程,你应该可以理解表单系统的其它功能并准备好学习更多的底层机制。

    Django Form 类详解

    所有的表单类都作为django.forms.Form 的子类创建,包括你在Django 管理站点中遇到的ModelForm

    模型和表单

    实际上,如果你的表单打算直接用来添加和编辑Django 的模型,ModelForm 可以节省你的许多时间、精力和代码,因为它将根据Model 类构建一个表单以及适当的字段和属性。

    绑定的和未绑定的表单实例

    绑定的和未绑定的表单 之间的区别非常重要:

    • 未绑定的表单没有关联的数据。当渲染给用户时,它将为空或包含默认的值。
    • 绑定的表单具有提交的数据,因此可以用来检验数据是否合法。如果渲染一个不合法的绑定的表单,它将包含内联的错误信息,告诉用户如何纠正数据。

    表单的is_bound 属性将告诉你一个表单是否具有绑定的数据。

    字段详解

    考虑一个比上面的迷你示例更有用的一个表单,我们可以用它来在一个个人网站上实现“联系我”功能:

    #forms.py
    
    from django import forms
    
    class ContactForm(forms.Form):
        subject = forms.CharField(max_length=100)
        message = forms.CharField(widget=forms.Textarea)
        sender = forms.EmailField()
        cc_myself = forms.BooleanField(required=False)

    我们前面的表单只使用一个字段your_name,它是一个CharField。在这个例子中,我们的表单具有四个字段:subjectmessagesendercc_myself。共用到三种字段类型:CharFieldEmailFieldBooleanField;完整的字段类型列表可以在表单字段中找到。

    Widgets

    每个表单字段都有一个对应的Widget 类,它对应一个HTML 表单Widget,例如<input type="text">

    在大部分情况下,字段都具有一个合理的默认Widget。例如,默认情况下,CharField 具有一个TextInput Widget,它在HTML 中生成一个<input type="text">。如果你需要<textarea>,在定义表单字段时你应该指定一个合适的Widget,例如我们定义的message 字段。

    字段的数据

    不管表单提交的是什么数据,一旦通过调用is_valid() 成功验证(is_valid() 返回True),验证后的表单数据将位于form.cleaned_data 字典中。这些数据已经为你转换好为Python 的类型。

    此时,你依然可以从request.POST 中直接访问到未验证的数据,但是访问验证后的数据更好一些。

    在上面的联系表单示例中,cc_myself 将是一个布尔值。类似地,IntegerFieldFloatField 字段分别将值转换为Python 的intfloat

    下面是在视图中如何处理表单数据:

    #views.py
    
    from django.core.mail import send_mail
    
    if form.is_valid():
        subject = form.cleaned_data['subject']
        message = form.cleaned_data['message']
        sender = form.cleaned_data['sender']
        cc_myself = form.cleaned_data['cc_myself']
    
        recipients = ['info@example.com']
        if cc_myself:
            recipients.append(sender)
    
        send_mail(subject, message, sender, recipients)
        return HttpResponseRedirect('/thanks/')

    提示

    关于Django 中如何发送邮件的更多信息,请参见发送邮件。

    有些字段类型需要一些额外的处理。例如,使用表单上传的文件需要不同地处理(它们可以从request.FILES 获取,而不是request.POST)。如何使用表单处理文件上传的更多细节,请参见绑定上传的文件到一个表单

    使用表单模板

    你需要做的就是将表单实例放进模板的上下文。如果你的表单在Context 中叫做form,那么{{ form }}将正确地渲染它的<label><input>元素。

    表单渲染的选项

    表单模板的额外标签

    不要忘记,表单的输出不 包含<form>标签,和表单的submit 按钮。你必须自己提供它们。

    对于<label>/<input> 对,还有几个输出选项:

    • {{ form.as_table }} 以表格的形式将它们渲染在<tr> 标签中
    • {{ form.as_p }} 将它们渲染在<p> 标签中
    • {{ form.as_ul }} 将它们渲染在<li> 标签中

    注意,你必须自己提供<table><ul> 元素。

    下面是我们的ContactForm 实例的输出{{ form.as_p }}

    <p><label for="id_subject">Subject:</label>
        <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
    <p><label for="id_message">Message:</label>
        <input type="text" name="message" id="id_message" /></p>
    <p><label for="id_sender">Sender:</label>
        <input type="email" name="sender" id="id_sender" /></p>
    <p><label for="id_cc_myself">Cc myself:</label>
        <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>

    注意,每个表单字段具有一个ID属性并设置为id_<field-name>,它被一起的label 标签引用。它对于确保屏幕阅读软件这类的辅助计算非常重要。你还可以自定义label 和 id 生成的方式

    更多信息参见 输出表单为HTML

    手工渲染字段

    我们没有必要非要让Django 来分拆表单的字段;如果我们喜欢,我们可以手工来做(例如,这样允许重新对字段排序)。每个字段都是表单的一个属性,可以使用{{ form.name_of_field }} 访问,并将在Django 模板中正确地渲染。例如:

    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="{{ form.subject.id_for_label }}">Email subject:</label>
        {{ form.subject }}
    </div>
    <div class="fieldWrapper">
        {{ form.message.errors }}
        <label for="{{ form.message.id_for_label }}">Your message:</label>
        {{ form.message }}
    </div>
    <div class="fieldWrapper">
        {{ form.sender.errors }}
        <label for="{{ form.sender.id_for_label }}">Your email address:</label>
        {{ form.sender }}
    </div>
    <div class="fieldWrapper">
        {{ form.cc_myself.errors }}
        <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
        {{ form.cc_myself }}
    </div>

    完整的<label> 元素还可以使用label_tag() 生成。例如:

    <div class="fieldWrapper">
        {{ form.subject.errors }}
        {{ form.subject.label_tag }}
        {{ form.subject }}
    </div>

    渲染表单的错误信息

    当然,这个便利性的代价是更多的工作。直到现在,我们没有担心如何展示错误信息,因为Django 已经帮我们处理好。在下面的例子中,我们将自己处理每个字段的错误和表单整体的各种错误。注意,表单和模板顶部的{{ form.non_field_errors }} 查找每个字段的错误。

    使用{{ form.name_of_field.errors }} 显示表单错误的一个清单,并渲染成一个ul。看上去可能像:

    <ul class="errorlist">
        <li>Sender is required.</li>
    </ul>

    这个ul 有一个errorlist CSS 类型,你可以用它来定义外观。如果你希望进一步自定义错误信息的显示,你可以迭代它们来实现:

    {% if form.subject.errors %}
        <ol>
        {% for error in form.subject.errors %}
            <li><strong>{{ error|escape }}</strong></li>
        {% endfor %}
        </ol>
    {% endif %}

    空字段错误(以及使用form.as_p() 时渲染的隐藏字段错误)将渲染成一个额外的CSS 类型nonfield 以帮助区分每个字段的错误信息。例如,{{ form.non_field_errors }} 看上去会像:

    <ul class="errorlist nonfield">
        <li>Generic validation error</li>
    </ul>
    Changed in Django 1.8:
    
    添加上面示例中提到的nonfield CSS 类型。

    参见Forms API 以获得关于错误、样式以及在模板中使用表单属性的更多内容。

    迭代表单的字段

    如果你为你的表单使用相同的HTML,你可以使用{% for %} 循环迭代每个字段来减少重复的代码:

    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }} {{ field }}
        </div>
    {% endfor %}

    {{ field }} 中有用的属性包括:

    {{ field.label }}

    字段的label,例如Email address

    {{ field.label_tag }}

    包含在HTML <label> 标签中的字段Label。它包含表单的label_suffix。例如,默认的label_suffix 是一个冒号:

    <label for="id_email">Email address:</label>

    {{ field.id_for_label }}

    用于这个字段的ID(在上面的例子中是id_email)。如果你正在手工构造label,你可能想使用它代替label_tag。如果你有一些内嵌的JavaScript 并且想避免硬编码字段的ID,这也是有用的。

    {{ field.value }}

    字段的值,例如someone@example.com

    {{ field.html_name }}

    输入元素的name 属性中将使用的名称。它将考虑到表单的前缀。

    {{ field.help_text }}

    与该字段关联的帮助文档。

    {{ field.errors }}

    输出一个<ul class="errorlist">,包含这个字段的验证错误信息。你可以使用{% for error in field.errors %}自定义错误的显示。 这种情况下,循环中的每个对象只是一个包含错误信息的简单字符串。

    {{ field.is_hidden }}

    如果字段是隐藏字段,则为True,否则为False。作为模板变量,它不是很有用处,但是可以用于条件测试,例如:

    {% if field.is_hidden %}
    
    {% endif %}

    {{ field.field }}

    表单类中的Field 实例,通过BoundField 封装。你可以使用它来访问Field 属性,例如{% char_field.field.max_length %}。

    迭代隐藏和可见的字段

    如果你正在手工布局模板中的一个表单,而不是依赖Django 默认的表单布局,你可能希望将<input type="hidden"> 字段与非隐藏的字段区别对待。例如,因为隐藏的字段不会显示,在该字段旁边放置错误信息可能让你的用户感到困惑 —— 所以这些字段的错误应该有区别地来处理。

    Django 提供两个表单方法,它们允许你独立地在隐藏的和可见的字段上迭代:hidden_fields()visible_fields()。下面是使用这两个方法对前面一个例子的修改:

    {% for hidden in form.hidden_fields %}
    {{ hidden }}
    {% endfor %}
    
    {% for field in form.visible_fields %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }} {{ field }}
        </div>
    {% endfor %}

    这个示例没有处理隐藏字段中的任何错误信息。通常,隐藏字段中的错误意味着表单被篡改,因为正常的表单填写不会改变它们。然而,你也可以很容易地为这些表单错误插入一些错误信息显示出来。

    可重用的表单模板

    如果你的网站在多个地方对表单使用相同的渲染逻辑,你可以保存表单的循环到一个单独的模板中来减少重复,然后在其它模板中使用include 标签来重用它:

    # In your form template:
    {% include "form_snippet.html" %}
    
    # In form_snippet.html:
    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }} {{ field }}
        </div>
    {% endfor %}

    如果传递到模板上下文中的表单对象具有一个不同的名称,你可以使用include 标签的with 参数来对它起个别名:

    {% include "form_snippet.html" with form=comment_form %}

    如果你发现自己经常这样做,你可能需要考虑一下创建一个自定义的inclusion标签。

    更深入的主题

    这里只是基础,表单还可以完成更多的工作:

    另见

    表单参考
    覆盖完整的API 参考,包括表单字段、表单Widget 以及表单和字段的验证。

    译者:Django 文档协作翻译小组,原文:Overview

    本文以 CC BY-NC-SA 3.0 协议发布,转载请保留作者署名和文章出处。

    Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。

    展开全文
  • post和get的区别

    万次阅读 热门讨论 2007-08-01 12:38:00
    今天看到了一些关于讨论post和get的区别,特贴出来和大家分享...bjnghfgreygt这样的东西如果是get,就会出现了1、Get 方法通过 URL 请求来传递用户的数据,将表单内各字段名称与其内容,以成对的字符串连接,置于 acti

    今天看到了一些关于讨论post和get的区别,特贴出来和大家分享:

     

    post和get的区别

    post 地址栏不会出现一大串?bjnghfgreygt这样的东西

    如果是get,就会出现了

    1、Get 方法通过 URL 请求来传递用户的数据,将表单内各字段名称与其内容,以成对的字符串连接,置于 action 属性所指程序的 url 后,如[url]http://www.mdm.com/test.asp?name=asd&password=sad[/url],数据都会 直接显示在 url 上,就像用户点击一个链接一样;Post 方法通过 HTTP post 机制,将表单内各字段名称与其内容放置在 HTML 表头(header)内一起传送给服务器端交由 action 属性能所指的程序处理,该程序会通过标准输入(stdin)方式,将表单的数据读出并加以处理


    2、 Get 方式需要使用 Request.QueryString 来取得变量的值;而 Post 方式通过 Request.Form 来访问提交的内容


    3、Get 方式传输的数据量非常小,一般限制在 2 KB 左右,但是执行效率却比 Post 方法好;而 Post 方式传递的数据量相对较大,它是等待服务器来读取数据,不过也有字节限制,这是为了避免对服务器用大量数据进行恶意攻击,根据微软方面的说法,微软对用 Request.Form() 可接收的最大数据有限制,IIS 4 中为 80 KB 字节,IIS 5 中为 100 KB 字节

    建议:除非你肯定你提交的数据可以一次性提交,否则请尽量用 Post 方法


    4、Get 方式提交数据,会带来安全问题,比如一个登陆页面,通过 Get 方式提交数据时,用户名和密码将出现在 URL 上,如果页面可以被缓存或者其他人可以访问客户这台机器,就可以从历史记录获得该用户的帐号和密码,所以表单提交建议使用 Post 方法;Post 方法提交的表单页面常见的问题是,该页面如果刷新的时候,会弹出一个对话框

    建议:出于安全性考虑,建议最好使用 Post 提交数据
     
    post和get的不同之处
    GET与POST的区别在于:(对于CGI)
    如果以GET方式传输,所带参数附加在CGI程式的URL后直接传给server,并可从server端的QUERY_STRING这个环境变量中读取;
    如果以POST方式传输,则参数会被打包在数据报中传送给server,并可从CONTENT_LENGTH这个环境变量中读取出来。
    还有一种情况是,你用的是GET方式,但传送的参数是路径,如:
    ----< ahref="/cgi-bin/a.pl/usr/local/bin/pine" >CGI< /a >
    ----这时所传递的参数"/usr/local/bin/pine"存放在PATH_INFO这个环境变量中。环境变量的读取方式为$str=$ENV{'QUERY_STRING'};
    理论上说,GET是从服务器上请求数据,POST是发送数据到服务器。事实上,GET方法是把数据参数队列(query string)加到一个URL上,值和表单是一一对应的。比如说,name=John。在队列里,值和表单用一个&符号分开,空格用 号替换,特殊的符号转换成十六进制的代码。因为这一队列在URL里边,这样队列的参数就能看得到,可以被记录下来,或更改。通常GET方法还限制字符的大小。事实上POST方法可以没有时间限制的传递数据到服务器,用户在浏览器端是看不到这一过程的,所以POST方法比较适合用于发送一个保密的(比如信用卡号)或者比较大量的数据到服务器。
    Post是允许传输大量数据的方法,而Get方法会将所要传输的数据附在网址后面,然后一起送达服务器,因此传送的数据量就会受到限制,但是执行效率却比Post方法好。
    对于GET提交的数据,WWW服务器将把数据放在环境变量QUERY_STRING中;对于POST方法,数据被送到WWW服务器的STDOUT中,然后CGI从自己的STDIN中读取。使用传统的CGI方法,用户必须自己编程来处理这些数据。
    GET与POST的区别在于,如果以GET方式传输,所带参数附加在CGI程式的URL后直接传给server,并可从server端的QUERY_STRING这个环境变量中读取;如果以POST方式传输,则参数会被打包在数据报中传送给server,并可从CONTENT_LENGTH这个环境变量中读取出来。还有一种情况是,你用的是GET方式,但传送的参数是路径,如:

    ----< ahref="/cgi-bin/a.pl/usr/local/bin/pine" >CGI< /a >

    ----这时所传递的参数"/usr/local/bin/pine"存放在PATH_INFO这个环境变量中。环境变量的读取方式为$str=$ENV{'QUERY_STRING'};
    总结起来:
    get方式:以URL字串本身传递数据参数,在服务器端可以从'QUERY_STRING'这个变量中直接读取,效率较高,但缺乏安全性,也无法来处理复杂的数据(只能是字符串,比如在servlet/jsp中就无法处理发挥java的比如vector之类的功能);
    post方式:就传输方式讲参数会被打包在数据报中传输,从CONTENT_LENGTH这个环境变量中读取,便于传送较大一些的数据,同时因为不暴露数据在浏览器的地址栏中,安全性相对较高,但这样的处理效率会受到影响。
    -------------------
    GET 和 POST 请求的区别
    // --TCP/IP
    协议详解卷3
    13.3.1 报文类型:请求与响应
    HTTP / 1 . 0报文有两种类型:请求和响应。HTTP / 1 . 0请求的格式是:
    reqe t - l i n e
    headers ( 0或有多个)

    body (只对POST请求有效)
    request - l i n e的格式是:
    request request-URI HTTP版本号
    支持以下三种请求:
    1) G E T请求,返回re q u e s t - U R I所指出的任意信息。
    2) H E A D请求,类似于G E T请求,但服务器程序只返回指定文档的首部信息,而不包含实际的文档内容。该请求通常被用来测试超文本链接的正确性、可访问性和最近的修改。
    3) P O S T请求用来发送电子邮件、新闻或发送能由交互用户填写的表格。这是唯一需要在请求中发送b o d y的请求。使用P O S T请求时需要在报文首部C o n t e n t - L e n g t h字段中
    指出b o d y的长度。
    // --TCP/IP 协议详解卷3

    明白?get 请求表示客户端请求一个uri,服务器返回客户端请求的uri,post请求表示客户端请求的时候还要提交数据,比喻提交form表单,要提交的数据会放到请求报文的body部分。服务器收到后这样的请求后通常需要来处理这些数据。
    应聘j2ee开发时问这个问题说明提问着的重点是服务器对这两种请求的处理有什么不同,而不是这两种请求的报文有什么不同。当然作为一个jsp servlet的开发者一般你是不会感觉到着两种请求有什么不同的,因为web服务器已经对这些请求进行处理了,web服务器调用相应的 jsp/servlet来响应客户端请求的时候,对于post的请求,web服务器已经把客户端提交的数据取出来,添到request对象中去了。不过,对于get、post的请求servlet 的 doGet、 doPost方法会被响应调用。也就是说如果客户端送来的是一个get的请求,那么你写到servlet中的dopost()方法中的代码是不会执行的,反之如果是post的请求,写在doget()中代码是不会被调用的(对于所有方式的请求,写在doservice的代码是会被调用的,因为在HttpServlet类中doGet doPost的请求都是由doService来分发的,具体的看关于servlet的生命周期介绍)。
    HTTP请求:GET与POST方法的区别

    HTTP 定义了与服务器交互的不同方法,最基本的方法是 GET 和 POST。事实上 GET 适用于多数请求,而保留 POST 仅用于更新站点。根据 HTTP 规范,GET 用于信息获取,而且应该是 安全的和 幂等的。所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。幂等的意味着对同一 URL 的多个请求应该返回同样的结果。完整的定义并不像看起来那样严格。从根本上讲,其目标是当用户打开一个链接时,她可以确信从自身的角度来看没有改变资源。比如,新闻站点的头版不断更新。虽然第二次请求会返回不同的一批新闻,该操作仍然被认为是安全的和幂等的,因为它总是返回当前的新闻。反之亦然。POST 请求就不那么轻松了。POST 表示可能改变服务器上的资源的请求。仍然以新闻站点为例,读者对文章的注解应该通过 POST 请求实现,因为在注解提交之后站点已经不同了(比方说文章下面出现一条注解);
    在FORM提交的时候,如果不指定Method,则默认为GET请求,Form中提交的数据将会附加在url之后,以?分开与url分开。字母数字字符原样发送,但空格转换为“ “号,其它符号转换为%XX,其中XX为该符号以16进制表示的ASCII(或ISO Latin-1)值。GET请求请提交的数据放置在HTTP请求协议头中,而POST提交的数据则放在实体数据中;
    GET方式提交的数据最多只能有1024字节,而POST则没有此限制。
    get和post方法的不同 在B/S应用程序中,前台与后台的数据交互,都是通过HTML中Form表单完成的。Form提供了两种数据传输的方式——get和post。虽然它们都是数据的提交方式,但是在实际传输时确有很大的不同,并且可能会对数据产生严重的影响。虽然为了方便的得到变量值,Web容器已经屏蔽了二者的一些差异,但是了解二者的差异在以后的编程也会很有帮助的。
    ? Form中的get和post方法,在数据传输过程中分别对应了HTTP协议中的GET和POST方法。二者主要区别如下:
    ? 1、Get是用来从服务器上获得数据,而Post是用来向服务器上传递数据。
    ? 2、Get将表单中数据的按照variable=value的形式,添加到action所指向的URL后面,并且两者使用“?”连接,而各个变量之间使用“&”连接;Post是将表单中的数据放在form的数据体中,按照变量和值相对应的方式,传递到action所指向URL。
    ? 3、Get是不安全的,因为在传输过程,数据被放在请求的URL中,而如今现有的很多服务器、代理服务器或者用户代理都会将请求URL记录到日志文件中,然后放在某个地方,这样就可能会有一些隐私的信息被第三方看到。另外,用户也可以在浏览器上直接看到提交的数据,一些系统内部消息将会一同显示在用户面前。Post的所有操作对用户来说都是不可见的。
    ? 4、Get传输的数据量小,这主要是因为受URL长度限制;而Post可以传输大量的数据,所以在上传文件只能使用Post(当然还有一个原因,将在后面的提到)。
    ? 5、Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
    ? 6、Get是Form的默认方法。
    ?
    GET 和 POST 的数据格式都是一样的:
    GET 支持的最大字节限制是 2048 Bytes
    POST 支持的最大字节限制是 2GB
     
     
     
    在表单里使用”post””get”有什么区别
    Form里面,可以使用post也可以使用get。它们都是method的合法取值。但是,postget方法在使用上至少有两点不同:
    1
    Get方法通过URL请求来传递用户的输入。Post方法通过另外的形式。
    2
    Get方式的提交你需要用Request.QueryString来取得变量的值,而Post方式提交时,你必须通过Request.Form来访问提交的内容。

    仔细研究下面的代码。你可以运行之来感受一下:

    代码
    <!--
    两个Form只有Method属性不同-->
    <FORM ACTION=“getpost.asp” METHOD=“get”>
    <INPUT TYPE=“text” NAME=“Text” VALUE=“Hello World”></INPUT>
    <INPUT TYPE=“submit” VALUE=“Method=Get”></INPUT>
    </FORM>
    <BR>
    <FORM ACTION=“getpost.asp” METHOD=“post”>
    <INPUT TYPE=“text” NAME=“Text” VALUE=“Hello World”></INPUT>
    <INPUT TYPE=“submit” VALUE=“Method=Post”></INPUT>
    </FORM>

    <BR>
    <BR>

    <% If Request.QueryString(“Text”) <> ““ Then %>
    通过get方法传递来的字符串是: “<B><%= Request.QueryString(“Text”) %></B>“<BR>
    <% End If %>

    <% If Request.Form(“Text”) <> ““ Then %>
    通过Post方法传递来的字符串是: “<B><%= Request.Form(“Text”) %></B>“<BR>
    <% End If %>

    说明
    把上面的代码保存为getpost.asp,然后运行,首先测试post方法,这时候,浏览器的url并没有什么变化,返回的结果是:
    通过Post方法传递来的字符串是: "Hello World"
    然后测试用get方法提交,请注意,浏览器的url变成了:
    http://localhost/general/form/getpost.asp?Text=Hello+World
    而返回的结果是:
    通过get方法传递来的字符串是: "Hello World"
    最后再通过post方法提交,浏览器的url还是:
    http://localhost/general/form/getpost.asp?Text=Hello+World
    而返回的结果变成:
    通过get方法传递来的字符串是: "Hello World"
    通过Post方法传递来的字符串是: "Hello World"

    提示
    通过get方法提交数据,可能会带来安全性的问题。比如一个登陆页面。当通过get方法提交数据时,用户名和密码将出现在URL上。如果:
    1、登陆页面可以被浏览器缓存;
    2、其他人可以访问客户的这台机器。
    那么,别人即可以从浏览器的历史记录中,读取到此客户的账号和密码。所以,在某些情况下,get方法会带来严重的安全性问题。
    建议
    Form中,建议使用post方法。
     
    展开全文
  • FillForm是Firefox的一个扩展,根据名字可以看出来是填写表单,但是实际基本所有的表单元素都会处理的,这个扩展的功能就是可以保存和填写当前网页所有文本框、复选框、单选框的内容(input、textarea、select标签)。...

    FillForm是Firefox的一个扩展,根据名字可以看出来是填写表单,但是实际基本所有的表单元素都会处理的,这个扩展的功能就是可以保存和填写当前网页所有文本框、复选框、单选框的内容(input、textarea、select标签)。

     

    火狐官方安装地址:https://addons.mozilla.org/zh-CN/firefox/addon/fillforms(推荐)
    谷歌托管安装地址:http://code.google.com/p/ffextension/downloads/list 


    虽然Firefox本身也有自动填表的功能,但是也有些缺陷,例如:
    1.别人临时用下你的电脑登录下邮箱,可是一打开网址发现里面已经填了你的用户名和密码,这是我们所不愿意的。
    2.有些网页在打开后会初始化一些东西,例如清空所有文本框,这样Firefox刚刚自动填入的内容也没了。典型的一个例子就是163邮箱登录。
    3.Firefox要保存文本框内容必须在你有提交表单动作的时候才提示“记住”还是“跳过”。
    4.有些文本框并没有放在Form表单中,这样Firefox是没法直接保存的。

    网上有一些填写表单的扩展的,但是用起来很不方便,记得有个扩展是把Firefox本身的填表功能升级了,填写用户名和密码后自动提交,这样上面列出的问题1对我们来说就更加糟糕了。还有个扩展是必须要用户自己设置哪个id哪个name的标签设置为什么值,这个对于大多数根本就不懂HTML基础知识的人来说应该比较困惑,即使懂HTML的人也觉得比较麻烦。

    用过世界之窗浏览器的人就知道了,那个里面的自动表单功能很好用,Alt+1就保持当前表单内容,Alt+Q就填写当前表单内容(前提是你已经保存了数据),在你需要的时候才填写。本扩展就是要在Firefox上实现这功能。网上制作Firefox扩展的范例很少,在这里就顺便和大家分享下,记录下我的开发过程,大家也可以在下面发表点的建议。

     

    2010/4/3

    关于如何写火狐扩展,网上中文资料真是少之又少,好不容易找到个范例,对照着做还是没反应。最后绕到火狐官网的开发者中心,都是英文的,尽管看着比较吃力,但是还是慢慢啃了,找到个helloworld的范例,安装很简单,直接把xpi文件拖到火狐浏览器里面即可。xpi文件其实就是个zip压缩文件,把后缀名改成zip然后解压就可以看到里面的结构和代码了。组成比较简单,基本就一个js和一个xul文件。它是在浏览器中的工具菜单下增加一项,点击后会弹出一个关于对话框。

    现在要做的就是在那个js文件里使用dom方式访问当前网页了,读取设置值。可是当前页面的顶级元素是什么呢?在js中是document,我在那范例代码里的js里修改测试了下发现并不被认识,然后研究了下其它的扩展的代码(所有本机安装了的扩展都可以看到代码的),尝试了doc、application、firefox、app等,都不行。

    继续找资料,发现原来火狐浏览器自己的整个界面也是个xul文件,可以通过chrome://browser/content/browser.xul访问,document是顶级元素,可是不是那个当前网页的顶级元素,这样直接用document访问显然就不行了。检查了下当前网页所在的元素是:

         

     

    这样用document.getElementById('content')访问,但是真正要访问当前打开的页还不行,得用document.getElementById('content').selectedBrowser.contentDocument,这样就突破了一大难关,和网页交互基本不是问题了。我写了下面这段代码读取Input标签的id、name、index(测试成功):

     

     

     

    2010/4/21

    要实现fillform的功能必须要解决js文件读写文件的问题,网上找了半天没什么特别有用的资料,最后还是绕道了那个develop center,有这么个页面专门讲解了如何如果操作文件:https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO ,粗略看了下,基本主要的读写功能以及读写模式什么的都有介绍,由于今天活比较多,没来得及细研究,只是把搜集到的资料保存到了一篇博文里:http://blog.csdn.net/sysdzw/article/details/5511755

     

     

     

    2010/4/22

    今天主要看了那个官网的File I/O的文章,打算写几个实现读写文件的功能的例子。先整理了下到目前为止遇到的些问题(*表示优先度高点的,灰色表示已解决):

    *1.js多个函数书写格式及调用问题,为什么增加了个函数就挂掉了
    *2.扩展读写本地文件问题
    *3.数据文件的格式设计

      4.界面语言(中文、英文)
      5.关于对话框
      6.版本控制(自动检测升级) 通过审核了会有Mozilla统一处理的
      7.图标问题,为什么按规则添加的gif或jpg都无效
    *8.保存的数据加密和解密问题

      9.考虑将来升级版本数据兼容问题
      10.可视化管理界面,暂时不提供,均由程序自动处理
    *11.热键控制操作

     直接复制那代码稍微修改下,然后放到一个我建立的函数writeToDataFile中,可是试了下怎么都不行,并没有写文件成功,这说明在哪一步出错了(很郁闷的是,出错应该有提示的,可是安装好有什么反应都没有,我只有在代码里各个位置加alert并不断的重启Firefox然后试,不知道有什么好点的办法),试了很多次还是不行,发现就保持那个默认的函数的可以的,我的加上去就不行了。找了很久最后才发现,原来函数的最后的右大括号“}”后面要价格逗号的“,”。而当只有一个函数的时候后面可以不加,真是郁闷。接下来又试了很多次,终于写文件成功。要打开文本文件也看到换行效果的话需要用/r/n,表示回车换行。

    但是测试写入中文发现都是乱码。之后的读取中文也是的,都是乱码。都改用UTF-8模式还是不行,到目前为止还没解决。

    2010/4/23

    今天设计了下大致的文件格式,第一行放url地址,紧接着就保存信息,例如标签名值什么的,暂时未处理加密问题,范例如下:

     

    2010/4/24

     

    今天开始着手写填写和保存网页元素的两个函数。在填写时首先获得当前打开网页的URL,然后根据从本地文件读进数据,用正则去匹配获得结果,没找到的话放弃,找到的话就开始一个个处理。文件就像上面的格式。

    在js中几乎没用过正则,遇到了不少问题。一般我们用match去匹配字符串,双斜杠//之间的是个固定常量,而我需要由一个变量拼接起来形成一个表达式,找了很多资料都不行,最后发现得用new RegExp,它在声明时可以由变量组成一个表达式,最后用exec方法处理。另外一个就是如何取得括号捕获的内容,这个没花多少时间,在javaeye里面找到了答案,也是用exce方法处理。

    基本填写的函数测试是通过了,基本没问题。

    保存的函数也已经开始写,首先是搜集当前页面的所有信息(目前只处理了Input的信息),只处理type为“text、password、checkbox、radio”这几类。已经可以搜集保存到一个字符串中。下面就是根据情况写入到文件了,如果已经存在则用正则表达式替换更新,如果不存在则在末尾追加。

    目前遇到的问题:

    1.使用正则替换该如何操作?xx.replace(//,xxx)这个估计是不行了,因为我的表达式含有变量。

    2.写入文件如何使用追加模式?这个可以到develop center找答案。

    3.写入文件如果是中文如何保证不乱码?这个找了很久都没找到方法,先放放

     

    另外上面提到过关于js代码出错并没有任何提示的问题其实很好解决,用try...catch捕获下就行了,呵呵。

    2010/4/25

    经过csdn一位网友的启发,找到了替换文件方法,其实也就是找到了正则表达式如何用变量组成的方法,这里顺便说下方法,是用eval函数。

    遇到了点小问题困惑了很久,不过最终还是解决了,这里提下,防止以后再碰到这样的问题:

    js中默认的换行是/n并非/r/n,这个在我要将数据内容中的回车换行替换为“/r/n”的字符表示时浪费了很多时间。

    填写的函数也已经完成,基本的功能测试都可以了,现在的最最主要的问题有以下几个:

    1.不支持中文,用utf-8格式也不行。

    2.不知如何设置热键。

    3.设置地区语言。

    4.使得弹出窗口有标题栏。(2010/5/2添加)

     

     

    2010/4/26

    解决了中文乱码问题,方法很简单就是用了escape和unescape函数,对于密码什么的依然是明文显示,以后考虑解决。

     

     

    2010/4/30

    对第三段值已经采用base64加密,这样中文英文都进行了处理,这样密码也不会光天化日的暴露在坏人面前了,呵呵。

    接下来打算处理热键功能。另外还有个小问题就是弹出的JavaScript窗口的标题很难看,看看能不能改掉。

     

    2010/5/2

    今天把热键的功能实现了,现在按Alt+Q就填表,按Alt+W就保存当前表单。 这个是研究了半天firebug代码的成果,好不容搞定,原来那个热键需要在key标签里定义的,而执行的需要放在command标签里。普通键直接写在key标签的key属性里,如果是特殊键的话需要用系统约定的例如“VK_F12”表示F12键、“VK_DEL”表示删除键等。如果有控制键的话约定是key标签的modifiers属性“alt、accel、shift”。

    有个困惑了很久的是判断函数是“file.exists”,郁闷了我半天,最后发现原来是exists后面必须要加括号,看样子还没有完全适应js啊。

    今天还把关于对话框搞定了,之前是用的自己做的一个简陋的对话框,什么东西都是写死的,版本什么的每次升级了还需要自己手动修改,现在直接调用系统的模板对话框读进配置信息,例如创建者版本什么的。

    总结下今天突破一下几点:

    1.解决了热键问题,下一步考虑看能否实现自定义。

    2.解决了关于对话框问题。

     

    另外保存信息部分做了点修改,即使当前文本框都为空也保存当前页面信息,不然用户可能会因此而困惑,扩展只需按用户的意愿执行就行了。目前版本为1.0.1

     

    2010/5/3

    修改了些翻译。谢谢小白(Shelley)的帮忙,呵呵。

    目前版本变更为1.0.2

     

    晚上修正了下支持单选框和复选框已经下拉列表。重复的name也支持了。

     

    2010/5/16

    由于提交审核被Mozilla打回,说使用eval函数可能会带来安全隐患什么的。在Mozilla官方网站提交扩展的时进行了次代码初步检查,当时就提示eval函数问题,我没在意,现在经过人工审核还是不行,看样子不得不修改了。查了下,代码里共3处使用了eval,主要就是针对于字符串的mach方法调用正则的地方。结果用new RegExp都改改了过来。

    另外做了本地化处理,如果你安装的火狐是英文版,那么相关的菜单项就是英文的,如果是中文版的火狐那么菜单项就是对应的中文。

    目前版本变更为1.0.6

     

    2010/6/1

    今天收到Mozilla通过审核的通知邮件,呵呵,不容易,终于出沙盒了。

     

    2010/6/2

    增改了一些功能:

    1.在没有找到与当前网址匹配内容时可以启用通用表单的填写功能。

    2.删除了以前填写的模块的一些不必要的代码。
    增加了通用表单的功能,当发现并没有为当前网址保存过信息时,则从历史数据中根据id和name尽量匹配填写各个元素,这一功能为很多人带来了极大的便利,因为有时相同的一个网页可能有不同的网址,典型的比如后面带参数的,这样一处理就基本解决了问题。

     

    2010/6/5

    为了使得读取的历史表单信息总是最新的内容,调整了保存数据模块的逻辑。
    当有新数据时追加到文件的最开始处。
    当有数据要更新时,先删除原来数据的数据,然后把新数据添加到文件的最开始处。

    这样如果用历史数据填写时总是用的最新的数据了,这样用户也可以控制让什么数据保持在最新,想使用什么数据来作为默认值填写,这时只要再去保存下就行了。

    今天下载量是198,前几天好像是110、128、144、198,呈几何级数增长啊,呵呵,很欣慰。好像默认的扩展搜索栏用fill form去搜索我的扩展都是排在第一位的,所以下载量比较高,新版本等下载量下去后再放,不然一审核的话下载量又下去了。

     

    2010/6/9

    发现“日用户停用数 ”越来越高了,截止到昨天已经达到了28,速度很快,不知是为什么用户要停用,虽然想对于总“日活跃用户数 ”400多,这个数字并不算什么,但是我还是仔细分析了下原因:

    1.用户只是把不常用的插件停用以节省资源,如果是这样还算好的。

    2.用户错误的下载了自己不需要的插件,这个也没问题,不需要的插件留着做什么呢。

    3.用户觉得可能有安全隐患,密码保存了会不会被别人知道呢?这个没办法,插件通过了审核的,爱下不下,没审核前下载量确实很小。

    4.停用的可能是部分升级的用户,插件一升级原来保存的数据都没有了,这让用户很困惑,难以接受,所以停用。要是这样的话就是我设计的问题了。

    5.觉得功能有缺陷,比如对于有frame嵌套网页的根本没法处理,这个以后会考虑解决。

     

    以上4、5两个问题会着重解决。

    另外加紧1.1.7版本的发布准备,当每日停用的用户达到50时决定正式发布。其实新版本的投入并不能减少用户的停用数,由于至少需要半个月的审核,所以此期间肯定会导致下载量大大减少。而且仓促盲目发布必然带来某些问题,翻译方面就是个典型的。

    不如先将用户下载数带上去,这样以形成影响,现在绝大部分下载来源是火狐的“附加组件”那边的搜索,估计用户一般是通过关键词“fillform”或者“fill form”搜索到的,前者搜出来的只有我一个插件,后者搜出来的有很多,但是我的是排第一位的,大名鼎鼎的“autofill forms”竟然也排在我下面,尽管其周下载量是七千多,我的是一千多,不过我相信这距离会越来越短的,而且他最新的一次更新是在2008年的,上个年代的了,呵呵。

    说实话,插件本身的技术性及功能上我的确实不如它,不过易用性上面它的绝对不如我的。

     

    版本1.1.7发布前必须处理好的事:

    1.数据文件的保存和转移,如果不能做到那么必须在组件介绍页写出提示,让用户备份数据,不过这是下下策,因为公开了数据源非常不好。

    2.英文翻译力求地道准确,插件80%的用户都是老外,不能让他们看得困惑。

    3.争取界面的人性化,典型的如保存后提示成功的对话框最好不要含有感叹号,能有个“i”的消息提示的框就好了。

     

    一些零碎的事:

    1.设置的对话框的按钮和底部距离应该小点,上面可以插入个空白。

    2.设置应该翻译为“Set”还是“Options”还是“Setting”?参考下其他几个插件。

    3.有必要的话采集其他插件的使用者邮件向他们推荐此插件。

    4.对于input如果没有type类型的也要能够处理。

     

    2010/6/10

    插件功能概要描述:

    1.通过一个快捷键(Alt + Q)就可以填写当前网页内容。
    2.通过一个快捷键(Alt + W)就可以保存当前网页内容,这是autofill forms所没有的功能。
    3.对于已经保存的数据都是做了一些加密的,别人是无法轻易看到得到里面的信息的。例如用户名、密码等。这是autofill forms一直存在的缺陷。
    4.对于表单,可以填写除了input标签之外的所有可填写的标签。例如textarea,select。(文本框、超文本框、下拉列表框、复选框、单选框)。
    5.可以填写非表单的标签。例如有些标签并没有放在表单内我们的fillform也可以处理,而autofill forms是无法处理表单之外的标签的。
    6.可以使用历史数据对未保存过信息的网址进行填写。有时一个相同的网页有着不同的网址,fillform只要保存一次就可以都填写了。
    7.如果使用历史数据填写,则都是使用最新的数据进行填写。例如很多文本框的name标签都叫username,历史记录中可能保存了很多 username的值,这时如果再遇到name为username的文本框则会直接使用最近保存的一次的值进行填写。通过保存操作用户可以调整控制“默认值”。autofill forms是弹出个对话框让用户选择的,比较麻烦。
    8.有效的减少了一些冲突,智能化处理的很多东西,大大降低用户的手工操作。例如几个文本框的name属性都叫“username”,在 fillform中可以自动编排处理,无须用户手工选择,这在autofill forms中需要手工确认information 的。
    9.此插件虽然小但功能却是强大的,而且使用方法很简单,占用系统资源也很小。

     

     

    英文部分已经翻译,可能需要调整,哪位英语好的同学帮忙看看。

    FillForm features:

    1. Fill out current web forms with a keyboard shortcut(Alt + Q).
    2. Save current web tags's information with a keyboard shortcut(Alt + W).
    3. The saved data has been encrypted. Nobody can get the information easy. Such as username, password.
    4. Works with all input fields, texteareas, selections, checkboxs, radio buttons etc.
    5. The addon also can deal with some input fields which are not in any forms.
    6. You can use the historical data to fill the web page which is not saved. You just need to save only once even if the same page or same form has different URLs.
    7. The addon always uses the latest historical data to fill the page. For example, there will be more than one input fields which have the same name property, such as "name='username'". The addon will use the latest value. You can set the "default" value by the saving operation.
    8. Can reduce conflicts effectively and deal with things intelligently, this will greatly reduce the user's operations. For example, several input fields with the same name attribute ("name='username'") in a web page,  the addon also can saved correctly, Of course, can filled correctly.
    9. The addon is not only powerful,but also easy to use. And only active when filling out forms so it doesn't consume any system resources while surfing.

     

    2010/6/11

    找到了confirm和alert的通用替代方法,需要做的修改:

    1.信息保存成功时弹出的对话框。
    标题用“Fillform”;
    底部按钮用“确定”;
    内容:

    信息已经被成功保存!

    □ 不再显示此消息


    The information has been saved successfully!

    □ Do not show this message again


    2.当要保存的信息已存在时弹出的是否覆盖对话框。
    标题用“Fillform”;
    底部按钮用“是、否”;
    内容:

    当前网页的相关信息都已经被保存过了,是否覆盖?
    http://writeblog.csdn.net/PostList.aspx

    Tips
    1. 按快捷键 "Alt + W" 保存当前网页信息
    2. 按快捷键 "Alt + Q" 填写当前网页信息


    This web page information has been saved, would you like to overwrite it?
    http://writeblog.csdn.net/PostList.aspx

    Tips
    1. Press "Alt + W" to save current page information.
    2. Press "Alt + Q" to fill information.


    3.如果当前网址未保存过信息会弹出是否使用历史数据填写。
    标题用“Fillform”;
    底部按钮用“是、否”;
    内容:
    你还没有为当前网页保存过信息!是否使用历史数据填写?
    http://writeblog.csdn.net/PostList.aspx

    Tips
    1. 按快捷键 "Alt + W" 保存当前网页信息
    2. 按快捷键 "Alt + Q" 填写当前网页信息

    □ 不再显示此消息


    You haven't saved the information for this web page! Would you like to fill with the historical data?
    http://writeblog.csdn.net/PostList.aspx

    Tips
    1. Press "Alt + W" to save current page information.
    2. Press "Alt + Q" to fill information.

    □ Do not show this message again


    4.如果使用默认信息填写,但是一个都没有填写成功,那么会弹出提示“没有填写任何标签”的信息。
    标题用“Fillform”;
    底部按钮用“确定”;
    内容:
    填写失败!历史数据中没有找到任何相吻合的信息,因此当前网页中一个元素都没有被填写!
    http://writeblog.csdn.net/PostList.aspx

    Tips
    1. 按快捷键 "Alt + W" 保存当前网页信息
    2. 按快捷键 "Alt + Q" 填写当前网页信息


    Failed! You haven't filled any element of this web page with the historical data!
    http://writeblog.csdn.net/PostList.aspx

    Tips
    1. Press "Alt + W" to save current page information.
    2. Press "Alt + Q" to fill information.

    参考网址:https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIPromptService

    重写了confirm和alert函数:

    2010/6/12

    今天把之前定的式样基本实现了。工作量最大的是翻译,找了好多人最后基本就这样了,已经上传到Mozilla,提示有30个插件正在排队,前几次提交都是一百多个,看样子大家都去看世界杯了。希望编辑对足球不感兴趣,呵呵。

    2010/6/14

    找到了检测语言的另一个方法,直接从系统中获得,封装成了函数:

    getLocale: function()
    {
    try{
    var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("general.useragent.");
    return prefs.getCharPref("locale");
    }catch(exc){alert(exc)}
    },

    另外顺便写下加按钮到工具栏的代码:

    <toolbarpalette  id="BrowserToolbarPalette">
    <toolbarbutton id="fillform-button" label="FillForm"  oncommand="FillForm.fillForm(event);" οnclick="if (event.button == 1) FillForm.fillForm(event);"  class="toolbarbutton-1 chromeclass-toolbar-additional" tooltiptext="FillForm"/>
    </toolbarpalette>

    2010/6/15

    这次更新果然很快,官方显示的6月14,也就是说提交后两天就审核通过了。

    查看了下昨天下载量是165,日用户量增加一百多,以前从来没这么多,之前的可能基本70%的下载好后就删了,现在的165日用户增加是707-578=129,也就是说165中有129个人是保留着这插件的,看样子新版本还是等到很大认可的,留住了不少用户,非常高兴。
    要是保守的话继续留着1.0.6的版本,估计会继续失去很多很多用户的,胜利在望了,呵呵。

    2010/6/24

    现在下载量突破4000,日用户突破1000,每日下载量突破200,收效比较明显。
    只是评分有点气愤,目前为止一共有4个评分,分别是4、1、4、5分,其中评分1的是一个国人的,其他都是老外的。那个丢人的国人说“PHP网页完全不能填写”,这个填写跟网页语言没任何关系的,可以看出其人对于web是基本什么都不懂的,还乱评,我在下面已经回复他了。又没有需要交费的,好像自己付了多少多少钱似的,这么嚣张,由此可见其素质,这也反映出了部分国人不知感恩,别人付出多少他不在乎,只知道自己获益多少,自私狭隘。

    2010/6/25

    下一版本1.1.8需要修改的几个地方:

    1.语言的获取方式,直接通过about:config那边获取
    2.设置信息直接保存到火狐的公共区域,这样下次再进行安装时不会被覆盖
    3.提示使用历史数据进行填写的时候那个“下次不再提醒”的对话框和上面的内容保持一个空行的距离

    2010/6/30

    昨晚收到一封来自美国旧金山大学的一个朋友的邮件,反映说fillform在iMac下无法使用快捷键。

    之前审核通过时编辑就发了邮件告诉我的,当时由于考虑到用着系统的人少(根据目前的统计数据来看,用widows的占90%左右)而且手头又没有这系统,所以就暂时没管。
    不过最近隔壁的一个朋友买了个苹果笔记本,于是就去测试了下,果然快捷键使用有点问题。如果当前光标在文本框内按下alt+q或w时会直接在文本框内输入一个字符;但是如果在网页的任意空白地方点击下使得文本框没有焦点,然后再使用快捷键就ok了。其实是苹果下面的功能键和windows下分配习惯有点区别,苹果下面一般都用ctrl+字母,或者ctrl+shift+字母,怪不得我看其他插件基本都没有用alt+字母做快捷键的,开始还高兴这样就没有冲突了的呢,原来是暗藏玄机。

    知道了问题的原因下面就要想对策了。我临时给那位朋友的意见就是不让文本框有焦点然后再按快捷键。打算最终解决问题的方案有两个:
    1.根据操作系统执行快捷键,在windows下面就是目前的。苹果下面ctrl+字母基本都被使用了,火狐浏览器本身也使用了很多ctrl+字母作为快捷键。很多插件都是用ctrl+shift+字母做快捷键的,我的用的q和w,目前好像还没发现有冲突的。打算就用ctrl+shift+q和ctrl+shift+w了。
    2.可以让用户自定义快捷键。这个可以参考其他软件,很多都有这功能的。
    以上两点打算在下一版本1.1.8中实现。

    2010/7/2

    今天又收到个用户请求说让在右击菜单中加入操作项,正好把要在下一版本1.1.8中所有可能的改造点罗列下:

    1.语言的获取方式,直接通过about:config那边获取   2010/7/4
    2.设置信息直接保存到火狐的公共区域,这样下次再进行安装时不会被覆盖   2010/7/4
    3.提示使用历史数据进行填写的时候那个“下次不再提醒”的对话框和上面的内容保持一个空行的距离2010/7/4
    4.根据操作系统设置默认快捷键。windows下就是1.1.7版本中的,iMac下面的用ctrl+shift代替alt(来自用户邮件反馈) 保留待1.1.9处理
    5.可以让用户自定义快捷键。这个可以参考其他软件,很多都有这功能的 保留待1.1.9处理
    6.设置选项可以让页面载入时就自动填写上保存的内容(来自火狐下载页评论)   2010/7/4
    7.提供可以修改保存的数据的功能(来自火狐下载页评论) 保留待1.1.9处理
    8.遍历所有frame框架页   2010/7/4
    9.在右击菜单中加入直接操作的菜单项(来自csdn反馈) 2010/7/5
    10.数据文件保存到非扩展目录,这样才有可能导入和导出 2010/7/6

     

    备注灰色 表示已经处理,划线 表示废弃,原状的表示保留

    2010/7/4

    今天对informenter进行了剖析,找到了将设置保存到火狐公共区域(about:config)的方法,主要就是处理设置的存取这一块,另外选项上面也增加了个在页面加载完成后是否自动填写当前网页,默认为是。三个默认选项分别为:不使用历史数据填写、不忽略提示成功的对话框、自动填写网页。

    处理项为7月2号list中的1、2、3、6、8这几项。

    顺便把封装的两个函数贴下:

     

    2010/7/5

    今天无意发现代码中的几个小问题:
    1.第128行 var url =location.href; 应为 var url =doc.location.href;
    2.第204行 var isFilled = false; 应该放到try语句中
    3.xul那边的不需要将当前选择的browser传递到那函数,参数都可以省略,然后交由fillform函数处理
    4.用 doc.baseURI; 替代 doc.location.href;
    5.找到了增加网页右击菜单项的方法了,研究了好一阵子firebug才找到方法的
    6.考虑到网页右击菜单中使用以前的“填写”、“保存”可能会造成歧义,所以文字上面做了较大修改,首先插件中文名称改为“自动表单”,因为可以自动填写了嘛,以前用“自动”二字一直很慎重,呵呵。另外两个操作改为“填写表单”、“保存表单”。大致和世界之窗浏览器的保持一致了。英文的还需要做相应的修改。

    2010/7/6

    昨天在火狐官方论坛上发了个帖子问如何使得数据文件不覆盖,今天看了下有个热心的老外回了。感觉他的英语有点别扭,看了下国别是乌拉圭,原来是个说西班牙语的国家,呵呵,和我一样,对于英语国家来看都是老外。

    扯远了,那位朋友说数据文件应该保存在profile文件夹下,也就是extension的上一层目录,并且给了个MDC关于IO的一篇官方文档的链接,看了下,找到可以在extension上层目录创建一个文件夹的方法了,可是怎么在这个目录中读写文件呢?整个文章看了半天都没找到结果。

    在扩展中写文件一般需要三个对象。nsIExtensionManager、nsIFileOutputStream、nsIConverterOutputStream,第一个是创建文件对象,拥有.path、.exist等基本属性,但是不像fso那么方便可以直接write什么的,需要靠nsIFileOutputStream和nsIConverterOutputStream才可以完成输出。后面两个对象主要是控制文件输出的方式以及编码格式。

    之前是在扩展目录下写文件的,范例中有代码通过nsIExtensionManager的getInstallLocation方法指定要读写的文件,可是这个方法只能局限于扩展自身的目录。我想如果要写在其他目录肯定也离不开nsIExtensionManager对象,它下面应该还有其他方法,可是搜遍了MDC都没找到满意的答案,说什么有两个接口都是叫这名字,有冲突不妥当什么的,就是没有找到个详细介绍页,然后在最新的某一份资料中发现说在Firefox4.0中这个将会被什么addonManager替代,然后找了这个对象还是没有什么实质性的内容。

    想想还是去研究informenter的代码吧,因为之前看到它的选项对话框中会显示数据文件内容的,而且重装这个插件后数据还是在的。可是搜遍了default(extension的父目录)目录都没找到它的影子,无奈一行行看那些js代码吧,最后终于发现,原来它的数据文件都保存在about:config哪里的,作者太不厚道了,怎么可以放在这里呢?每次启动的话这些数据都会被加载的,十分影响火狐启动速度及其性能和稳定性的,而且我那边数据量可能很大,这个方法肯定不行,顺便也建议informenter的作者妥善处理下。

    看样子必须得研究iMacros了,因为这个可以编辑内容,并且可以导入导出,而且我也看到了它在default下有个专门的文件夹iMacros,可是这家伙很庞大,代码一大堆。在火狐官网上午又求助了,不时的去刷新,到现在还没人理。还是先看看iMacros的代码吧。

    那个插件的代码太复杂了,没看出什么头绪,晚上回来上了下论坛,发现老外回复了,给了方法了,十分感谢这位国际友人tito bouzout,决定加入贡献者列表。上面的问题list的第十项已经解决。

    无意发现个问题,会弹出什么“invalid range in character class”错误,一行行删除代码,错误终于定位在“new RegExp(”这里了,自己检查才发现原来是构造正则表达式时由于id或name可能有元字符导致正则表达式出错。然后对于有变量的统一用 replaceRegChar函数处理了下,一个自定义函数,可以转移那些正则表达式中的元字符。

    发布了才发现上面的那个错误,再去重发布下。

    2010/7/7

    在英文版下测试发现快捷键显示有点不妥,然后工具菜单那边做了稍微调整,页面上的右击菜单拿掉快捷键。还有右击菜单项的id和工具栏上面菜单的重复了,修改了下。设置界面的英文太长了看起来极不协调,又调整了下现在看起来简洁多了。重新发布了,上次排队好像65,现在87了,不知又要到什么时候了。

    2010/7/9

    本以为要等个几天的呢,想不到今天就收到审核通过的邮件了。Mozilla工作效率真高,某D的真应该跟老外好好学学。

    发现多了条留言,原来是某个国人挑刺,说应该通过设置来决定是否在右击菜单中加入填写和保存的操作项,只给了4分,小气,不跟下面的几个老外学学。真是众口难调,那个要加入右击菜单的是这篇文章下面的某个用户要求的。现在修改了下可以通过设置来决定是否显示在右击菜单中了,等在下一个版本1.1.9中发布。

    2010/7/11

    搜集并整理下1.2.9中可能需要新增加的功能:

    1.右击菜单中的操作项可以由用户设置是否显示(来自火狐下载页评论) 2010/7/8
    2.根据操作系统设置默认快捷键。windows下就是1.1.7版本中的,iMac下面的用ctrl+shift代替alt(来自用户邮件反馈)
    3.可以让用户自定义快捷键。这个可以参考其他软件,很多都有这功能的
    4.增加可以删除单条已保存数据和清空所有已保存数据的功能(来自火狐下载页评论) 2010/7/16
    5.所有id命名含-的改为_ 2010/7/16
    6.语言文件,自定义的语句直接用xx=yy的格式 2010/7/16

     

    备注灰色 表示已经处理,划线 表示废弃,原状的表示保留

    2010/7/16

    下午研究了半天firebug的代码终于搞明白了怎么用listview显示数据和处理数据了,真的很麻烦,怪不得MDC上说这个是较复杂的控件,其实类似这样的控件在其他语言中操作都比较简单的,不知道这个上面怎么搞的,非得建立个和其内部结构一致的对象。

    现在已经完成了逐个删除单个数据和清空所有数据的功能。本来想支持多条数据删除的,可是selection好像无法一个个访问,只得selection.currentindex来逐个删除。剩下的导入导出好像比较麻烦的。(考虑到数据的隐私性还是不提供导出功能了)

    另外还发现listview的一个bug,删除最后一个元素后明明没有选择的条目了,但是根据selection.count查询的结果还是1,奇怪。

    真正发布时还得处理下原来的数据是否合法。

    终于完善好了,发布下。现在已经凌晨2点47了。 - -

    2010/7/18

    稍微修正了点然后又重新上传了

    选项那边的第一句英文翻译。

    选项的风格和数据查看的风格设置一样。

    部分js代码按照功能分离到不同的js文件中了。

    2010/7/23

    审核没通过,说部分变量命名和其他插件的命名冲突,比如Lib.和Base64.,我在前面都加了个ff这样就冲突了。在提交提示队列95,和上次一样,又得过个5天才能审核到我了。

    2010/7/24

    今天插件的统计数据表明下载量已超1万,可喜可贺!

    总下载
    从 2010 年 05 月 5 日 以来
    10,032   日活跃用户
    在 07 月 23 日 (五)
    2,287
    最近一天
    07 月 23 日 (五)
    148   变化率
    2,297 在 7月. 22
    -0.44%
    平均日下载量 126   平均日活跃用户 824
    最近七天 1,499   本周平均每日用户
    +16.78% 自上周以来
    2,041

    2010/7/28

    今天闲的蛋疼,想一鼓作气在老外人群中推广下,将下载量再推到一个高潮,可惜结果事与愿讳,将插件推向了死亡的火坑,陷入了万劫不复的境地,真是欲速则不达啊。

    我写了个程序从Mozilla官网的一些评论页采集用户邮箱,然后通过网页上“插件的分享给好友功能”批量发送。由于采集结果未作重复筛选处理,结果很多人都收到几封推荐邮件,由于那上面人基本都是老外,对这样的邮件反应很强烈,结果被当做骚扰邮件向AMO举报了。AMO对此也比较重视做出了严厉的处理,直接将我插件封了,并通知我到其他地方放插件。现在那插件不能升级也不能提交新版本了,而且搜也搜不到了,打开插件的网址提示“We're sorry, but we can't find what you're looking for”,555

    2010/7/29

    早上打开邮箱有回信了,说继续禁用我的插件,大大的杯具,555

    2010/8/6

    今天向AMO发了邮件申请给我插件启用,说的真是声泪俱下啊,不知道有没有用。。

    2010/8/9

    到现在还没收到AMO的回信。有两个可能:

    1.AMO看到了后仍旧维持“原判”然后直接忽略此信,这样的话就杯具了。

    2.AMO被我声泪俱下的文字感动了,正在讨论处理决定。

    不过根据以前和他们的信件交往来看,不理不问的可能性比较小,无论是什么结果他们都会回信的。这样第二个可能性就比较大了,因为那时周五晚上发出的,如果他们是双休的话那么可能今天晚上(米国和我们时间相反的)他们应该才看到信件。

    主啊,让我度过此劫吧!

    2010/8/10

    我昨天分析的对的,差不多是上面的第二种情况,早上收到的回信,沟通无果。。。以下为接收到的邮件:

    Hello,

    As you said, we forgive mistakes that are not very serious. But this was very serious. We are still getting emails from people complaining about the spam. Emails sent from our site will likely be marked as spam for many users now.

    It's well-known that writing scripts to scrape information from websites without using their API is considered abuse, and while it may have been unintentional that you sent multiple emails to the same users, writing the script to begin with was intentional.

    And yes, due to your actions, we will be putting checks in place on the email sharing feature to make sure this can't happen again.

    Justin Scott

    2010/11/20

    由于旧的被封了,所以我重新注册了个账户将fillform改成fillforms提交了上去,结果审核通过。不过发展已经没有之前那么好了。 之前下载量一天好几百,现在只能几十了。通过关键字fillform已经不能搜到我的扩展了,555


    2011/1/10

    虽然被火狐屏蔽了,但是插件还在他们服务器上,并没有被删除。

    http://releases.mozilla.org/pub/mozilla.org/addons/160849/


    2011/4/4

    有不少用户反映最近在Firefox4.0下不能使用。试了下果然如此,看看还是修改下,毕竟ff4正式发布了。把之前老外的审核邮件翻出来,看看哪些能修改的就改下。

    1) On Firefox 4.0b8, it causes several alerts on startup, one stemming from your attempt to use the old nsIExtensionManager rather than the new AddonManager. These are unacceptable and must be fixed.
    这个在两个地方用到的。一个是关于对话框,在mdc上找了很久没找到方法,发现firebug的对话框可以正常使用的,研究了下它的代码,原来只要修改一点点而已,这个修正完毕。另外一个就是读取文件的地方,换了另外一种方式读取这样也没问题的。主要问题解决。

    2) I would be extremely surprised if this worked on Firefox 1.0 at all.
    呵呵,随便写的,想不到老外还真去试验了。挺有意思的是他并没有明显的责怪意思。

    3) You can improve your extension's portability and save some code if you use the prefwindow element (https://developer.mozilla.org/en/XUL/prefwindow ) to manage your preferences.
    这个没找到方法,暂时搁置。

    4) You should consider using window.atob and window.btoa rather than your own base64 encoder and decoder.
    Please address the above issues and nominate again.
    放着以后再说。

    5) The extensions preferences have not the prefix "extensions." (so that they have the structure extensions.fillform.*)
    几个参数统一都加上extension前缀而已,已经修改了。

    6) Why do you write code like this?                     var sLocale = prefs.getCharPref("gene" + "ral.user" + "agent.locale");
    这个我也不想的,因为提交插件时总是提示这里通不过,好像是为了什么安全问题的。我也是没办法,于是就拆开来骗下自动检测的程序。没想通竟然通过了。


    新版本v1.2.10于4月6号初步审核通过。


    2014/1/16

    很久不关心这个扩展了,而且自己也很少用火狐了,主要是火狐之前的版本不兼容我的扩展,具体原因我也没去看打算放弃这扩展了。
    直到前几天有人联系上我,问我这个扩展代码方面的事情,才发现不知道什么时候代码已经被我从电脑中删了。令我惊奇的是,用户告诉我扩展是能正常使用的。在火狐26.0上测试还真是的。

    谷歌邮箱我都有几年没登录了,今天偶尔登录下看到里面有很多世界各地的用户发来的邮件,有意大利的用户问我能不能翻译个意大利语言的(目前有的是中文,英文,日文三个版本,其他国家和地区使用默认都是英语),有美国朋友跟我反应在某个网址打开时发生错误,还有很多评论,有称赞的有改进建议的,很是感动,我都抛弃了它,可用户还没有抛弃。

    我都忘了在火狐官网上的注册的账号了,试了几个密码都不对,点击忘记密码重置,在gmail里收到了邮件。发现下载量已经达到了3万多,每日用户数近3000人。 相当于10个人下载了只有1个人选择保留,呵呵。用户中国人占到了30%,英语国家的多。

    打算重拾,看看有哪些地方能改进的更新个新版本上去。 主要迫在眉睫的是快捷键冲突问题。

    不过最近估计还不现实,各种事情太多了,吃饭要紧啊。

    2014/1/18

    即将开发的V1.3.11要增加的功能:

    1.在复选框中增加两个项目功能:
       □ 填写数据时忽略disabled或readonly的元素
       □ 保存数据时候忽略disabled或readonly的元素
       □ 数据发生填写或者保存时的显示虚线框框提示

    2.修改快捷键Alt+W,这个快捷键被火狐自己占用了,目前状态无效,具体改成什么还不清楚,如果快捷键能够自定义就更牛了

    3.增加意大利语言,如果意大利那位国际友人能在发布新版本前将翻译结果提供过来的话

    4.将内置图标和火狐扩展网站上的图标一致起来,启用新图标

    5.处理联动问题。下拉框复选框等控件(checkbox、option 、selection等)勾选或者选择的时候尽量使用元素自带的方法,要能够触发事件,而不是仅仅设置个静态的值。

    6.修改AMO在1.2.10审核时候提出的为什么用prefs.getCharPref("gene" + "ral.user" + "agent.locale"); 这样奇怪的代码。




    展开全文
  • 如何防止重复提交

    千次阅读 2019-06-20 09:50:39
    4、使用浏览器历史记录重复提交表单。 5、浏览器重复的 HTTP 请求。 二、防止表单重复提交的方法 1、禁掉提交按钮 表单提交后使用 Javascript 使提交按钮disable。这种方法防止心急的用户多次点击按钮。但有...

    转载 如何防止重复提交

    一、有很多的应用场景都会遇到重复提交问题,比如:

    1、点击提交按钮两次。
    2、点击刷新按钮。
    3、使用浏览器后退按钮重复之前的操作,导致重复提交表单。
    4、使用浏览器历史记录重复提交表单。
    5、浏览器重复的 HTTP 请求。

    二、防止表单重复提交的方法

    1、禁掉提交按钮

    表单提交后使用 Javascript 使提交按钮disable。这种方法防止心急的用户多次点击按钮。但有个问题,如果客户端把 Javascript 给禁止掉,这种方法就无效了。

    2、Post/Redirect/Get 模式

    在提交后执行页面重定向,这就是所谓的 Post-Redirect-Get (PRG)模式。简言之,当用户提交了表单后,你去执行一个客户端的重定向,转到提交成功信息页面。

    这能避免用户按 F5 导致的重复提交,而其也不会出现浏览器表单重复提交的警告,也能消除按浏览器前进和后退按导致的同样问题。

    3、在 session 中存放一个特殊标志

    当表单页面被请求时,生成一个特殊的字符标志串,存在 session 中,同时放在表单的隐藏域里。接受处理表单数据时,检查标识字串是否存在,并立即从 session 中删除它,然后正常处理数据。如果发现表单提交里没有有效的标志串,这说明表单已经被提交过了,忽略这次提交。这使你的 web 应用有了更高级的 XSRF 保护。

    4、使用Cookie处理

    使用Cookie记录表单提交的状态,根据其状态可以检查是否已经提交表单。

    5、在数据库里添加约束

    在数据库里添加唯一约束或创建唯一索引,防止出现重复数据。这是最有效的防止重复提交数据的方法。

    在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。

    一、表单重复提交的常见应用场景

    有如下的form.jsp页面

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <!DOCTYPE HTML>
    <html>
      <head>
        <title>Form表单</title>
      </head>
      
      <body>
          <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post">
            用户名:<input type="text" name="username">
            <input type="submit" value="提交" id="submit">
        </form>
      </body>
    </html>
    

    复制代码
      form表单提交到DoFormServlet进行处理

    package xdp.gacl.session;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class DoFormServlet extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            //客户端是以UTF-8编码传输数据到服务器端的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码
            request.setCharacterEncoding("UTF-8");
            String userName = request.getParameter("username");
            try {
                //让当前的线程睡眠3秒钟,模拟网络延迟而导致表单重复提交的现象
                Thread.sleep(3*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("向数据库中插入数据:"+userName);
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    
    }
    

    如果没有进行form表单重复提交处理,那么在网络延迟的情况下下面的操作将会导致form表单重复提交多次

    1.1、场景一:在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交
      演示动画如下所示:

    1.2、场景二:表单提交后用户点击【刷新】按钮导致表单重复提交
    演示动画如下所示:

    点击浏览器的刷新按钮,就是把浏览器上次做的事情再做一次,因为这样也会导致表单重复提交。

    1.3、场景三:用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交
    演示动画如下所示:

    二、利用JavaScript防止表单重复提交

    既然存在上述所说的表单重复提交问题,那么我们就要想办法解决,比较常用的方法是采用JavaScript来防止表单重复提交,具体做法如下:

    修改form.jsp页面,添加如下的JavaScript代码来防止表单重复提交

    1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
     2 <!DOCTYPE HTML>
     3 <html>
     4   <head>
     5     <title>Form表单</title>
     6         <script type="text/javascript">
     7         var isCommitted = false;//表单是否已经提交标识,默认为false
     8         function dosubmit(){
     9             if(isCommitted==false){
    10                 isCommitted = true;//提交表单后,将表单是否已经提交标识设置为true
    11                 return true;//返回true让表单正常提交
    12             }else{
    13                 return false;//返回false那么表单将不提交
    14             }
    15         }
    16     </script>
    17   </head>
    18   
    19   <body>
    20       <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" onsubmit="return dosubmit()" method="post">
    21         用户名:<input type="text" name="username">
    22         <input type="submit" value="提交" id="submit">
    23     </form>
    24   </body>
    25 </html>
    

    我们看看使用了JavaScript来防止表单提交重复是否可以成功,运行效果如下:

    可以看到,针对"在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交"这个应用场景,使用JavaScript是可以解决这个问题的,解决的做法就是"用JavaScript控制Form表单只能提交一次"。

    除了用这种方式之外,经常见的另一种方式就是表单提交之后,将提交按钮设置为不可用,让用户没有机会点击第二次提交按钮,代码如下:

    1 function dosubmit(){
    2     //获取表单提交按钮
    3     var btnSubmit = document.getElementById("submit");
    4     //将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮
    5     btnSubmit.disabled= "disabled";
    6     //返回true让表单可以正常提交
    7     return true;
    8 }
    

    运行效果如下:

    另外还有一种做法就是提交表单后,将提交按钮隐藏起来,这种做法和将提交按钮设置为不可用是差不多的,个人觉得将提交按钮隐藏影响到页面布局的美观,并且可能会让用户误以为是bug(怎么我一点击按钮,按钮就不见了呢?用户可能会有这样的疑问),我个人在开发中用得比较多的是表单提交后,将提交按钮设置为不可用,反正使用JavaScript防止表单重复提交的做法都是差不多的,目的都是让表单只能提交一次,这样就可以做到表单不重复提交了。

    使用JavaScript防止表单重复提交的做法只对上述提交到导致表单重复提交的三种场景中的【场景一】有效,而对于【场景二】和【场景三】是没有用,依然无法解决表单重复提交问题。

    三、利用Session防止表单重复提交

    对于【场景二】和【场景三】导致表单重复提交的问题,既然客户端无法解决,那么就在服务器端解决,在服务器端解决就需要用到session了。

    具体的做法:在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
      在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

    存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
    当前用户的Session中不存在Token(令牌)。
    用户提交的表单数据中没有Token(令牌)。
    看具体的范例:

    1.创建FormServlet,用于生成Token(令牌)和跳转到form.jsp页面

     1 package xdp.gacl.session;
     2 
     3 import java.io.IOException;
     4 import javax.servlet.ServletException;
     5 import javax.servlet.http.HttpServlet;
     6 import javax.servlet.http.HttpServletRequest;
     7 import javax.servlet.http.HttpServletResponse;
     8 
     9 public class FormServlet extends HttpServlet {
    10     private static final long serialVersionUID = -884689940866074733L;
    11 
    12     public void doGet(HttpServletRequest request, HttpServletResponse response)
    13             throws ServletException, IOException {
    14 
    15         String token = TokenProccessor.getInstance().makeToken();//创建令牌
    16         System.out.println("在FormServlet中生成的token:"+token);
    17         request.getSession().setAttribute("token", token);  //在服务器使用session保存token(令牌)
    18         request.getRequestDispatcher("/form.jsp").forward(request, response);//跳转到form.jsp页面
    19     }
    20 
    21     public void doPost(HttpServletRequest request, HttpServletResponse response)
    22             throws ServletException, IOException {
    23         doGet(request, response);
    24     }
    25 
    26 }
    

    2.在form.jsp中使用隐藏域来存储Token(令牌)

     1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
     2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
     3 <html>
     4 <head>
     5 <title>form表单</title>
     6 </head>
     7 
     8 <body>
     9     <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post">
    10         <%--使用隐藏域存储生成的token--%>
    11         <%--
    12             <input type="hidden" name="token" value="<%=session.getAttribute("token") %>">
    13         --%>
    14         <%--使用EL表达式取出存储在session中的token--%>
    15         <input type="hidden" name="token" value="${token}"/> 
    16         用户名:<input type="text" name="username"> 
    17         <input type="submit" value="提交">
    18     </form>
    19 </body>
    20 </html>
    

    3.DoFormServlet处理表单提交

    1 package xdp.gacl.session;
     2 
     3 import java.io.IOException;
     4 import javax.servlet.ServletException;
     5 import javax.servlet.http.HttpServlet;
     6 import javax.servlet.http.HttpServletRequest;
     7 import javax.servlet.http.HttpServletResponse;
     8 
     9 public class DoFormServlet extends HttpServlet {
    10 
    11     public void doGet(HttpServletRequest request, HttpServletResponse response)
    12                 throws ServletException, IOException {
    13 
    14             boolean b = isRepeatSubmit(request);//判断用户是否是重复提交
    15             if(b==true){
    16                 System.out.println("请不要重复提交");
    17                 return;
    18             }
    19             request.getSession().removeAttribute("token");//移除session中的token
    20             System.out.println("处理用户提交请求!!");
    21         }
    22         
    23         /**
    24          * 判断客户端提交上来的令牌和服务器端生成的令牌是否一致
    25          * @param request
    26          * @return 
    27          *         true 用户重复提交了表单 
    28          *         false 用户没有重复提交表单
    29          */
    30         private boolean isRepeatSubmit(HttpServletRequest request) {
    31             String client_token = request.getParameter("token");
    32             //1、如果用户提交的表单数据中没有token,则用户是重复提交了表单
    33             if(client_token==null){
    34                 return true;
    35             }
    36             //取出存储在Session中的token
    37             String server_token = (String) request.getSession().getAttribute("token");
    38             //2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单
    39             if(server_token==null){
    40                 return true;
    41             }
    42             //3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单
    43             if(!client_token.equals(server_token)){
    44                 return true;
    45             }
    46             
    47             return false;
    48         }
    49 
    50     public void doPost(HttpServletRequest request, HttpServletResponse response)
    51             throws ServletException, IOException {
    52         doGet(request, response);
    53     }
    54 
    55 }
    

    生成Token的工具类TokenProccessor

     1 package xdp.gacl.session;
     2 
     3 import java.security.MessageDigest;
     4 import java.security.NoSuchAlgorithmException;
     5 import java.util.Random;
     6 import sun.misc.BASE64Encoder;
     7 
     8 public class TokenProccessor {
     9 
    10     /*
    11      *单例设计模式(保证类的对象在内存中只有一个)
    12      *1、把类的构造函数私有
    13      *2、自己创建一个类的对象
    14      *3、对外提供一个公共的方法,返回类的对象
    15      */
    16     private TokenProccessor(){}
    17     
    18     private static final TokenProccessor instance = new TokenProccessor();
    19     
    20     /**
    21      * 返回类的对象
    22      * @return
    23      */
    24     public static TokenProccessor getInstance(){
    25         return instance;
    26     }
    27     
    28     /**
    29      * 生成Token
    30      * Token:Nv6RRuGEVvmGjB+jimI/gw==
    31      * @return
    32      */
    33     public String makeToken(){  //checkException
    34         //  7346734837483  834u938493493849384  43434384
    35         String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
    36         //数据指纹   128位长   16个字节  md5
    37         try {
    38             MessageDigest md = MessageDigest.getInstance("md5");
    39             byte md5[] =  md.digest(token.getBytes());
    40             //base64编码--任意二进制编码明文字符   adfsdfsdfsf
    41             BASE64Encoder encoder = new BASE64Encoder();
    42             return encoder.encode(md5);
    43         } catch (NoSuchAlgorithmException e) {
    44             throw new RuntimeException(e);
    45         }
    46     }
    47 }
    

    首先访问FormServlet,在FormServlet中生成Token之后再重定向到form.jsp页面,这次是在服务器端处理表单重复提交的,运行效果如下:

    从运行效果中可以看到,通过这种方式处理表单重复提交,可以解决上述的场景二和场景三中出现的表单重复提交问题。

    展开全文
  • javaweb:在线聊天网站(框架版)

    万次阅读 多人点赞 2018-08-19 11:04:32
    之前写过一次在线聊天网站,不过那次是无框架版的,这次用框架构建网站,基本功能和上次差不多 涉及知识 java spring(4.3.5):spring、spring MVC hibernate bootstrap jsp JavaScript,jquery ...
  • 第1章Java Web应用概述 1.1认识Web应用 1.1.1Web应用的运行过程(用户的使用过程) 1.1.2Web应用 1.1.3运行环境支持 1.1.4HTML语言 1.1.5HTTP协议 1.1.6特点 1.1.7B/S结构
  • Flask Web 开发学习稿(二)

    千次阅读 2016-05-31 17:37:43
    第四章 Web 表单request.from 能获取 POST 请求中提交的表单数据 Flask-WTF 扩展可以把处理 Web 表单的过程变成一种愉悦的体验4.1 跨站请求伪造保护默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造的攻击,...
  • 表单防止 多次提交的4种方法

    千次阅读 2019-03-14 19:49:10
    表单重复提交是在web开发中经常遇到的问题,顾名思义就是指用户有意或无意对同一个表单提交了两次(多次),轻则引起系统数据出现重复,重则引起系统业务逻辑异常,导致系统数据错乱。一方面向数据库中插入大量的...
  • HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则。根据HTTP标准,HTTP请求可以使用多种请求方法。 HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。 HTTP1.1新增了五种请求方法:OPTIONS...
  • java防止页面刷新重复提交

    万次阅读 2012-05-02 16:32:20
    生成一个令牌保存在用户session中,在form中加一个hidden域,显示该令 牌的值,form提交后重新生成一个新的令牌,将用户提交的令牌和session 中的令牌比较,如相同则是重复提交 3 在你的服务器端控件的代码...
  • 一、创建项目1.1.创建项目和appdjango-admin startproject mysite_login python manage.py startapp login1.2.设置时区和语言Django默认使用美国时间和英语,在项目的settings文件中,如下所示:LANGUAGE_CODE = '...
  • 两种 HTTP 请求方法:GET 和 POST

    万次阅读 2016-10-24 11:16:39
    两种 HTTP 请求方法:GET 和 POST 在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是:GET 和 POST。 GET - 从指定的资源请求数据。POST - 向指定的资源提交要被处理的数据 ...
  • web网页测试用例(非常实用)

    万次阅读 多人点赞 2018-02-06 15:21:14
    Web测试中,各类web控件测试点总结 一 、界面检查  进入一个页面测试,首先是检查title,页面排版,字段等,而不是马上进入文本框校验  1、页面名称title是否正确  2、当前位置是否可见 您的位置:xxx>xxxx ...
  • postman安装与简要操作

    万次阅读 2018-08-16 10:43:00
    1、Postman接口测试...它提供功能强大的 Web API &amp; HTTP 请求调试。它能够发送任何类型的HTTP 请求 (GET, HEAD, POST, PUT..),附带任何数量的参数+ headers。 Postman功能: 主要用于模拟网络请求包...
  • 19个方便渗透测试的Chrome扩展

    千次阅读 2014-12-19 21:44:01
    Google Chrome是目前世界上最流行的浏览器之一,简洁的界面让他俘获了许多用户,除此之外还具有许多改善浏览体验的功能。与Firefox一样,Chrome也支持插件,称之为“扩展”,这些扩展程序极大地拓展了Chrome的公呢个...
  • postman详细的中文使用教程

    万次阅读 2018-11-29 13:57:15
    web页面开发和测试的人员应该是无人不晓无人不用!其主要特点特点: 创建 + 测试:创建和发送任何的HTTP请求,请求可以保存历史中再次执行 Organize:使用Postman Collections为更有效的测试及集成工作流管理和...
  • Java Web整合开发王者归来(含源代码) 并且源代码比光碟中的新,光盘中的代码有部分不全,有两个项目没有,现已打上补丁。 PDF文件解压缩后324M,源代码压缩文件65.5M 下载地址:...
  • fiddler 抓包详细教程

    万次阅读 多人点赞 2019-07-04 14:02:15
    为什么要先学fiddler? 学习接口测试必学http协议,如果直接先讲协议,我估计小伙伴们更懵,为了更好的理解协议,先从抓包开始。 结合抓包工具讲http协议更容易学... ... 一、网页不安全 1.用fiddler抓包时候,打开百...
  • Activiti6详细教程

    万次阅读 多人点赞 2020-07-13 15:14:04
    一、为什么选择Activiti activiti介绍 Activiti是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理,工作流,服务协作等领域的一个开源,灵活的,易扩展的可执行流程语言框架。...
1 2 3 4 5 ... 20
收藏数 6,819
精华内容 2,727
热门标签
关键字:

web 修改form表单并保存历史