【原创】网站抓包HttpWebRequest不返回Javascript生成的Cookie的解决办法 - 0ng_Ching_Tong - 博客园

mikel阅读(1243)

来源: 【原创】网站抓包HttpWebRequest不返回Javascript生成的Cookie的解决办法 – 0ng_Ching_Tong – 博客园

前言:

最近在做中国移动爬虫的过程中,首先遇到的就是 在某个请求中,有一个名为“WT_PFC”的cookie键值是由前端JavaScript生成的,没有进入到HttpWebResponse中,也就是 说C#不回去执行客户端脚本 ,HttpWebRequest不是一个真正意义上的web浏览器,它只会下载它所请求的地址的html信息,它永远不会去执行JavaScript或者 ajax。

但是由于其他的请求的Request需要Sent该Cookie,所以查了很多资料,基本上只能 重新构建 js 算法 或者使用 WebBrowser自动去执行页面js, 但这些都不是最好最快的方法。我采用的是以下的

C# 代码动态编译JavaScript代码的方式得出 JavaScript函数被调用之后的 返回值。

 

1.Cookie(WT_FPC):

 

2. 通过HttpWatch查到的生成该cookie的js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static function GetWT_FPC(){
     
    var $t = "2";
    var $u = new Date();
    var $v = new Date($u.getTime() + 315360000000);
    var $w = new Date($u.getTime());
     
    if ($t.length < 10) {
        var $x = $u.getTime().toString();
        for (var i = 2; i <= (32 - $x.length); i++) $t += Math.floor(Math.random() * 16.0).toString(16);
        $t += $x;
    };
    $t = encodeURIComponent($t);
    return "WT_FPC=id=" + $t + ":lv=" + $u.getTime().toString() + ":ss=" + $w.getTime().toString() ;
    };

请注意:请按以上的格式书写 脚本函数,即加上”public static “.

 

3.JsHelper(动态编译Js代码):

我把上面js代码放到本地”WT_FPC.js”文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static class JsHelper {
        /// <summary>
        /// 执行JS方法
        /// </summary>
        /// <param name="methodName">方法名</param>
        /// <param name="para">参数</param>
        /// <returns></returns>
        public static string GetJsMethd(string methodName, object[] para) {
            string path = AppDomain.CurrentDomain.BaseDirectory + "WT_FPC.js";
            string str2 = File.ReadAllText(path);
            StringBuilder sb = new StringBuilder();
            sb.Append("package aa{");   
            sb.Append(" public class JScript {");
        
            sb.Append(str2);
            sb.Append("}}");
              
            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateInMemory = true;
            CodeDomProvider _provider = new Microsoft.JScript.JScriptCodeProvider();
            CompilerResults results = _provider.CompileAssemblyFromSource(parameters, sb.ToString());
            Assembly assembly = results.CompiledAssembly;
            Type _evaluateType = assembly.GetType("aa.JScript");
            object obj = _evaluateType.InvokeMember("GetWT_FPC", BindingFlags.InvokeMethod,
            null, null, para);
            return obj.ToString();
        }
    }

注意:以上的helper代码如果报错的话,99%都是由于 Js代码的问题,即js代码不规范或者 变量缺少定义之类。

 

4. C#代码调用helper获得执行结果

1
2
3
4
5
6
7
8
9
10
11
12
//设置Cookie"WT_FPC"
            string wt_fpc = JsHelper.GetJsMethd("GetWT_FPC", null);
            CookieCollection hcc = new CookieCollection();
            Cookie wtcookie = new Cookie() {
                Expires = DateTime.Now.AddYears(10),
                Path = "/",
                Domain = ".10086.cn",
                Name = "WT_FPC",
                 Value = wt_fpc.Substring(wt_fpc.IndexOf('=') + 1, wt_fpc.Length - 7)//
            };
            hcc.Add(wtcookie);
            HttpHelperNew.cookie.Add(wtcookie);

 

5.完结! 小经验: 有时候 JavaScript前端生成的cookie,有时候 服务器端并不 校验,也就是,如果把这个cookie值不通过js代码动态得到,直接 写死的话 也应该可以。

 

测试时间 > 构造时间 > 代码时间

android 7.0 学习笔记(一) - zhangbz - 博客园

mikel阅读(1725)

来源: android 7.0 学习笔记(一) – zhangbz – 博客园

导读

增强的Doze模式

后台优化

Data Saver

 

一.增强的Doze模式

Android N对Android M引进的Doze模式进行了进一步的增强,变化体现在两个方面.一方面是降低了进入Doze模式的条件,Android M中的条件是不插电,屏幕熄灭且静置一段时间,在Android N中去掉了静置的条件,这个改变大大增加了设备进入Doze模式的机会,因而使得Doze对应用程序的影响大大增加.另一方面,Doze模式被分成了两个阶段,当设备切断电源,熄灭屏幕一段时间,会进入到第一阶段,切断网络连接,推迟任务和同步.如果设备在第一阶段的基础上再静置一段时间,就会进入第二阶段,在第一阶段的基础上增加对维持唤醒(PowerManager.WakeLock),定时任务(AlarmManager alarms),GPS和Wi-Fi扫描的限制,如下图所示:

First level

 

Second level

 

 

应对方案:

方案一:在Android 6.0中AlarmManager中增加了两个方法setAndAllowWhileIdle() and setExactAndAllowWhileIdle(),通过使用这两个方法可以让alarm在Doze模式下运行.

需要注意的是官方文档指出,使用这两个方法时,每个应用每9分钟只能唤醒一次alarm.

Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app.

以一个定时任务为例进行测试,核心代码如下:

Service:

在Service的onStartCommand()方法中,获取一个AlarmManager的实例,设置任务执行的时间是10s后,处理定时任务的广播接收器是AlarmReceiver.

public class LongRunningService extends Service {

public static final String TAG = “LongRunningService”;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG,”executed at ” + new Date().toString());
}
}).start();

AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int offset= 10 * 1000;//间隔时间10s
long triggerAtTime = SystemClock.elapsedRealtime() + offset;
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, i, 0);

manager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
//        manager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);

return super.onStartCommand(intent, flags, startId);
}
}

BroadcastReceiver:

重写onReceive()方法,创建一个Intent,启动LongRunningService.这样一来,就形成了一个每隔10s执行一次的定时任务.

public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, LongRunningService.class);
context.startService(i);
}
}

 

测试结果:

 I/LongRunningService: executed at Thu Apr 14 22:32:58 GMT+08:00 2016

 I/LongRunningService: executed at Thu Apr 14 22:33:08 GMT+08:00 2016

 I/LongRunningService: executed at Thu Apr 14 22:33:18 GMT+08:00 2016

 I/LongRunningService: executed at Thu Apr 14 22:42:18 GMT+08:00 2016

 I/LongRunningService: executed at Thu Apr 14 22:51:18 GMT+08:00 2016

从测试结果可以看出,设备在正常使用的情况下(前三行),每隔10s运行一次,进入到Doze模式后(后三行),每隔9分钟执行一次.

为方便操作,这里介绍一下测试步骤:

Step1.运行应用程序

Step2.关闭设备的屏幕

Step3.使用如下命令强制系统进入Doze模式

$ adb shell dumpsys battery unplug

$ adb shell dumpsys deviceidle step

需要多次运行第二条命令,直到设备进入到空闲状态.

 

 

方案二:在应用程序运行时,引导用户将该应用添加到白名单中.

实现方式1(不推荐):使用ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS使用户跳转到电池优化设置页,手动将该应用添加到白名单中.

核心代码如下:

Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivity(intent);

该方法可行,但存在一个缺点:跳转到电池优化页后,用户需要在”所有应用”列表里(应用安装后会默认设置为”优化”)找到该应用进行设置,而且系统提示会引导用户选择优化,如图所示:

 

实现方式2(推荐):给应用添加权限REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,并使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,系统会弹出设置窗口,用户可以直接将该应用添加到白名单中,如下图所示:

 

核心代码:

在清单文件中添加权限:

<uses-permission android:name=”android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS”/>

使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS作为参数创建一个Intent,并以”package:com.example.xxx.xxx”的Uri形式将包名传入.

Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse(“package:com.example.janiszhang.dozedemo3”));
startActivity(intent);

方案三:使用GCM(Google Cloud Messaging ),该方法不可行,不再赘述.

二.后台优化

优化点1.针对预览版,应用不再接收静态注册的CONNECTIVITY_ACTION广播.但是应用在前台时仍然能够监听到动态注册的CONNECTIVITY_CHANGE广播.

优化点2.应用程序不能发送或者接收ACTION_NEW_PICTURE 和ACTION_NEW_VIDEO广播,这个优化会影响到所有应用,不只是针对预览版.

应对方案:

为了应对CONNECTIVITY_ACTION的变化所带来的影响,官方给出了两种缓解方案.

方案一:使用JobScheduler在无计量网络下调度网络任务.

核心代码:

Activity:

在使用JobInfo.Builder()创建JobInfo对象时,调用setRequiredNetworkType()方法,并将

JobInfo.NETWORK_TYPE_UNMETERED作为参数传递进去,这段代码的作用是当设备接入无计量网络时将调起MyJobService.

public static final int MY_BACKGROUND_JOB = 0;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static void scheduleJob(Context context) {
JobScheduler js =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo job = new JobInfo.Builder(
MY_BACKGROUND_JOB,
new ComponentName(context, MyJobService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.build();
js.schedule(job);
}

Service:

当条件满足时(在该例中为接入无计量网络),MyJobService中的回调方法onStartJob()将被执行,在实际业务中,可以在这里执行网络任务.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService{

public static final String TAG = “MyJobService”;
@Override
public boolean onStartJob(JobParameters jobParameters) {
Log.i(TAG, “on start job: ” + jobParameters.getJobId());
return false;
}

@Override
public boolean onStopJob(JobParameters jobParameters) {
Log.i(TAG, “on stop job: ” + jobParameters.getJobId());
return false;
}
}

注意:

需要在清单文件中为该Service设置权限:android.permission.BIND_JOB_SERVICE”

<service android:name=”.MyJobService”
android:permission=”android.permission.BIND_JOB_SERVICE”/>

方案二:在应用运行时监控网络连接

方式一:动态注册BroadcastReceiver,监听

“android.net.conn.CONNECTIVITY_CHANGE”

BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,”onReceive”);
}
};

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(“android.net.conn.CONNECTIVITY_CHANGE”);
this.registerReceiver(broadcastReceiver,intentFilter);

方式二:使用ConnectivityManager

首先,使用NetworkRequest.Builder创建一个NetworkRequest对象,然后使用registerNetworkCallback()把这个NetworkRequest对象传递给系统.当网络条件被满足时,应用将收到一个回调去执行定义在ConnectivityManager.MetworkCallback类中的onAvailable()方法.

ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkRequest.Builder builder = new NetworkRequest.Builder();

builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);

NetworkRequest networkRequest = builder.build();

ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {

@Override
public void onAvailable(Network network) {
super.onAvailable(network);
Log.i(TAG, “onAvailable”);
}
};
connectivityManager.registerNetworkCallback(networkRequest, networkCallback);

三.Data Saver

 

当用户在计量网络下启用数据节约功能时,系统会封锁后台数据的使用,运行在前台的应用也会尽量少的使用数据流量.用户可以使用白名单允许指定的应用在数据节约模式下使用后台数据.Android N开发者预览版中扩展了ConnectivityManager API的能力,向用户提供了查看和监控数据节约设置的接口.

检测Data Saver首选项的变化

动态注册监听ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED (“android.net.conn.RESTRICT_BACKGROUND_CHANGED”)的广播接收者.

核心代码:

BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, “Data Saver Changed”);
}
};

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(“android.net.conn.RESTRICT_BACKGROUND_CHANGED”);
this.registerReceiver(broadcastReceiver,intentFilter);

注意:必须使用动态注册的方式才能够监听到该广播,不能在清单文件中静态注册.

检查数据节约设置

 

ConnectivityManager的getResrictBackgroundStatus()方法的返回值如下:

RESTRICT_BACKGROUND_STATUS_DISABLED

禁用数据节约

RESTRICT_BACKGROUND_STATUS_ENABLED

启用数据节约

RESTRICT_BACKGROUND_STATUS_WHITELISTED

用户启用了数据节约,但是该应用在白名单中,故不受限制.

下面的示例代码来自官方文档,给出了使用ConnectivityManager.isActiveNetworkMetered()

 和ConnectivityManager.getRestrictBackgroundStatus()来判断当前Data Saver设置状态的方法.

 

注意:这段代码目前还不能使用,RESTRICT_BACKGROUND_STATUS_ENABLED等三个状态值尚不可用.

使用adb命令进行测试

$ adb shell dumpsys netpolicy

生成一个报告,包括当前的全部后台网络限制的设置,白名单中的包的UID,其他已知包的网络限制.

$ adb shell cmd netpolicy

显示一个完整的网络策略管理命令列表.

$ adb shell cmd netpolicy set restrict-background <boolean>

启用或者禁用数据节约命令

$ adb shell cmd netpolicy add restrict-background-whitelist <UID>

将指定的包的UID加入白名单中

$ adb shell cmd netpolicy remove restrict-background-whitelist <UID>

从白名单中将指定包的UID移除

windows下mysql忘记root密码的解决办法-hot815-ChinaUnix博客

mikel阅读(1209)

来源: windows下mysql忘记root密码的解决办法-hot815-ChinaUnix博客

今天早上 一朋友说自己的mySQL 忘记root密码了 让我帮忙给看看,因为没有接触过mySQL 所以从网上找了一下信息
经我亲身实践  已经成功!mySQL版本是5.1
以下是从网上找的信息:

1、 首先检查mysql服务是否启动,若已启动则先将其停止服务,可在开始菜单的运行,使用命令:

net stop mysql

 

打开第一个cmd1窗口,切换到mysql的bin目录,运行命令:

mysqld –defaults-file=”C:\Program Files\MySQL\MySQL Server 5.1\my.ini” –console –skip-grant-tables

注释:

该命令通过跳过权限安全检查,开启mysql服务,这样连接mysql时,可以不用输入用户密码。  此时已经开启了mysql服务了!

这个窗口保留 不关闭。

 

 

2、打开第二个cmd2窗口,连接mysql:

输入命令:

mysql -u root -p

出现:

Enter password:

在这里直接回车,不用输入密码。

然后就就会出现登录成功的信息,

 

 

使用命令:

show databases;

 

 

使用命令切换到mysql数据库:

use mysql;

使用命令更改root密码:

UPDATE user SET Password=PASSWORD(‘newpassword’) where USER=’root’;

 

刷新权限:

FLUSH PRIVILEGES;

然后退出,重新登录:

quit

重新登录: 可以关掉之前的cmd1 窗口了。然后用net start mysql 启动服务

mysql -u root -p

出现输入密码提示,输入新的密码即可登录:

Enter password: ***********

显示登录信息: 成功  就一切ok了

 

备注:这种方法亲测可以通过

Windows下mysql忘记root密码的解决方法 - andy_tigger - 博客园

mikel阅读(962)

来源: Windows下mysql忘记root密码的解决方法 – andy_tigger – 博客园

MySQL版本:5.1

 

1、 首先检查mySQL服务是否启动,若已启动则先将其停止服务,可在开始菜单的运行,使用命令:

net stop mysql

 

打开第一个cmd窗口,切换到mysql的bin目录,运行命令:

mysqld –defaults-file=”C:\Program Files\MySQL\MySQL Server 5.1\my.ini” –console –skip-grant-tables

注释:

该命令通过跳过权限安全检查,开启mysql服务,这样连接mysql时,可以不用输入用户密码。 

 

 

2、打开第二个cmd窗口,连接mysql:

输入命令:

mysql -uroot -p

出现:

Enter password:

在这里直接回车,不用输入密码。

然后就就会出现登录成功的信息,

 

 

使用命令:

show databases;

 

 

使用命令切换到mysql数据库:

use mysql;

使用命令更改root密码:

UPDATE user SET Password=PASSWORD(‘newpassword’) where USER=’root’;

 

刷新权限:

FLUSH PRIVILEGES;

然后退出,重新登录:

quit

重新登录:

mysql -uroot -p

出现输入密码提示,输入新的密码即可登录:

Enter password: ***********

显示登录信息: 成功  就一切ok了

windows下mysql提示access denied for user ''@'localhost' to database - wanghuahui的专栏 - 博客频道 - CSDN.NET

mikel阅读(633)

来源: windows下mysql提示access denied for user ”@’localhost’ to database – wanghuahui的专栏 – 博客频道 – CSDN.NET

最近在使用mySQL,版本为‘5.6.20’。

在官网http://dev.mysql.com/downloads/mysql/下载最新windows安装版本,之后安装。我选的是custom安装模式,设置安装路径后,一直next即可。

安装完成后,使用命令行cmd进入安装路径bin目录下,我的目录为“D:\Program Files\MySQL\MySQL Server 5.6\bin”。

用root帐号连上mysql,语句为“mysql -u root”即可,然后创建一个数据库,提示如下错误:

ERROR 1044 (42000): Access denied for user ”@’localhost’ to database ‘mydb’。

原因是因为mysql数据库的user表里,存在用户名为空的账户即匿名账户,导致登录的时候是虽然用的是root,但实际是匿名登录的,通过错误提示里的”@’localhost’可以看出来。

由于网上大多是linux下解决方案,但是在windows下输入命令却有不同,经过在网上查找试验,终于找到一个比较有效的方法,现整理如下:

1、cmd模式进入mysql的bin目录,输入D:\Program Files\MySQL\MySQL Server 5.6\bin>mysqld –defaults-file=”D:\Program Files\MySQL\MySQL Server 5.6\my-default.ini” –console –skip-grant-tables,回车

2014-08-29 10:40:48 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use
2014-08-29 10:40:48 8108 [Note] Plugin ‘FEDERATED’ is disabled.
2014-08-29 10:40:48 8108 [Note] InnoDB: Using atomics to ref count buffer pool pages
2014-08-29 10:40:48 8108 [Note] InnoDB: The InnoDB memory heap is disabled
2014-08-29 10:40:48 8108 [Note] InnoDB: Mutexes and rw_locks use Windows interlocked functions
2014-08-29 10:40:48 8108 [Note] InnoDB: Memory barrier is not used
2014-08-29 10:40:48 8108 [Note] InnoDB: Compressed tables use zlib 1.2.3
2014-08-29 10:40:48 8108 [Note] InnoDB: Not using CPU crc32 instructions
2014-08-29 10:40:48 8108 [Note] InnoDB: Initializing buffer pool, size = 128.0M
2014-08-29 10:40:48 8108 [Note] InnoDB: Completed initialization of buffer pool
2014-08-29 10:40:48 8108 [Note] InnoDB: Highest supported file format is Barracuda.
2014-08-29 10:40:57 8108 [Note] InnoDB: 128 rollback segment(s) are active.
2014-08-29 10:40:57 8108 [Note] InnoDB: Waiting for purge to start
2014-08-29 10:40:57 8108 [Note] InnoDB: 5.6.20 started; log sequence number 1625997
2014-08-29 10:40:57 8108 [Note] Server hostname (bind-address): ‘*’; port: 3306
2014-08-29 10:40:57 8108 [Note] IPv6 is available.
2014-08-29 10:40:57 8108 [Note]   – ‘::’ resolves to ‘::’;
2014-08-29 10:40:57 8108 [Note] Server socket created on IP: ‘::’.
2014-08-29 10:40:58 8108 [Note] mysqld: ready for connections.
Version: ‘5.6.20’  socket: ”  port: 3306  MySQL Community Server (GPL)

看到这个结果就说明MySQL已经起来了。
2、再开一个dos窗口,同样切到mysql bin目录下,D:\Program Files\MySQL\MySQL Server 5.6\bin>mysql -u root mysql,回车

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.20 MySQL Community Server (GPL)

mysql>

已连接上mysql数据库,可以输入mysql命令了。

3、设置密码

mysql> update user set password=PASSWORD(‘password’) where user=’root’;
Query OK, 2 rows affected (0.02 sec)
Rows matched: 3  Changed: 2  Warnings: 0

mysql> FLUSH PRIVILEGES;
mysql> quit

之后按正常方法连接mysql就可以了。如果不能连接,查看下mysql服务是否启动。

 

解决方案2:
1、打开MySQL目录下的my.ini文件,在文件的最后添加一行“skip-grant-tables”,保存并关闭文件。
2、重启MySQL服务。
3、在命令行中输入“mysql -uroot -p”(不输入密码),回车即可进入数据库。
4、执行,“use mysql;”使用mysql数据库。
5、执行,“update user set password=PASSWORD(“rootadmin”) where user=’root’;”(修改root的密码)
(期间我还select下user表中的记录,查询全部的时候显示了一些乱码,然后我只查询了user表中的Host、User、Password字段,貌似当时显示了3条记录,有1个没有名称。当然,为了解决问题,这些我也管不了了。)
6、打开MySQL目录下的my.ini文件,删除最后一行的“skip-grant-tables”,保存并关闭文件。
7、重启MySQL服务。
8、在命令行中输入“mysql -uroot -prootadmin”,问题搞定!

微信JSApi支付~集成到MVC环境后的最后一个坑(网上没有这种解决方案) - 张占岭 - 博客园

mikel阅读(1298)

来源: 微信JSApi支付~集成到MVC环境后的最后一个坑(网上没有这种解决方案) – 张占岭 – 博客园

大叔第一人

之前写了关于微信的坑《微信JSApi支付~坑和如何填坑》, 今天将微信的jsapi支付封装到了MVC环境里,当然也出现了一些新的坑,如支付参数应该是Json对象而不是Json字符串,这样也会应付引起 “get_brand_wcpay_request:fail_invalid appid”这个大家都知道的异常,呵呵,解决方案网上说是“授权目录“,事实上,还有一种原因,那就是你的WeixinJSBridge.invoke方法里的参数应该是Json对象而不是字符串,这才是最重要的。

代码我们需要写成下面的格式

复制代码
    //调用微信JS api 支付
        function jsApiCall() {
            var price = 1.0;
            var no = '@Request.QueryString["orderNumber"]';
            var action = '@Url.Action("Get")';
            var openID = '@openID';
            $.get(action, { price: price, orderNumber: no, openID: openID }, function (data) {
                WeixinJSBridge.invoke('getBrandWCPayRequest', JSON.parse(data), function (res) {
                    WeixinJSBridge.log(res.err_msg);
                    alert(res.err_code + res.err_desc + res.err_msg);
                });
            });
        }
复制代码

对于我们封装成MVC后,由于MVC的路由将扩展名去除了,所以我们的授权目录也发生了变化,如Order/Pay这个页面,在收取时应该是http://域名/Order/Pay/,而之前的http://域名/Order/这个级别就不被认可了,这点也要注意一下。

还有一点要注册,如果你添加了测试授权目录,那必须要添加测试用的白名单,否则你的微信也测试不了。

大叔封装的MVC版的微信JSAPI支付

复制代码
    /// <summary>
    /// 构建支付处理类
    /// </summary>
    public class JsApiImplement
    {
        public static string wxJsApiParam { get; set; } //H5调起JS API参数

       /// <summary>
       /// 返回当前微信客户端的OpenId,每个客户端在每个公众号里的OpenId是唯一的
       /// </summary>
       /// <returns></returns>
        public static string GetOpenId()
        {
            JsApiPay jsApiPay = new JsApiPay(System.Web.HttpContext.Current);
            jsApiPay.GetOpenidAndAccessToken();
            Log.Debug("GetOpenId", "openid : " + jsApiPay.openid);
            return jsApiPay.openid;
        }


        /// <summary>
        /// JsApi返回微信支付的连接参数,这个方法需要前台UI页面调用,通常可以使用AJAX进行调用它
        /// </summary>
        /// <param name="total_fee">订单金额</param>
        /// <param name="orderId">业务的订单编号</param>
        /// <returns></returns>
        public static string Send(int total_fee, string orderId, string openId)
        {
            try
            {
                //调用【网页授权获取用户信息】接口获取用户的openid和access_token
                //jsApiPay.GetOpenidAndAccessToken();
                JsApiPay jsApiPay = new JsApiPay(System.Web.HttpContext.Current);
                jsApiPay.openid = openId;
                Log.Debug("Send", "openid : " + jsApiPay.openid);
                //若传递了相关参数,则调统一下单接口,获得后续相关接口的入口参数,微信的价格是分
                jsApiPay.total_fee = total_fee;
                WxPayData unifiedOrderResult = jsApiPay.GetUnifiedOrderResult(orderId);
                wxJsApiParam = jsApiPay.GetJsApiParameters();//获取H5调起JS API参数                    
                Log.Debug("Send", "wxJsApiParam : " + wxJsApiParam);
            }
            catch (Exception ex)
            {
                Log.Error("Error", ex.Message);
            }
            return wxJsApiParam;
        }

        /// <summary>
        /// JsApi微信回调
        /// </summary>
        public static void Notify(Action<NotifyModel> action)
        {
            var context = System.Web.HttpContext.Current;
            ResultNotify resultNotify = new ResultNotify(context);
            resultNotify.ProcessNotify(action);
        }
    }
复制代码

对于使用者来说,也很简单,在页面上拿OpenId,之后把OpenId传到后台方法,拿到微信支付的参数对象(JSON对象),之后完成支付

复制代码
<script type="text/javascript">

    //调用微信JS api 支付
    function jsApiCall() {
        $.get("/weixin/get", new { money: 1, order: '001', openId: 'test' }, function (data) {
            WeixinJSBridge.invoke('getBrandWCPayRequest', JSON.parse(data), function (res) {
                WeixinJSBridge.log(res.err_msg);
                alert(res.err_code + res.err_desc + res.err_msg);
            });
        });
    }

    function callpay() {
        if (typeof (WeixinJSBridge) == "undefined") {
            if (document.addEventListener) {
                document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
            }
            else if (document.attachEvent) {
                document.attachEvent('WeixinJSBridgeReady', jsApiCall);
                document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
            }
        }
        else {
            jsApiCall();
        }
    }

</script>

<body>
    <a href="javascript:void(0)" onclick="callpay();return false;">立即支付</a>
</body>
复制代码

希望大家一起来找各种坑,然后把坑填上,分享给大家!

微信JSApi支付~坑和如何填坑

在phpmyadmin中创建存储过程并在php中调用_zhouzerong2006_新浪博客

mikel阅读(941)

 

来源: 在phpmyadmin中创建存储过程并在php中调用_zhouzerong2006_新浪博客

语法:

CREATE PROCEDURE p()

BEGIN

 

END

CREATE PROCEDURE productpricing()

BEGIN

SELECT Avg(pro_price) AS priceaverage

FROM products;

END;

# begin…end之间是存储过程的主体定义

# mySQL的分界符是分号(;)

调用存储过程的方法是:

# CALL加上过程名以及一个括号

# 例如调用上面定义的存储过程

CALL productpricing();

# 哪怕是不用传递参数,存储过程名字后面的括号“()”也是必须的

删除存储过程的方法是:

DROP PROCUDURE productpricing;

创建带参数的存储过程:

CREATE PROCUDURE productpricing(

OUT p1 DECIMAL(8,2),

OUT ph DECIMAL(8,2),

OUT pa DECIMAL(8,2)

)

BEGIN

SELECT Min(prod_price) INTO pl FROM products;

SELECT Max(prod_price) INTO ph FROM products;

SELECT Avg(prod_price) INTO pa FROM products;

END;

# DECIMAL用于指定参数的数据类型

# OUT用于表明此值是用于从存储过程里输出的

# MySQL支持 OUT, IN, INOUT

调用带参数的存储过程:

CALL productpricing(@pricelow,

@pricehigh,

@priceaverage);

# 所有的参数必须以@开头

# 要想获取@priceaverage的值,用以下语句

SELECT @priceaverage;

# 获取三个的值,用以下语句

SELECT @pricehigh, @pricelow, @priceaverage;

另一个带IN和OUT参数的存储过程:
CREATE PROCEDURE ordertotal(

IN onumber INT,

OUT ototal DECIMAL(8,2)

)

BEGIN

SELECT Sum(item_price*quantity)

FROM orderitems

WHERE order_num = onumber

INTO ototal;

END;

CALL ordertotal(20005, @total);

SELECT @total;

注意:在phpmyadmin中创建时,那个delimite要改成//

存储过程返回数据集:

CREATE PROCEDURE simpleproc (IN myId INT)
BEGIN
CREATE TEMPORARY TABLE tmpMyTbl LIKE t2;
insert into tmpMyTbl select * from t2 where id<myId;
END;
mysql> call simpleproc(10); Query OK, 9 rows affected (0.13 sec)
mysql> select * from tmpMyTbl;
在php中调用存储过程:

$host=”localhost”;
$user=”root”;
$password=””;
$db=”xydc”;
$dblink=mysql_connect($host,$user,$password)
or die(“can’t connect to mysql”);
mysql_select_db($db,$dblink)
or die(“can’t select samp_db”);
$res=mysql_query(“call pp(@total)”,$dblink);
$res=mysql_query(“select @total as total”,$dblink);
$row=mysql_fetch_row($res);
echo $row[‘total’].”<br />”;
print_r($row);

区块,条件,循环

1.区块定义,常用
begin
……
end;
也可以给区块起别名,如:
lable:begin
………..
end lable;
可以用leave lable;跳出区块,执行区块以后的代码
2.条件语句

if 条件 then
statement
else
statement
end if;

3.循环语句
(1).while循环

[label:] WHILE expression DO

statements

END WHILE [label] ;

(2).loop循环

[label:] LOOP

statements

END LOOP [label];

(3).repeat until循环

[label:] REPEAT

statements

UNTIL expression

END REPEAT [label] ;

五.其他常用命令

1.show procedure status
显示数据库中所有存储的存储过程基本信息,包括所属数据库,存储过程名称,创建时间等
2.show create procedure sp_name
显示某一个存储过程的详细信息

mysql存储过程中要用到的运算符

mysql存储过程学习总结-操作符

算术运算符

+     加   SET var1=2+2;       4
–     减   SET var2=3-2;       1
*     乘   SET var3=3*2;       6
/     除   SET var4=10/3;      3.3333
DIV   整除 SET var5=10 DIV 3;  3
%     取模 SET var6=10%3 ;     1

比较运算符

>            大于 1>2 False
<            小于 2<1 False
<=           小于等于 2<=2 True
>=           大于等于 3>=2 True
BETWEEN      在两值之间 5 BETWEEN 1 AND 10 True
NOT BETWEEN  不在两值之间 5 NOT BETWEEN 1 AND 10 False
IN           在集合中 5 IN (1,2,3,4) False
NOT IN       不在集合中 5 NOT IN (1,2,3,4) True
=            等于 2=3 False
<>, !=       不等于 2<>3 False
<=>          严格比较两个NULL值是否相等 NULL<=>NULL True
LIKE         简单模式匹配 “Guy Harrison” LIKE “Guy%” True
REGEXP       正则式匹配 “Guy Harrison” REGEXP “[Gg]reg” False
IS NULL      为空 0 IS NULL False
IS NOT NULL  不为空 0 IS NOT NULL True

逻辑运算符

(AND)

 

AND TRUE FALSE NULL
TRUE TRUE FALSE NULL
FALSE FALSE FALSE NULL
NULL NULL NULL NULL

或(OR)

OR TRUE FALSE NULL
TRUE TRUE TRUE TRUE
FALSE TRUE FALSE NULL
NULL TRUE NULL NULL

异或(XOR)

XOR TRUE FALSE NULL
TRUE FALSE TRUE NULL
FALSE TRUE FALSE NULL
NULL NULL NULL NULL

位运算符

|   位或
&   位与
<<  左移位
>>  右移位
~   位非(单目运算,按位取反)

 

mysq存储过程中常用的函数,字符串类型操作,数学类,日期时间类。

mysql存储过程基本函数

一.字符串类

CHARSET(str) //返回字串字符集
CONCAT (string2  [,… ]) //连接字串
INSTR (string ,substring ) //返回substring首次在string中出现的位置,不存在返回0
LCASE (string2 ) //转换成小写
LEFT (string2 ,length ) //从string2中的左边起取length个字符
LENGTH (string ) //string长度
LOAD_FILE (file_name ) //从文件读取内容
LOCATE (substring , string  [,start_position ] ) 同INSTR,但可指定开始位置
LPAD (string2 ,length ,pad ) //重复用pad加在string开头,直到字串长度为length
LTRIM (string2 ) //去除前端空格
REPEAT (string2 ,count ) //重复count次
REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替换search_str
RPAD (string2 ,length ,pad) //在str后用pad补充,直到长度为length
RTRIM (string2 ) //去除后端空格
STRCMP (string1 ,string2 ) //逐字符比较两字串大小,
SUBSTRING (str , position  [,length ]) //从str的position开始,取length个字符,
注:mysql中处理字符串时,默认第一个字符下标为1,即参数position必须大于等于1

mysql> select substring(‘abcd’,0,2);
+———————–+
| substring(‘abcd’,0,2) |
+———————–+
|                       |
+———————–+
1 row in set (0.00 sec)

mysql> select substring(‘abcd’,1,2);
+———————–+
| substring(‘abcd’,1,2) |
+———————–+
| ab                    |
+———————–+
1 row in set (0.02 sec)

TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //转换成大写
RIGHT(string2,length) //取string2最后length个字符
SPACE(count) //生成count个空格

二.数学类

ABS (number2 ) //绝对值
BIN (decimal_number ) //十进制转二进制
CEILING (number2 ) //向上取整
CONV(number2,from_base,to_base) //进制转换
FLOOR (number2 ) //向下取整
FORMAT (number,decimal_places ) //保留小数位数
HEX (DecimalNumber ) //转十六进制
注:HEX()中可传入字符串,则返回其ASC-11码,如HEX(‘DEF’)返回4142143
也可以传入十进制整数,返回其十六进制编码,如HEX(25)返回19
LEAST (number , number2  [,..]) //求最小值
MOD (numerator ,denominator ) //求余
POWER (number ,power ) //求指数
RAND([seed]) //随机数
ROUND (number  [,decimals ]) //四舍五入,decimals为小数位数]

注:返回类型并非均为整数,如:
(1)默认变为整形值
mysql> select round(1.23);
+————-+
| round(1.23) |
+————-+
|           1 |
+————-+
1 row in set (0.00 sec)

mysql> select round(1.56);
+————-+
| round(1.56) |
+————-+
|           2 |
+————-+
1 row in set (0.00 sec)

(2)可以设定小数位数,返回浮点型数据
mysql> select round(1.567,2);
+—————-+
| round(1.567,2) |
+—————-+
|           1.57 |
+—————-+
1 row in set (0.00 sec)

SIGN (number2 ) //返回符号,正负或0
SQRT(number2) //开平方
三.日期时间类
ADDTIME (date2 ,time_interval ) //将time_interval加到date2
CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //转换时区
CURRENT_DATE (  ) //当前日期
CURRENT_TIME (  ) //当前时间
CURRENT_TIMESTAMP (  ) //当前时间戳
DATE (datetime ) //返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或时间
DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式显示datetime
DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上减去一个时间
DATEDIFF (date1 ,date2 ) //两个日期差
DAY (date ) //返回日期的天
DAYNAME (date ) //英文星期
DAYOFWEEK (date ) //星期(1-7) ,1为星期天
DAYOFYEAR (date ) //一年中的第几天
EXTRACT (interval_name  FROM date ) //从date中提取日期的指定部分
MAKEDATE (year ,day ) //给出年及年中的第几天,生成日期串
MAKETIME (hour ,minute ,second ) //生成时间串
MONTHNAME (date ) //英文月份名
NOW (  ) //当前时间
SEC_TO_TIME (seconds ) //秒数转成时间
STR_TO_DATE (string ,format ) //字串转成时间,以format格式显示
TIMEDIFF (datetime1 ,datetime2 ) //两个时间差
TIME_TO_SEC (time ) //时间转秒数]
WEEK (date_time [,start_of_week ]) //第几周
YEAR (datetime ) //年份
DAYOFMONTH(datetime) //月的第几天
HOUR(datetime) //小时
LAST_DAY(date) //date的月的最后日期
MICROSECOND(datetime) //微秒
MONTH(datetime) //月
MINUTE(datetime) //分

phpMyAdmin添加存储过程 - 耗子的博客 - ITeye技术网站

mikel阅读(1096)

来源: phpMyAdmin添加存储过程 – 耗子的博客 – ITeye技术网站

存储过程:

 

SQL代码  收藏代码
  1. CREATE PROCEDURE `ClearCoupon`()
  2. BEGIN
  3.      declare today int(16);
  4.      select UNIX_TIMESTAMP(curdate()) into today;
  5.      UPDATE coupon SET status=0 WHERE endtime<today;
  6. END;

 

copy到phpmyadmin里面执行,就报错了

错误

SQL 查询:

CREATE PROCEDURE `ClearCoupon` ( ) BEGIN declare today INT( 16 ) ;

MySQL 返回:文档

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 3 

解决办法:
存储过程的语法是没错的,出问题的是 语句界定符”;”
只需要把phpmyadmin的sql输入框下面的 “语句界定符”改为”//”就OK了

Linux删除文件名含有特殊字符的文件和文件夹-power562-ChinaUnix博客

mikel阅读(1118)

来源: 删除文件名含有特殊字符的文件-power562-ChinaUnix博客

Linux 系统下的文件名长度最多可到256个字符。通常情况下,文件名的字符包括:字母、数字、“.”(点)、“_”(下划线)和“-”(连字符)。Linux 允许在文件名中使用除上述符号之外的其它符号, 但并不建议用户这样做。 有些转意字符 (即该字符被系统借用,表示某种特殊含义) 在 Linux 的 命令解释器(shell)中有特殊的含义。这样的转意字符有:“?”(问号),“*”(星号), “ ”(空格), “$”(货币符), “&”, 扩号等等。在文件名中应尽量避免使用这些字符。文件名中可以有“ ”(空格),但建议用户用“_”(下划线)来替代。“/” 既可代表目录树的根也可作为路径名中的分隔符(类似DOS下的“\”),因此“/“不能出现在文件名中。   和 DOS 一样,“.” 和 “..”分别用来表示“当前目录”和“父目录”, 因此它们也不能作为文件名。

1. 文件名含有特殊字符,直接使用 rm 可能删除不了,可以使用如下方法:
1) 使用 ls -i 查处该文件的 inode 号,假设为654321
2) 使用find命令删除

  1. find ./ -inum 654321 -ok rm ‘{}’ \;
  2. find ./ -inum 644321 -ok rm -rf  ‘{}’ \;
  3. find ./ -inum 7506726 -delete;

2. 如果文件名是以 – 连字符开头的,可以使用如下方法来删除,如删除 “-filename” 文件:

  1. rm — -filename
    rm ./-filename

在PHP语言中使用JSON - 阮一峰的网络日志

mikel阅读(1056)

来源: 在PHP语言中使用JSON – 阮一峰的网络日志

作者: 阮一峰

日期: 2011年1月14日

目前,JSON已经成为最流行的数据交换格式之一,各大网站的API几乎都支持它。

我写过一篇《数据类型和JSON格式》,探讨它的设计思想。今天,我想总结一下PHP语言对它的支持,这是开发互联网应用程序(特别是编写API)必须了解的知识。

从5.2版本开始,PHP原生提供json_encode()json_decode()函数,前者用于编码,后者用于解码。

一、json_encode()

该函数主要用来将数组和对象,转换为json格式。先看一个数组转换的例子:


  $arr = array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);
  
  echo json_encode($arr);
  

结果为


  {"a":1,"b":2,"c":3,"d":4,"e":5}
  

再看一个对象转换的例子:


  $obj->body           = 'another post';
  
  $obj->id             = 21;
  
  $obj->approved       = true;
  
  $obj->favorite_count = 1;
  
  $obj->status         = NULL;
  
  echo json_encode($obj);
  

结果为


  {
    "body":"another post",
  
    "id":21,
  
    "approved":true,
  
    "favorite_count":1,
  
    "status":null
  } 
  

由于json只接受utf-8编码的字符,所以json_encode()的参数必须是utf-8编码,否则会得到空字符或者null。当中文使用GB2312编码,或者外文使用ISO-8859-1编码的时候,这一点要特别注意。

二、索引数组和关联数组

PHP支持两种数组,一种是只保存”值”(value)的索引数组(indexed array),另一种是保存”名值对”(name/value)的关联数组(associative array)。

由于JavaScript不支持关联数组,所以json_encode()只将索引数组(indexed array)转为数组格式,而将关联数组(associative array)转为对象格式。

比如,现在有一个索引数组


  $arr = Array('one', 'two', 'three');
  
  echo json_encode($arr);
  

结果为:


  ["one","two","three"] 
  

如果将它改为关联数组:


  $arr = Array('1'=>'one', '2'=>'two', '3'=>'three');
   
  echo json_encode($arr);
    

结果就变了:


  {"1":"one","2":"two","3":"three"} 
  

注意,数据格式从”[]”(数组)变成了”{}”(对象)。

如果你需要将”索引数组”强制转化成”对象”,可以这样写


  json_encode( (object)$arr );
  

或者


  json_encode ( $arr, JSON_FORCE_OBJECT );
  

三、类(class)的转换

下面是一个PHP的类:


  class Foo {
  
    const     ERROR_CODE = '404';
  
    public    $public_ex = 'this is public';
  
    private   $private_ex = 'this is private!';
  
    protected $protected_ex = 'this should be protected'; 
   
    public function getErrorCode() {
  
      return self::ERROR_CODE;
  
    }
  
  }
  

现在,对这个类的实例进行json转换:


  $foo = new Foo;
  
  $foo_json = json_encode($foo);
  
  echo $foo_json;
  

输出结果是


  {"public_ex":"this is public"} 
  

可以看到,除了公开变量(public),其他东西(常量、私有变量、方法等等)都遗失了。

四、json_decode()

该函数用于将json文本转换为相应的PHP数据结构。下面是一个例子:


  $json = '{"foo": 12345}';
   
  $obj = json_decode($json);
  
  print $obj->{'foo'}; // 12345
  

通常情况下,json_decode()总是返回一个PHP对象,而不是数组。比如:


  $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
   
  var_dump(json_decode($json));
  

结果就是生成一个PHP对象:


  object(stdClass)#1 (5) {
  
    ["a"] => int(1)
    ["b"] => int(2)
    ["c"] => int(3)
    ["d"] => int(4)
    ["e"] => int(5)
  
  }
  

如果想要强制生成PHP关联数组,json_decode()需要加一个参数true:


  $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
   
  var_dump(json_decode($json,true));
  

结果就生成了一个关联数组:


  array(5) {
  
     ["a"] => int(1)
     ["b"] => int(2)
     ["c"] => int(3)
     ["d"] => int(4)
     ["e"] => int(5)
  
  }
  

五、json_decode()的常见错误

下面三种json写法都是错的,你能看出错在哪里吗?


  $bad_json = "{ 'bar': 'baz' }";
  
  $bad_json = '{ bar: "baz" }';
  
  $bad_json = '{ "bar": "baz", }';
  

对这三个字符串执行json_decode()都将返回null,并且报错。

第一个的错误是,json的分隔符(delimiter)只允许使用双引号,不能使用单引号。第二个的错误是,json名值对的”名”(冒号左边的部分),任何情况下都必须使用双引号。第三个的错误是,最后一个值之后不能添加逗号(trailing comma)。

另外,json只能用来表示对象(object)和数组(array),如果对一个字符串或数值使用json_decode(),将会返回null。


  var_dump(json_decode("Hello World")); //null
  

六、参考材料

  [1] PHP Manual

[2] Ed Finkler, JSON is Everybody’s Friend

(完)