手机端远程通过机智云平台和esp8266控制led灯开关

通过机智云平台生成的固件,刷入到esp8266中,实现物联网基本功能。

图例:产品引脚功能说明

esp8266官网:https://www.espressif.com/zh-hans/home

机智云官网:http://www.gizwits.com

 

以下实例通过esp8266控制led灯。

参考地址:http://docs.gizwits.com/zh-cn/deviceDev/UseSOC.html

无交叉编译环境时的开发方式(验证可行)

ESP8266 GPIO入门:http://blog.csdn.net/zhipun/article/details/60572335

GPIO控制流水灯:http://blog.csdn.net/qq236106303/article/details/60331497

Android端通过socket实现手机控制8路继电器板

手里有一个8路继电器控制板子,可通过web和socket两种方式控制,板子为server端,本文中使用socket方式,在Android上编写client端连接到板子实现手机控制继电器开关。

 

参考文章:Android:这是一份很详细的Socket使用攻略 (这里比较形象的介绍了socket的原理及使用)

 

安卓端实现如下:

一、添加网络权限

配置文件AndroidMainfest.xml中添加如下代码

<uses-permission android:name="android.permission.INTERNET" />

注意位置为

    </application>
    <uses-permission android:name="android.permission.INTERNET" />

</manifest>

 

二、布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="tech.linfeng.linfeng.MainActivity">

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:text="IP"
        android:ems="10"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_marginStart="14dp"
        android:layout_marginTop="14dp"
        android:id="@+id/edit" />

    <Button
        android:text="send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/send"
        android:layout_alignTop="@+id/edit"
        android:layout_alignParentEnd="true" />

    <TextView
        android:text="msg"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@+id/send"
        android:layout_alignStart="@+id/edit"
        android:layout_marginTop="24dp"
        android:id="@+id/receive_message" />

    <Button
        android:text="connect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/receive_message"
        android:layout_alignStart="@+id/receive_message"
        android:layout_marginTop="25dp"
        android:id="@+id/connect" />

    <Button
        android:text="disconn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/connect"
        android:layout_centerHorizontal="true"
        android:id="@+id/disconnect" />

    <Button
        android:text="Receive"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/disconnect"
        android:layout_alignParentEnd="true"
        android:layout_marginEnd="12dp"
        android:id="@+id/Receive" />

    <Button
        android:text="L1ON"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/connect"
        android:layout_alignStart="@+id/connect"
        android:layout_marginTop="32dp"
        android:id="@+id/L1on" />

    <Button
        android:text="L1OFF"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/L1off"
        android:layout_alignBaseline="@+id/L1on"
        android:layout_alignBottom="@+id/L1on"
        android:layout_alignStart="@+id/disconnect" />

    <Button
        android:text="Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/L1off"
        android:layout_alignStart="@+id/Receive"
        android:id="@+id/button7" />

    <Button
        android:text="Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/L1on"
        android:layout_alignStart="@+id/L1on"
        android:layout_marginTop="36dp"
        android:id="@+id/button8" />

    <Button
        android:text="Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/button8"
        android:layout_alignStart="@+id/L1off"
        android:id="@+id/button9" />

    <Button
        android:text="Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/button9"
        android:layout_alignStart="@+id/button7"
        android:id="@+id/button10" />

</RelativeLayout>

 

三、JAVA文件

package tech.linfeng.linfeng;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {

    /**
     * 主 变量
     */

    // 主线程Handler
    // 用于将从服务器获取的消息显示出来
    private Handler mMainHandler;

    // Socket变量
    private Socket socket;

    // 线程池
    // 为了方便展示,此处直接采用线程池进行线程管理,而没有一个个开线程
    private ExecutorService mThreadPool;

    /**
     * 接收服务器消息 变量
     */
    // 输入流对象
    InputStream is;

    // 输入流读取器对象
    InputStreamReader isr ;
    BufferedReader br ;

    // 接收服务器发送过来的消息
    String response;


    /**
     * 发送消息到服务器 变量
     */
    // 输出流对象
    OutputStream outputStream;

    /**
     * 按钮 变量
     */

    // 连接 断开连接 发送数据到服务器 的按钮变量
    private Button btnConnect, btnDisconnect, btnSend, L1on, L1off;

    // 显示接收服务器消息 按钮
    private TextView Receive,receive_message;

    // 输入需要发送的消息 输入框
    private EditText mEdit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**
         * 初始化操作
         */

        // 初始化所有按钮
        btnConnect = (Button) findViewById(R.id.connect);
        btnDisconnect = (Button) findViewById(R.id.disconnect);
        btnSend = (Button) findViewById(R.id.send);
        mEdit = (EditText) findViewById(R.id.edit);
        receive_message = (TextView) findViewById(R.id.receive_message);
        Receive = (Button) findViewById(R.id.Receive);
        L1on = (Button) findViewById(R.id.L1on);
        L1off = (Button) findViewById(R.id.L1off);

        // 初始化线程池
        mThreadPool = Executors.newCachedThreadPool();


        // 实例化主线程,用于更新接收过来的消息
        mMainHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 0:
                        receive_message.setText(response);
                        break;
                }
            }
        };


        /**
         * 创建客户端 & 服务器的连接
         */
        btnConnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 利用线程池直接开启一个线程 & 执行该线程
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {

                        try {

                            // 创建Socket对象 & 指定服务端的IP 及 端口号
                            socket = new Socket("192.168.8.90", 1234);

                            // 判断客户端和服务器是否连接成功
                            System.out.println(socket.isConnected());

                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                });

            }
        });

        /**
         * 接收 服务器消息
         */
        Receive.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 利用线程池直接开启一个线程 & 执行该线程
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {

                        try {
                            // 步骤1:创建输入流对象InputStream
                            is = socket.getInputStream();

                            // 步骤2:创建输入流读取器对象 并传入输入流对象
                            // 该对象作用:获取服务器返回的数据
                            isr = new InputStreamReader(is);
                            br = new BufferedReader(isr);

                            // 步骤3:通过输入流读取器对象 接收服务器发送过来的数据
                            response = br.readLine();

                            // 步骤4:通知主线程,将接收的消息显示到界面
                            Message msg = Message.obtain();
                            msg.what = 0;
                            mMainHandler.sendMessage(msg);

                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                });

            }
        });


        /**
         * 发送消息 给 服务器
         */
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 利用线程池直接开启一个线程 & 执行该线程
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {

                        try {
                            // 步骤1:从Socket 获得输出流对象OutputStream
                            // 该对象作用:发送数据
                            outputStream = socket.getOutputStream();

                            // 步骤2:写入需要发送的数据到输出流对象中
                            outputStream.write((mEdit.getText().toString()+"\n").getBytes("utf-8"));
                            // 特别注意:数据的结尾加上换行符才可让服务器端的readline()停止阻塞

                            // 步骤3:发送数据到服务端
                            outputStream.flush();

                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                });

            }
        });


        /**
         * 断开客户端 & 服务器的连接
         */
        btnDisconnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                try {
                    // 断开 客户端发送到服务器 的连接,即关闭输出流对象OutputStream
                    outputStream.close();

                    // 断开 服务器发送到客户端 的连接,即关闭输入流读取器对象BufferedReader
                    br.close();

                    // 最终关闭整个Socket连接
                    socket.close();

                    // 判断客户端和服务器是否已经断开连接
                    System.out.println(socket.isConnected());

                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        });

        /**
         * 继电器1开
         */
        L1on.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 利用线程池直接开启一个线程 & 执行该线程
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {

                        try {
                            // 步骤1:从Socket 获得输出流对象OutputStream
                            // 该对象作用:发送数据
                            outputStream = socket.getOutputStream();



                            // 步骤2:写入需要发送的数据到输出流对象中
                            outputStream.write(("L1"+"\n").getBytes("utf-8"));
                            // 特别注意:数据的结尾加上换行符才可让服务器端的readline()停止阻塞

                            // 步骤3:发送数据到服务端
                            outputStream.flush();

                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                });

            }
        });


        /**
         * 继电器1关
         */
        L1off.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 利用线程池直接开启一个线程 & 执行该线程
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {

                        try {
                            // 步骤1:从Socket 获得输出流对象OutputStream
                            // 该对象作用:发送数据
                            outputStream = socket.getOutputStream();



                            // 步骤2:写入需要发送的数据到输出流对象中
                            outputStream.write(("D1"+"\n").getBytes("utf-8"));
                            // 特别注意:数据的结尾加上换行符才可让服务器端的readline()停止阻塞

                            // 步骤3:发送数据到服务端
                            outputStream.flush();

                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                });

            }
        });


    }
}

 

python实现的json数据以HTTP GET,POST,PUT,DELETE方式页面请求

【转载】文章转载自光阴过客

一、JSON简介

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。
它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition – December 1999的一个子集。
JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。
这些特性使JSON成为理想的数据交换语言。

二、HTTP的请求方法

HTTP/1.1协议中共定义了八种方法(有时也叫“动作”)来表明Request-URI指定的资源的不同操作方式:
. OPTIONS – 返回服务器针对特定资源所支持的HTTP请求方法。
也可以利用向Web服务器发送’*’的请求来测试服务器的功能性。
. HEAD    – 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。
这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。
. GET     – 向特定的资源发出请求。
注意:GET方法不应当被用于产生“副作用”的操作中,例如在web app.中。
其中一个原因是GET可能会被网络蜘蛛等随意访问。
. POST    – 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。
数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
. PUT     – 向指定资源位置上传其最新内容。
. DELETE  – 请求服务器删除Request-URI所标识的资源。
. TRACE   – 回显服务器收到的请求,主要用于测试或诊断。
. CONNECT – HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
. PATCH   – 用来将局部修改应用于某一资源,添加于规范RFC5789。

其中,GET,POST, PUT, DELETE常用于RESTful API的实现,所以下面做的代码实现

三、Python实现的json数据以HTTP GET,POST,PUT,DELETE方式进行页面请求

闲言少述,直接上代码.

1. GET方法

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# File: http_get.py

import urllib2

def http_get():
url=’http://192.168.1.13:9999/test’   #页面的地址
response = urllib2.urlopen(url)         #调用urllib2向服务器发送get请求
return response.read()                     #获取服务器返回的页面信息

ret = http_get()
print(“RET %r” % (ret))

2. POST方法

#!/usr/bin/env python
#  -*- coding:utf-8 -*-
# File http_post.py

import urllib
import urllib2
import json

def http_post():
url=’http://192.168.1.13:9999/test’
values ={‘user’:’Smith’,’passwd’:’123456}

jdata = json.dumps(values)             # 对数据进行JSON格式化编码
req = urllib2.Request(url, jdata)       # 生成页面请求的完整数据
response = urllib2.urlopen(req)       # 发送页面请求
return response.read()                    # 获取服务器返回的页面信息

resp = http_post()
print resp

3. PUT方法

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# File: http_put.py

import urllib2
import json

def http_put():
url=’http://192.168.1.13:9999/test’
values={”:”}

jdata = json.dumps(values)                  # 对数据进行JSON格式化编码
request = urllib2.Request(url, jdata)
request.add_header(‘Content-Type’, ‘your/conntenttype’)
request.get_method = lambda:’PUT’           # 设置HTTP的访问方式
request = urllib2.urlopen(request)
return request.read()

resp = http_put()
print resp

4. DELETE方法

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# File: http_delete.py

import urllib2
import json

def http_delete():
url=’http://192.168.1.13:9999/test’
values={‘user’:’Smith’}

jdata = json.dumps(values)
request = urllib2.Request(url, jdata)
request.add_header(‘Content-Type’, ‘your/conntenttype’)
request.get_method = lambda:’DELETE’        # 设置HTTP的访问方式
request = urllib2.urlopen(request)
return request.read()

resp = http_delete()
print resp

Python Flask Restful API

【转载】文章转载自bestallen

目前Web应用这块,restufl API用得非常普遍,因为,你手上的前端设备五花八门,各种系统的手机,pad等等,而且网站和手机APP经常会有需要资源共享的时候。

如果网站做个app,手机端再独立一个app,要保持2者同步,估计做开发的要做死了。

所以,RESTFUL API提出了一个概念,就是资源为上,通俗地讲:就是,我有一个资源A,可以作为接口来提供出来,那么,前端设备的B,C,D 都可以通过申请这个API接口的方式,来进行获取资源,而最后显示画面如何渲染,让客户看到是怎么样的一个样子,那是前端的事情了。

学习API之前,有两个前置技能需要去了解下,一个是JSON数据格式,另外一个是curl或者HTTPie这样的http调试工具,我这里用的是HTTPie

最简单的Web API

先来看看通过Python Flask做一个非常简单的Web API接口范例

[python] view plain copy

  1. from flask import Flask,jsonify
  2. app = Flask(__name__)
  3. tasks =[
  4.     {
  5.         ‘id’:1,
  6.         ‘title’: u‘Buy groceries’,
  7.         ‘description’: u‘Milk, Cheese, Pizza, Fruit, Tylenol’,
  8.         ‘done’False
  9.     },
  10.     {
  11.         ‘id’2,
  12.         ‘title’: u‘Learn Python’,
  13.         ‘description’: u‘Need to find a good Python tutorial on the web’,
  14.         ‘done’False
  15.     }]
  16. @app.route(‘/’)
  17. def index():
  18.     return ‘Hello,world!’
  19. @app.route(‘/todo/api/v1.0/tasks’,methods=[‘GET’])
  20. def get_tasks():
  21.     return jsonify({‘tasks’:tasks})

如上面代码,当你请求 /todo/api/v1.0/tasks 的时候,通过路由get_tasks,就能返回当前所有的数据信息

他是通过jsonify来进行返回的,这里需要注意的是,有2种方法可以返回json数据,一个是jsonify,还有一个是json.dumps()

那么2者有什么区别呢?可以看下图

jsonify直接返回的是Content-type:application/json的响应对象(Response对象)

json.dumps返回的,则是Content-type:text/html,charset=utf-8的HTML格式

我们可以看下jsonfiy的官方解释


来看下效果图及两者的区别

返回单个数据

刚才是获取了所有的任务,那如果我需要其中某一个数据的话,如何做呢?

[python] view plain copy

  1. @app.route(‘/todo/api/v1.0/tasks/<int:task_id>’,methods=[‘GET’])
  2. def get_task(task_id):
  3.     task = list(filter(lambda t:t[‘id’]==task_id,tasks))   #检查tasks内部元素,是否有元素的id的值和参数id相匹配
  4.     if len(task)==0:                                       #有的话,就返回列表形式包裹的这个元素,如果没有,则报错404
  5.         abort(404)
  6.     return jsonify({‘tasks’:task[0]})                      #否则,将这个task以JSON的响应形式返回

在URL里加入变量,通过检查tasks内部每个元素的id对应的值,是否有和输入的id匹配的,如果有匹配的,那就返回成一个list给task变量

比如下面,他就获取了id为2的这个数据的内容,当然,如果你在URL后面添加的是3,那他就会报错了,因为暂时还没有id为3的这个数据

上面的报错画面有些难看,而且,这样的报错格式,不太利于别人接口的使用。

一般像正规的接口,他都会以JSON格式返回error内容或者代码,所以,我们也要优化一下这样的错误信息。

[python] view plain copy

  1. @app.errorhandler(404)
  2. def not_found(error):
  3.     return jsonify({‘error’:‘Not found’}),404

 

这样,返回的格式,也是以JSON的格式,那样,如果别人调用你的接口,如果发生错误,别人就可以通过error信息,来进行渲染了。

POST方法添加信息

接下来,既然有数据,那肯定会涉及到添加数据,那我们就要用到POST方法了,就和上传FORM表单的性质是一样的

HTTPie的好处是,他默认的POST格式是JSON的,所以,你不必特意指定格式,只要写一个POST,就ok,如下

[python] view plain copy

  1. @app.route(‘/todo/api/v1.0/tasks’,methods=[‘POST’])
  2. def create_task():
  3.     if not request.json or not ‘title’ in request.json:   #如果请求里面没有JSON数据,或者在JSON数据里面,title的内容是空的
  4.         abort(404)                                    #返回404报错
  5.     task = {
  6.         ‘id’:tasks[-1][‘id’]+1,                       #取末尾task的id号,并加一作为新的数据的id号
  7.         ‘title’:request.json[‘title’],                #title必须要设置,不能为空
  8.         ‘description’:request.json.get(‘description’,””),     #描述可以添加,但是也可以不写,默认为空
  9.         ‘done’:False
  10.     }
  11.     tasks.append(task)                                    #完了以后,添加这个task进tasks列表
  12.     return jsonify({‘task’:task}),201                     #并返回这个添加的task内容,和状态码

 

后面加入的信息,你写title=test或者title=”test”都可以


PUT方法修改和DELETE方法删除

既然已经有了添加的功能,那么修改和删除的也必不可少,如下

[python] view plain copy

  1. @app.route(‘/todo/api/v1.0/tasks/<int:task_id>’,methods=[‘PUT’])
  2. def update_task(task_id):
  3.     task = list(filter(lambda t:t[‘id’]==task_id,tasks))     #检查是否有这个id的数据
  4.     if len(task)==0:
  5.         abort(404)
  6.     if not request.json:                                     #如果请求中没有附带json数据,则报错400
  7.         abort(400)
  8.     if ‘title’ in request.json and not isinstance(request.json[‘title’],str):   #如果title对应的值,不是字符串类型,则报错400
  9.         abort(400)
  10.     if ‘description’ in request.json and not isinstance(request.json[‘description’],str):  #同上,检查description对应的值是否为字符串
  11.         abort(400)
  12.     if ‘done’ in request.json and not isinstance(request.json[‘done’],bool):    #检查done对应的值是否是布尔值
  13.         abort(400)
  14.     task[0][‘title’] = request.json.get(‘title’,task[0][‘title’])          #如果上述条件全部通过的话,更新title的值,同时要设置默认值
  15.     task[0][‘description’] = request.json.get(‘description’, task[0][‘description’])  #修改description值
  16.     task[0][‘done’] = request.json.get(‘done’, task[0][‘done’])            #修改done的值
  17.     return jsonify({‘task’:task[0]})                                       #最后,返回修改后的数据
  18. @app.route(‘/todo/api/v1.0/tasks/<int:task_id>’,methods=[‘DELETE’])
  19. def delete_task(task_id):
  20.     task = list(filter(lambda t: t[‘id’]==task_id,tasks))     #检查是否有这个数据
  21.     if len(task) ==0:
  22.         abort(404)
  23.     tasks.remove(task[0])                                     #从tasks列表中删除这个值
  24.     return jsonify({‘result’True})                          #返回结果状态,自定义的result

优化接口

这样做接口系统,功能是达到了,但是你不见得让用户去背你的URL吧?这样不现实,像我们平时用的话,最好是可以自动生成URL

所以,这里我们需要写一个辅助函数,来返回一个完整的URL,这样可以就可以直接拿着URL用了

[python] view plain copy

  1. def make_public_task(task):
  2.     new_task={}             #新建一个对象,字典类型
  3.     for key in task:        #遍历字典内部的KEY
  4.         if key == ‘id’#当遍历到id的时候,为新对象增加uri的key,对应的值为完整的uri
  5.             new_task[‘uri’] = url_for(‘get_task’,task_id=task[‘id’],_external=True)
  6.         else:
  7.             new_task[key] = task[key]  #其他的key,分别一一对应加入新对象
  8.     return new_task                            #最后返回新对象数据

这样的话,你获取数据合集的路由,也需要修改了

[python] view plain copy

  1. @app.route(‘/todo/api/v1.0/tasks’,methods=[‘GET’])
  2. def get_tasks():
  3.     return jsonify({‘tasks’: list(map(make_public_task,tasks))})  #使用map函数,罗列出所有的数据,返回的数据信息,是经过辅助函数处理的

也就是说,经过辅助函数处理,你就可以直接获取整体URI了,而不是单个id号

接口安全性

接口的应用功能上,已经做得差不多了,但是现在的接口外圈是对外开放的,如果有人恶意破坏,数据就有可能有危险

所以,这里引入了认证机制,其实简单来说就是要求客户端的用户有使用使用权限,比如有账号和密码。

这里引入HTTPBasicAuth

auth=HTTPBasicAuth()

[python] view plain copy

  1. @auth.get_password
  2. def get_password(username):
  3.     if username == “allen”:
  4.         return “python”
  5.     return None
  6. @auth.error_handler
  7. def unauthorized():
  8.     return make_response(jsonify({‘error’:‘Unauthorized access’}),401)

通过客户端发送的用户名和密码,匹配get_password函数内部这个用户名和密码是否匹配

如果没有用户名密码,或者说是错误的,则返回401错误

于是,像查看资料啊这些的路由,都要给他挂一个login_required的装饰器了

[python] view plain copy

  1. @app.route(‘/todo/api/v1.0/tasks’,methods=[‘GET’])
  2. @auth.login_required
  3. def get_tasks():
  4.     return jsonify({‘tasks’: list(map(make_public_task,tasks))})

如果你没有附带用户名密码,那么,就会禁止访问了

附带了用户名和密码的话,就可以有权限进行操作

python_socket

python下tcp服务也分为服务端和客户端

实测服务端在ubuntu下,客户端在windows下正常运行

*客户端运行在ubuntu下会提示input报错,替换为固定str正常数据传输,正在查找原因……

(问题找到了,是输入用input未加“双引号”引起的输入不合法,可通过raw_input函数解决,点击链接到原文查看详细解决办法)

继续阅读python_socket

HTTP中Get与Post的区别

Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。

1.根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的。

(1).所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。

* 注意:这里安全的含义仅仅是指是非修改信息。

(2).幂等的意味着对同一URL的多个请求应该返回同样的结果。这里我再解释一下幂等这个概念:

幂等(idempotent、idempotence)是一个数学或计算机学概念,常见于抽象代数中。
幂等有一下几种定义:
对于单目运算,如果一个运算对于在范围内的所有的一个数多次进行该运算所得的结果和进行一次该运算所得的结果是一样的,那么我们就称该运算是幂等的。比如绝对值运算就是一个例子,在实数集中,有abs(a)=abs(abs(a))。
对于双目运算,则要求当参与运算的两个值是等值的情况下,如果满足运算结果与参与运算的两个值相等,则称该运算幂等,如求两个数的最大值的函数,有在在实数集中幂等,即max(x,x) = x。

看完上述解释后,应该可以理解GET幂等的含义了。

但在实际应用中,以上2条规定并没有这么严格。引用别人文章的例子:比如,新闻站点的头版不断更新。虽然第二次请求会返回不同的一批新闻,该操作仍然被认为是安全的和幂等的,因为它总是返回当前的新闻。从根本上说,如果目标是当用户打开一个链接时,他可以确信从自身的角度来看没有改变资源即可。

2.根据HTTP规范,POST表示可能修改变服务器上的资源的请求。继续引用上面的例子:还是新闻以网站为例,读者对新闻发表自己的评论应该通过POST实现,因为在评论提交后站点的资源已经不同了,或者说资源被修改了。

 

上面大概说了一下HTTP规范中GET和POST的一些原理性的问题。但在实际的做的时候,很多人却没有按照HTTP规范去做,导致这个问题的原因有很多,比如说:

1.很多人贪方便,更新资源时用了GET,因为用POST必须要到FORM(表单),这样会麻烦一点。

2.对资源的增,删,改,查操作,其实都可以通过GET/POST完成,不需要用到PUT和DELETE。

3.另外一个是,早期的Web MVC框架设计者们并没有有意识地将URL当作抽象的资源来看待和设计,所以导致一个比较严重的问题是传统的Web MVC框架基本上都只支持GET和POST两种HTTP方法,而不支持PUT和DELETE方法。

* 简单解释一下MVC:MVC本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。

以上3点典型地描述了老一套的风格(没有严格遵守HTTP规范),随着架构的发展,现在出现REST(Representational State Transfer),一套支持HTTP规范的新风格,这里不多说了,可以参考《RESTful Web Services》。

 

说完原理性的问题,我们再从表面现像上面看看GET和POST的区别:

1.GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连,如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。

POST把提交的数据则放置在是HTTP包的包体中。

2.”GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据,IIS4中最大为80KB,IIS5中为100KB”??!

以上这句是我从其他文章转过来的,其实这样说是错误的,不准确的:

(1).首先是”GET方式提交的数据最多只能是1024字节”,因为GET是通过URL提交数据,那么GET可提交的数据量就跟URL的长度有直接关系了。而实际上,URL不存在参数上限的问题,HTTP协议规范没有对URL长度进行限制。这个限制是特定的浏览器及服务器对它的限制。IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。

注意这是限制是整个URL长度,而不仅仅是你的参数值数据长度。[见参考资料5]

(2).理论上讲,POST是没有大小限制的,HTTP协议规范也没有进行大小限制,说“POST数据量存在80K/100K的大小限制”是不准确的,POST数据是没有限制的,起限制作用的是服务器的处理程序的处理能力。

对于ASP程序,Request对象处理每个表单域时存在100K的数据长度限制。但如果使用Request.BinaryRead则没有这个限制。

由这个延伸出去,对于IIS 6.0,微软出于安全考虑,加大了限制。我们还需要注意:

1).IIS 6.0默认ASP POST数据量最大为200KB,每个表单域限制是100KB。
2).IIS 6.0默认上传文件的最大大小是4MB。
3).IIS 6.0默认最大请求头是16KB。
IIS 6.0之前没有这些限制。[见参考资料5]

所以上面的80K,100K可能只是默认值而已(注:关于IIS4和IIS5的参数,我还没有确认),但肯定是可以自己设置的。由于每个版本的IIS对这些参数的默认值都不一样,具体请参考相关的IIS配置文档。

3.在ASP中,服务端获取GET请求参数用Request.QueryString,获取POST请求参数用Request.Form。在JSP中,用request.getParameter(\”XXXX\”)来获取,虽然jsp中也有request.getQueryString()方法,但使用起来比较麻烦,比如:传一个test.jsp?name=hyddd&password=hyddd,用request.getQueryString()得到的是:name=hyddd&password=hyddd。在PHP中,可以用$_GET和$_POST分别获取GET和POST中的数据,而$_REQUEST则可以获取GET和POST两种请求中的数据。值得注意的是,JSP中使用request和PHP中使用$_REQUEST都会有隐患,这个下次再写个文章总结。

4.POST的安全性要比GET的安全性高。注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击。

总结一下,Get是向服务器发索取数据的一种请求,而Post是向服务器提交数据的一种请求,在FORM(表单)中,Method默认为”GET”,实质上,GET和POST只是发送机制不同,并不是一个取一个发!

 

转载自[hyddd(http://www.cnblogs.com/hyddd/)]