微信小程序开发框架搭建 - hss01248的专栏 - CSDN博客

mikel阅读(885)

来源: 微信小程序开发框架搭建 – hss01248的专栏 – CSDN博客

使用开发工具的正确姿势
微信提供的开发工具的编辑功能不是一般的水,写代码肯定不能用它,否则就是浪费生命.不说别的,连自动保存都没有,第一次写时写了一个多小时,后面下班直接关掉,也不弹出提示说没保存.然后第二天过来,写的代码全没了!!! 顿时感到巨坑无比.这些工具开发人员吃干饭的么???
(后来的版本已经修复不能自动保存的问题了,当然编辑功能还是不好用.)

它的正确用法是作为运行和调试工具.

那么适合作为编辑工具的是: webStorm.基于IntelJ内核,开启Dracula主题,跟Android studio的使用习惯非常接近,so cool!各种方法提示,自动保存,快速查找…应有尽有.闭源的微信开发工具就不要用来写代码了,珍惜生命.
webStorm要识别wxml和wxss,还需要配置一下文件类型:(看下面别人截的图)
记住html和css里都要加上微信小程序对应的类型

综上,开发时,用webstorm来写代码,用微信开发工具来运行和调试,速度飕飕的!

网络请求的封装
微信提供了底层网络驱动以及成功和失败的回调.但对于一个项目中的实际使用而言,仍然还是显得繁琐,还有很多封装和简化的空间.

wx.request({
url: ‘test.php’,//请求的url
data: {//请求的参数
x: ” ,
y: ”
},
header: {//请求头
‘Content-Type’: ‘application/json’
},
method:”POST”,
success: function(res) {//成功的回调
console.log(res.data)
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
网络框架二次封装的一般姿势
对于一个网络访问来说,请求一般是get和post,拼上各种参数以及请求头,然后拿到回来的响应,解析并得到最终需要的数据.

对于具体项目来说,请求时会有每个(或大多数)请求都要带的参数,都要带的请求头,返回的数据格式可能都是一致的,那么基于此,对微信的网络请求api进行二次封装:

在我目前的项目中,

请求:
大多数请求是post,基本上每个请求都需要携带sessionId来与服务器验证登录状态,还有很多请求是基于分页的,需要带上pageSize和pageIndex.
再跟页面逻辑关联起来,请求可能是因为第一次进入页面,或者刷新,或者上拉加载更多.

响应:
大多数拿到的数据格式是标准json格式,如下

{
“code”:1,
“data”:xxx,//可能是String,也可能是JsonObject,或JsonArray,也可能是null,或undefined
“msg”:yyy//可能为空

}
1
2
3
4
5
6
通过请求的状态码code来判断这个请求是否真正成功.我们的项目中还有常见的是code=5:登录过期或未登录,code=2: 没有找到对应的内容 等等.

我们实际使用中需要的:
如果是大多数情况的请求时,只需要指定:

url的尾部
该请求的非一般的参数
该请求由什么动作引起的(第一次进入,刷新,加载更多)
对于响应,我们只需要:

成功时:拿到data里面的数据
失败时,拿到失败的信息(细分一下,包括可以直接显示给用户的和不能让用户看到的),以及失败状态码
数据为空的回调:(常见于列表数据的加载)
我们期望的api是:

netUtil.buildRequest(page,urlTail,params,callback)//必须的参数和回调
.setXxx(xxx)//额外的设置,链式调用
..
.send();//最终发出请求的动作
1
2
3
4
基于上面的分析,封装如下:
定义好携带构建请求的对象:
//这两个错误码是项目接口文档统一定义好的
const code_unlogin = 5;
const code_unfound = 2;

function requestConfig(){
this.page; //页面对象
this.isNewApi = true;
this.urlTail=”;
this.params={
pageIndex:0,
pageSize:getApp().globalData.defaultPageSize,
session_id:getApp().globalData.session_id
};
this.netMethod=’POST’;
this.callback={
onPre: function(){},
onEnd: function(){

},
onSuccess:function (data){},
onEmpty : function(){},
onError : function(msgCanShow,code,hiddenMsg){},
onUnlogin: function(){
this.onError(“您还没有登录或登录已过期,请登录”,5,”)
},
onUnFound: function(){
this.onError(“您要的内容没有找到”,2,”)
}
};

this.setMethodGet = function(){
this.netMethod = ‘GET’;
return this;
}

this.setApiOld = function(){
this.isNewApi = false;
return this;
}

this.send = function(){
request(this);
}
}
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
37
38
39
40
41
42
43
44
请求的封装
供我们调用的顶层api:
//todo 拷贝这段代码去用–buildRequest里的callback
/*
onPre: function(){},
onEnd: function(){
hideLoadingDialog(page);
},
onSuccess:function (data){},
onEmpty : function(){},
onError : function(msgCanShow,code,hiddenMsg){},
onUnlogin: function(){
this.onError(“您还没有登录或登录已过期,请登录”,5,”)
},
onUnFound: function(){
this.onError(“您要的内容没有找到”,2,”)
}

* */

/**
* 注意,此方法调用后还要调用.send()才是发送出去.
* @param page
* @param urlTail
* @param params
* @param callback 拷贝上方注释区的代码使用
* @returns {requestConfig}
*/
function buildRequest(page,urlTail,params,callback){
var config = new requestConfig();
config.page = page;
config.urlTail = urlTail;

if (getApp().globalData.session_id == null || getApp().globalData.session_id == ”){
params.session_id=”
}else {
params.session_id = getApp().globalData.session_id;
}
if (params.pageIndex == undefined || params.pageIndex <=0 || params.pageSize == 0){
params.pageSize=0
}else {
if (params.pageSize == undefined){
params.pageSize = getApp().globalData.defaultPageSize;
}
}
log(params)
config.params = params;

log(config.params)

//config.callback = callback;

if(isFunction(callback.onPre)){
config.callback.onPre=callback.onPre;
}

if(isFunction(callback.onEnd)){
config.callback.onEnd=callback.onEnd;
}

if(isFunction(callback.onEmpty)){
config.callback.onEmpty=callback.onEmpty;
}

if(isFunction(callback.onSuccess)){
config.callback.onSuccess=callback.onSuccess;
}

if(isFunction(callback.onError)){
config.callback.onError=callback.onError;
}

if(isFunction(callback.onUnlogin)){
config.callback.onUnlogin=callback.onUnlogin;
}
if(isFunction(callback.onUnFound)){
config.callback.onUnFound=callback.onUnFound;
}
return config;
}
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
最终请求的发送:
function request(requestConfig){

//检验三个公有参数并处理.这里与上面有所重复,是为了兼容之前写的几个api,不想改了.
requestConfig.params.sessionId= getApp().globalData.sessionId;
if (requestConfig.params.sessionId ==null || requestConfig.params.sessionId == ”){
delete requestConfig.params.sessionId;
}
if (requestConfig.params.pageIndex ==0 || requestConfig.params.pageSize == 0){
delete requestConfig.params.pageIndex ;
delete requestConfig.params.pageSize;
}

//var body = getStr(“&”, requestConfig.params);//拼接请求参数
requestConfig.onPre();//请求发出前
wx.request({
// url: getApp().globalData.apiHeadUrl+requestConfig.urlTail+”?”+body,貌似这样写,同时不给data赋值,post请求也是可以成功的
url: getApp().globalData.apiHeadUrl+requestConfig.urlTail,
method:requestConfig.netMethod,
data:requestConfig.params,
header: {‘Content-Type’:’application/json’},
success: function(res) {
console.log(res);
if(res.statusCode = 200){
var responseData = res.data
var code = responseData.code;
var msg = responseData.message;

if(code == 0){
var data = responseData.data;
var isDataNull = isOptStrNull(data);
if(isDataNull){
requestConfig.onEmpty();
}else{
requestConfig.onSuccess(data);
}
}else if(code == 2){
requestConfig.onUnFound();
}else if(code == 5){
requestConfig.onUnlogin();
}else{
var isMsgNull = isOptStrNull(msg);
if(isMsgNull){
var isCodeNull = isOptStrNull(code);
if (isCodeNull){
requestConfig.onError(“数据异常!,请核查”,code,”);
}else {
requestConfig.onError(“数据异常!,错误码为”+code,code,”);
}

}else{
requestConfig.onError(msg,code,”);
}
}
}else if(res.statusCode >= 500){
requestConfig.onError(“服务器异常!”,res.statusCode,”);
}else if(res.statusCode >= 400 && res.statusCode < 500){
requestConfig.onError(“没有找到内容”,res.statusCode,”);
}else{
requestConfig.onError(“网络请求异常!”,res.statusCode,”);
}
},
fail:function(res){
console.log(“fail”,res)
requestConfig.onError(“网络请求异常!”,res.statusCode,”);

},
complete:function(res){
// that.setData({hidden:true,toast:true});
}
})
}
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
将方法暴露,并在需要时引用:
方法写在netUtil.js下,在该js文件最下方暴露方法:

module.exports = {
buildRequest:buildRequest

}
1
2
3
4
实际引用:

var netUtil=require(“../../utils/netUtil.js”);
1
实际使用时:
小技巧: js无法像java一样定义好了接口,然后IDE自动生成代码.可以这样: 将callback的空方法写到netUtil的buildRequest方法上方的注释区,每次用时,点击方法名跳到那边去拷贝即可.

var params = {};
params.id = id;

netUtil.buildRequest(that,API.Album.DETAIL,params,{
onPre: function(){
netUtil.showLoadingDialog(that);
},
onEnd:function(){

},
onSuccess:function (data){
netUtil.showContent(that);
….
},
onEmpty : function(){

},
onError : function(msgCanShow,code,hiddenMsg){
netUtil.showErrorPage(that,msgCanShow);
},
onUnlogin: function(){
this.onError(“您还没有登录或登录已过期,请登录”,5,”)
},
onUnFound: function(){
this.onError(“您要的内容没有找到”,2,”)
}
}).send();
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
},

页面状态管理
对于大多数网络请求后显示的页面,有这么几种页面状态:
第一次进入时,网络请求过程中:显示”加载中”的状态
加载的内容为空,显示”空白页面”
加载发生错误,显示”错误页面”,此页面一般有一个点击重试的按钮.该按钮一般的逻辑是:如果没有网络则点击后去打开网络设置,如果有网络,则重新发送网络请求.
加载成功,就显示内容页.
对于已经加载成功了,显示了内容页的”下拉拉刷新”:
页面上方会有”刷新中”的ui显示,这个微信已经原生集成,无需处理.
刷新成功,最好是弹出toast提示数据刷新成功
刷新失败,可以不提示,也可以提示,看具体选择.
对于一些分批加载的列表数据,一般还有上拉”加载更多”的功能:
参考微信文档中ui设计规范,上拉加载更多的ui提示应该放在页面最下部占一行,而不应该在页面中间显示一个大大的loading的效果.

scrollview拉到最底部,触发加载事件,显示”加载中”的ui
加载成功,直接就将数据添加到原list上,这时也看不到最底部那行ui,所以不用处理
加载失败,则在那一行显示”加载失败”的字样,同时提示用户”上拉重试”,或者在那一行放置一个按钮,点击按钮重试.
封装
通过上面的分析,可以确定大部分页面的通用状态管理逻辑,那么就可以设计通用的状态管理模板了.

ui的显示是通过Page里的data中的数据来控制的,并通过page.setData({xxx})来刷新的,原先每个页面都拷贝同样的js属性和wxml代码去实现封装,后来进行了封装,js属性用方法来封装,通过微信提供的template封装共同的wxml代码,通过import或include导入到wxml中(但是不知什么bug,template一直无法起作用).

控制ui显示与否的属性的封装
function netStateBean(){
//toast的是老api,工具升级后无需设置了
this.toastHidden=true,
this.toastMsg=”,

this.loadingHidden=false,
this.emptyHidden = true,
this.emptyMsg=’暂时没有内容,去别处逛逛吧’,

this.errorMsg=”,
this.errorHidden=true,

this.loadmoreMsg=’加载中…’,
this.loadmoreHidden=true,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
页面js里的使用:
Page(
data: {
title:’名师’,//todo 设置标题栏
emptyMsg:’暂时没有内容,去别处逛逛吧’,//todo 空白页面的显示内容
netStateBean: new netUtil.netStateBean(),

},

)
1
2
3
4
5
6
7
8
9
wxml里:
模板

<template name=”pagestate” >
<view class =”empty_view” wx:if=”{{!emptyHidden}}” >
<view class=”center_wrapper” >
<view class=”center_child” >
<icon type=”info” size=”45″/>
<view class=”msg”> {{emptyMsg}}</view>
</view>
</view>
</view>

<view class =”error_view” wx:if=”{{!errorHidden}}” >
<view class=”center_wrapper”>
<view class=”center_child” >
<icon type=”warn” size=”45″ />
<view class=”msg”> {{errorMsg}}</view>
<button class = “retrybtn” type=”warn” loading=”{{btnLoading}}”
disabled=”{{btnDisabled}}” catchtap=”onRetry” hover-class=”other-button-hover”> 点击重试 </button>
</view>
</view>
</view>

</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
使用

<!–状态管理模板–>
<import src=”../../template/pagestate.wxml”/>
<view >
<template is=”pagestate” data=”{{…netStateBean}}”/>
</view>
1
2
3
4
5
js中提供API来控制页面状态:
module.exports = {
showContent:showContent,
showErrorPage:showErrorPage,
showEmptyPage:showEmptyPage,
loadMoreNoData:loadMoreNoData,
loadMoreStart:loadMoreStart,
loadMoreError:loadMoreError,
hideLoadingDialog:hideLoadingDialog,
showLoadingDialog:showLoadingDialog,
showSuccessToast:showSuccessToast,
dismissToast:dismissToast,
showFailToast:showFailToast,
….

}

//具体的实现就是,拿到page对象中的data.netStateBean,修改部分数值,然后刷新ui.
function showEmptyPage(that){
hideLoadingDialog(that);
var bean = that.data.netStateBean;
bean.emptyHidden = false;
bean.loadingHidden = true;
var empty = that.data.emptyMsg;
if (isOptStrNull(empty)){//如果那个页面没有自定义空白说明文字,就使用默认的.
empty = “没有内容,去别的页面逛逛吧”
}
bean.emptyMsg= empty;
bean.contentHidden=true;//隐藏content页面
bean.errorHidden = true;//隐藏error页面
//刷新UI
that.setData({
netStateBean: bean
});
}
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
最终的效果:

常用页面模板的封装
整个页面就是只有一个简单的listview或gridview,数据从网络拉取,带上拉刷新和下拉加载更多的功能
分析
对于这种简单的页面来说,分析各页面不同的地方:
1.上一个页面传入的参数:
2.网络请求的url
3.网络请求的部分参数(其中分批加载的每批大小和第几批这两个参数的key在整个项目中都是一样的,每批大小的value可能不一样)
4.response数据回来后,从哪个字段中取出列表对应的数据,可能会不一样
5.对列表数据datas的每一条,有些字段的数据需要处理,处理的方式会不一样.
6.UI中:item内容和样式每个页面会不一样

7.标题栏文字
8.列表数据为空时的说明文字
9.非第一进入时调用onShow(相当于安卓中的onResume)时,是否自动刷新页面数据

js-页面加载逻辑全部封装在方法中:
netUtil.requestSimpleList(that,pageIndex,action);
1
js–page里:需要设置的都加上了todo注释,高亮显示,便于直接填内容
var utils=require(“../../utils/util.js”);
var netUtil=require(“../../utils/netUtil.js”);
var viewBeans=require(“../../beans/viewBeans.js”);
var infoBeans=require(“../../beans/infoBeans.js”);
var API=require(“../../utils/API.js”);

const request_firstIn = 1;
const request_refresh = 2;
const request_loadmore = 3;
const request_none = 0;

var app = getApp();
var that;
var id =0; //页面id
var intentDatas;
var isFristIn = true;

var needRefreshOnResume = false;//todo 页面需要自己决定

Page({
data: {
title:’我的收藏’,//todo 设置标题栏
emptyMsg:’暂时没有内容,去别处逛逛吧’,//todo 空白显示内容
requestMethod:”POST”,//todo 如果不是post,则在这里改
urlTail:API.User.MYCOLLECTION,//todo 需补全的,页面请求的url

netStateBean: new netUtil.netStateBean(),
currentPageIndex:1,
currentAction : 0,
infos:[],//列表数据
},

//以下四个方法是生命周期方法,已封装好,无需改动
onLoad: function(options) {
that = this;
intentDatas = options;
if (that.data.emptyMsg != null && that.data.emptyMsg != ” ){
that.data.netStateBean.emptyMsg = that.data.emptyMsg;
}
that.parseIntent(options);
this.requestList(1,request_firstIn)
},
onReady: function () {
wx.setNavigationBarTitle({
title: this.data.title
});
},
onHide:function(){

},
onShow:function(){
if (isFristIn){
isFristIn = false;
}else {
if (needRefreshOnResume){
if (that.data.currentAction ==request_none){
this.requestList(1,request_refresh);//刷新
}
}
}
},
//上拉加载更多
onLoadMore: function(e) {
console.log(e);
console.log(that.data.currentAction +”—“+request_none);
if (that.data.currentAction ==request_none){
this.requestList(that.data.currentPageIndex+1,request_loadmore)
}
},
//下拉刷新,通过onPullDownRefresh来实现,这里不做动作
onRefesh: function(e) {
console.log(e);
},

onPullDownRefresh:function(e){
this.requestList(1,request_refresh);
},

//针对纯listview,网络请求直接一行代码调用
requestList:function(pageIndex, action){
netUtil.requestSimpleList(that,pageIndex,action)
},

//todo 滑动监听,各页面自己回调
scroll: function(e) {
console.log(e)
},
//todo 将intent传递过来的数据解析出来
parseIntent:function(options){

},

//todo 设置网络参数
/**
* 设置网络参数
* @param params config.params
* @param id 就是请求时传入的id,也是成员变量-id
*/
setNetparams: function (params) {

},

//todo 如果list数据是netData里一个字段,则更改此处
getListFromNetData:function(netData){
return netData;
},

//todo 数据的一些处理并刷新数据
handldItemInfo:function(info){

}
})
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
wxml中,写好空白页面,错误页面,加载更多栏
<view class=”section” style=”width: 100% ;height: 100%”>

<scroll-view wx:if=”{{!netStateBean.contentHidden}}” scroll-y=”true” style=”height:1300rpx;position:relative; z-index:0;” lower-threshold=”50″
bindscrolltolower=”onLoadMore” bindscrolltoupper=”onRefesh” >

<!–todo listview–>

<view class=”list_data”>
<block wx:for-items=”{{infos}}” wx:for-item=”info” wx:for-index=”index”>
<navigator url=”/pages/lession/{{info.classname}}?id={{info.id}}&title={{info.title}}” hover-class=””>
<!–todo—listview: 这里写item的具体内容 –>

</navigator>
</block>
</view>

<!–todo gridview 同时js中getListFromNetData()方法返回utils.to2DimensionArr(netData,columnNum);–>

<block wx:for-items=”{{infos}}” wx:for-item=”info” >
<view class=”row-container”>
<block wx:for-items=”{{info}}” wx:for-item=”item”>
<navigator url=”/pages/lession/album?id={{item.id}}” hover-class=””>
<!–todo gridview 这里写item具体的内容–>

</navigator>
</block>
</view>
</block>

<!–加载更多的条栏–>
<view class=”loadmore_view” wx:if=”{{!netStateBean.loadmoreHidden}}” >
{{netStateBean.loadmoreMsg}}
</view>
</scroll-view>

<!–空白页面–>
<view class =”empty_view” wx:if=”{{!netStateBean.emptyHidden}}” >
<view class=”center_wrapper” >
<view class=”center_child” >
<icon type=”info” size=”45″/>
<view class=”msg”> {{netStateBean.emptyMsg}}</view>
</view>
</view>
</view>

<!–错误页面–>
<view class =”error_view” wx:if=”{{!netStateBean.errorHidden}}” >
<view class=”center_wrapper”>
<view class=”center_child” >
<icon type=”warn” size=”45″ />
<view class=”msg”> {{netStateBean.errorMsg}}</view>
<button class = “retrybtn” type=”warn” loading=”{{loading}}”
disabled=”{{disabled}}” bindtap=”onRetry” hover-class=”other-button-hover”> 点击重试 </button>
</view>
</view>
</view>

</view>
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
日志打印控制
function log(msg){
var isDebug = getApp().globalData.isDebug;
if (isDebug){
console.log(msg);
}
1
2
3
4
5
}

Android中的gridview在小程序里的实现
小程序文档中只提供了一维的列表渲染示例,相对应的就是安卓中的列表listview.一维的数组,一维的UI.
如果是实现gridview这种XY轴两个方向的延伸,数据方面需要一个二维数组,UI方法,需要两层的嵌套渲染.

数据转换:一维数组转换成二维数组
/**
*
* @param arr 原始数据,是一个一维数组
* @param num 变成二维数组后,每个小一维数组的元素个数,也就是gridview中每行的个数
*/
function to2DimensionArr(arr,num){
var newArr = new Array();//二维数组
if (arr == undefined){
return newArr;
}
var subArr=null;
for(var i =0;i<arr.length;i++){
var item = arr[i];
if((i%num==0) ||subArr==null){
subArr = new Array();//内部的一维数组
newArr.push(subArr);
}
subArr.push(item);
}
return newArr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
UI上嵌套渲染:
<block wx:for-items=”{{infos}}” wx:for-item=”info” >
<view class=”row-container”>
<block wx:for-items=”{{info}}” wx:for-item=”albumItem”>
<view class=”row-album-item”>
<navigator url=”/pages/lession/album?id={{albumItem.id}}” hover-class=””>
<image mode=”aspectFill” src=”{{albumItem.coverUrl}}” class=”album-cover-img”/>
<text class=”album-name”>{{albumItem.name}}</text>
</navigator>
</view>
</block>
</view>
</block>
1
2
3
4
5
6
7
8
9
10
11
12
效果

代码
wxapp-devFrame
———————
作者:hss01248
来源:CSDN
原文:https://blog.csdn.net/hss01248/article/details/53405308
版权声明:本文为博主原创文章,转载请附上博文链接!

分分钟制作微信朋友圈页面 - 海绵小猪 - 博客园

mikel阅读(892)

来源: 分分钟制作微信朋友圈页面 – 海绵小猪 – 博客园

微信朋友圈内嵌WebView,可以理解成一个浏览器。

所以,任何可访问网页都能在朋友圈打开、分享,就非常容易理解了。

 

  • 微信的WebView内有一些自带特性,如WeixinJSBridge对象,可以定制一些特性。

 

最简单的情况,我们写一个可以访问的网页(带服务器的),把url复制到微信中发送给任何人,点击url就可以打开这个网页。但是url如果不是域名是ip地址会提示是否继续访问。

 

通过微信打开的网页,头部会带有分享等功能。分享到朋友圈,发送给特定人后,url变好看了,微信做了处理和提取。

  • 最重要的,图片和文字是怎么来的?

微信的提取机制

图片:从body开始找到第一个像素大于300×300,显示着的图片。

文字:html中title里内容。

 

为了让图片更符合微信气息,专门为微信准备一个图片更好,又不能隐藏,聪明的你早就想到了,把图片这样设置:

<img style="height: 0;width: 0;" src="wechatIcon.jpg">

 

  • 开发调试

开发微信网页可以使用微信web开发者工具来调试,看效果。开发者工具主要继承了Chrome开发者工具,加微信自有特性。

但微信有个限制:地址要使用域名而不能使用ip。开发者工具不识别ip地址,微信的webView也不识别,只能查看静态效果。

未能加载文件或程序集“System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依 - qdluo的博客 - CSDN博客

mikel阅读(1121)

来源: 未能加载文件或程序集“System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依 – qdluo的博客 – CSDN博客

网站访问出错,如下

原理在我本地运行正常,但部署到服务器上就出现了这问题。,

原因是引用的system.web.mvc文件和配置项配置的节点版本不一致导致的,所以把服务器上项目bin目录下的system.web.mvc.dll文件替换和本地版本一样就行了,或者修改Web配置项的节点版本号。
———————
作者:qdluo
来源:CSDN
原文:https://blog.csdn.net/qdluo/article/details/75676172
版权声明:本文为博主原创文章,转载请附上博文链接!

SQL Server2008附加数据库之后显示为只读时解决方法 - 轻狂の书生 - 博客园

mikel阅读(1715)

来源: SQL Server2008附加数据库之后显示为只读时解决方法 – 轻狂の书生 – 博客园

啰嗦的话就不多说了,直入主题吧!

方案一:

碰到这中情况一般是使用的sa账户登录的,只要改为Windows身份验证,再附加数据库即可搞定。

方案二:

使用sa登录SQL Server2008附加数据库,附加之后数据库为只读的,然后点数据库–>“属性”–>“选项”–>“状态”,发现“数据库为只读”这一项为True,改为false,如果能够修改的话,那么恭喜你,你的人品不错哦!我在修改的时候又报了这样一个错误:

这种情况下,找到你的数据文件和日志文件,右击“属性”–>“安全”,添加一个“Network Service”账户,然后赋予该账户完全控制的权限即可。再次使用sa登录,附加数据库,嘿嘿…你会发现,生活如此美好!!!

C#读取PDF文档文字内容 - tzdk - 博客园

mikel阅读(1470)

来源: C#读取PDF文档文字内容 – tzdk – 博客园

C#读取PDF文档文字内容

通过iTextSharp读取PDF文件内容,下载地址,下载后解压itextsharp-dll-core.zip。

只能读取英文和数字,文档中包含的汉字无法正常读取:

复制代码
private string ReadPdfContent(string filepath)  
{  
    try  
    {  
        string pdffilename = filepath;  
        PdfReader pdfReader = new PdfReader(pdffilename);  
        int numberOfPages = pdfReader.NumberOfPages;  
        string text = string.Empty;  
  
        for (int i = 1; i <= numberOfPages; ++i)  
        {  
            byte[] bufferOfPageContent = pdfReader.GetPageContent(i);  
            text += System.Text.Encoding.UTF8.GetString(bufferOfPageContent);  
        }  
        pdfReader.Close();  
  
        return text;  
    }  
    catch (Exception ex)  
    {  
        StreamWriter log = File.AppendText(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase+"\\log.log");  
        log.WriteLine("出错文件:" + e.FullPath + "原因:" + ex.ToString());  
        log.Flush();  
        log.Close();return null;  
    } 
}
复制代码

 

可以读取中英文

复制代码
private string OnCreated(string filepath)  
{  
    try  
    {  
        string pdffilename = filepath;  
        PdfReader pdfReader = new PdfReader(pdffilename);  
        int numberOfPages = pdfReader.NumberOfPages;  
        string text = string.Empty;  
  
        for (int i = 1; i <= numberOfPages; ++i)  
        {  
            iTextSharp.text.pdf.parser.ITextExtractionStrategy strategy = new iTextSharp.text.pdf.parser.SimpleTextExtractionStrategy();
            text += iTextSharp.text.pdf.parser.PdfTextExtractor.GetTextFromPage(pdfReader, i, strategy);
        }  
        pdfReader.Close();  
  
        return text;  
    }  
    catch (Exception ex)  
    {  
        StreamWriter wlog = File.AppendText(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase+"\\mylog.log");  
        wlog.WriteLine("出错文件:" + e.FullPath + "原因:" + ex.ToString());  
        wlog.Flush();  
        wlog.Close();return null;  
    }  
  
}
复制代码

 

谷歌推出了有意思的 “Just A Line” AR 应用

mikel阅读(1123)

来源: 谷歌推出了有意思的 “Just A Line” AR 应用

谷歌经常用新技术做一些稀奇古怪的实验应用。“Just a Line” 是一款新推出的 AR 应用,建立在谷歌的 ARCore 技术之上。这个应用的设计想法非常简单,但它的成效实际上非常有趣。

“Just A Line” 是一个 AR 实验项目,你可以利用 AR 技术进行简单的绘画,然后用一个简短的视频分享你的创作。触摸屏幕即可开始绘制,点击拍摄按钮并分享你用 #justaline 做了什么。Just A Line 在任何支持 AR Core 的设备上均可用。

想了解更多信息,请访问 g.co/ARExperiments。

您也可以在 https://github.com/googlecreativelab/ar-drawing-java 找到该项目的开源代码。

在应用中,用户只需透过相机拍摄周围的世界,再点击屏幕就可以开始进行虚拟绘画。使用 Just a Line 进行的小创作都可以被保存在手机上,如果你的设备与 ARCore 兼容,不妨下载这个应用玩玩看。

from:青亭

解决IIS7中出现An error occurred on the server when processing the URL错误提示的方法 - yuhaibin168的专栏 - CSDN博客

mikel阅读(697)

来源: 解决IIS7中出现An error occurred on the server when processing the URL错误提示的方法 – yuhaibin168的专栏 – CSDN博客

解决IIS7中出现An error occurred on the server when processing the URL错误提示的方法

前提在ie选项中去掉  显示友好HTTP页面信息
在IIS7上调试程序时,如果程序有错误,默认情况下,会提示:An error occurred on the server when processing the URL. Please contact the system administrator.

在IIS7上调试程序时,如果程序有错误,默认情况下,会提示:

An error occurred on the server when processing the URL. Please contact the system administrator.

If you are the system administrator please click here to find out more about this error.

其实这是IIS7对ASP程序发送的一个脚本错误信息,只要是程序中有错误,就会出现这样的提示。下面的方法是将具体的错误信息显示出来。

一、打开 Internet 信息服务(IIS)管理器。点击出错的站点,并双击右边的ASP图标,如下图所示:

 

二、展开右侧配置中的“调试属性”,把“将错误发送到浏览器”的值设为 “true”,如图所示:

 

题外话:一般ASP程序中,建议将启用父路径设为True,

通过Global.asax文件里面的Session_End事件记录用户退出 (or session timeout) - liangzi4000 - 博客园

mikel阅读(1092)

来源: 通过Global.asax文件里面的Session_End事件记录用户退出 (or session timeout) – liangzi4000 – 博客园

Session.Abandon()和timeout会触发Global.asax的Session_End事件。可以通过这个事件来记录用户退出或者session timeout,这样每个用户都会有一条登陆和退出记录。

退出登陆调用方法:

1
2
3
4
5
public void PerformLogout()
{
    HttpContext.Current.Session["PerformLogout"] = true;
    HttpContext.Current.Session.Abandon();
}

Session_End事件代码:

1
2
3
4
5
6
7
8
9
10
11
protected void Session_End(Object sender, EventArgs e)
{
    BusinessContext bizContext = (BusinessContext)Session["BusinessContext"];
    string loginID = string.IsNullOrEmpty(bizContext.LoginID) ? "" : bizContext.LoginID;
    string title = "Timeout";
    if (Convert.ToBoolean(Session["PerformLogout"]))
    {
        title = "Logout";
    }
    BusinessEvent.Log(BizLogCategory.SECURITY, BizLogModule.USER_AUTHENTICATION, title, "[PerformLogout]Logout Successfully""LoginID: " + loginID, bizContext);
}

有如下几点需要注意:

1. 尽管我们先调用Session.Abandon(),但是在Session_End事件里还是可以继续访问所有session的值。正是因为这个,所以我们可以在PerformLogout方法中给Session[“PerformLogout”]赋值,然后通过这个值来判断Session_End事件是由用户登出触发还是由session timeout触发。

2. ASP.NET里面Session和HttpContext.Current.Session对象都指向System.Web.SessionState.HttpSessionState,大部分地方这两个对象可以互换使用,但是在Session_End事件里,HttpContext.Current返回的是null,所以只能通过Session对象来访问session值。为什么要这样写,参考这里

3. 引起session timeout的设置比较多,测试过的有web.config里面的sessionState timeout, IIS Application Pools的Idle Time-out, IIS Application Pools Recycle, Stop website, 修改web.config等。

 

https://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstatemodule.end(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.abandon(v=vs.110).aspx
http://forums.ASP.NET/t/1275683.aspx?Can+t+access+to+Session+variable+inside+Session_End+Event
http://stackoverflow.com/questions/940742/difference-between-session-and-httpcontext-current-session
http://stackoverflow.com/questions/27657773/why-is-httpcontext-current-null-during-the-session-end-event
http://stackoverflow.com/questions/19509672/why-is-httpcontext-current-null
http://stackoverflow.com/questions/12294532/asp-net-values-of-session-variables-in-session-end-event
https://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.abandon.aspx
https://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstatemodule.end.aspx
http://stackoverflow.com/questions/13264212/on-session-timeout-capture-info
http://www.beansoftware.com/ASP.NET-Tutorials/Find-If-Session-Expired.aspx

[C#][ASP.NET MVC]處理Session Timeout - 程序新青年 - 博客园

mikel阅读(1793)

来源: [C#][ASP.NET MVC]處理Session Timeout – 程序新青年 – 博客园

Session Timeout導回登入頁面這樣的功能大家應該並不陌生,

而處理Session  Timeout也有很多方式(也不一定要導回登入頁面),

可以使用client script固定時間輪詢Server(callback)不讓Session Timeout也是一種方法,

在MVC中個人較愛操作Action Filters(比較能展現MVC在設計上的特性~XD),

這裡自己紀錄一下。

 

新增自訂類別並繼承ActionFilterAttribute

public class CheckSessionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting( ActionExecutingContext filterContext )
        {
            HttpContext httpcontext = HttpContext.Current;
            // 確認目前要求的HttpSessionState
            if( httpcontext.Session != null )
            {
                //確認Session是否為新建立
                if( httpcontext.Session.IsNewSession )
                {
                    //確認是否已存在cookies
                    String sessioncookie = httpcontext.Request.Headers[ "Cookie" ];
                    if( ( sessioncookie != null ) && ( sessioncookie.IndexOf( "ASP.NET_SessionId" ) >= 0 ) )
                    {
                        Logon( filterContext );
                    }
                }
            }
            base.OnActionExecuting( filterContext );
}
        private void Logon( ActionExecutingContext filterContext )
        {
            RouteValueDictionary dictionary = new RouteValueDictionary
                ( new
                {
                    controller = "Account",
                    action = "Logon",
                    returnUrl = filterContext.HttpContext.Request.RawUrl
                } );
            filterContext.Result = new RedirectToRouteResult( dictionary );
        }
    }

Controller

public ActionResult Index()
        {
            Session[ "mytime" ] = DateTime.Now.ToString();
            ViewData[ "Message" ] = Session[ "mytime" ] as String;
            return View();
        }
        [CheckSessionFilterAttribute]//自訂Action Filters
        public ActionResult About()
        {
return View();
   }

Web.config

image

設定2分鐘Session timeout。

結果:

image

2分鐘過後點擊About。

image

導回登入頁面。

Asp.net Mvc+MongoDB+Autofac等打造轻量级blog系统(一) - Nic Pei - 博客园

mikel阅读(1100)

来源: Asp.net Mvc+MongoDB+Autofac等打造轻量级blog系统(一) – Nic Pei – 博客园

这两天坐地铁上在想着是否可以做一个很轻量级的.net博客发布系统。。。所有东西都用轻量级的,我想要系统是基于ASP.NET Mvc框架的,所以选定了如下几个大的组件来完成这个设想。

1. 整个应用程序架构:ASP.NET mvc 3 (Razor)

2.数据存储 : MongoDB,是个面向文档的数据库,它是多系统支持,轻量级,高性能的。

3.ORM : 现在的应用开发如果你不用ORM,那就好像有点老土了,但是ORM永远都无法和ado.net媲美,无乱是EF,NHibernate还是linq等等。。。。而我这里还是想使用一个ORM工具,于是选择了Simple.Data这个非常轻量级的ORM工具,它使用了C# 中的Dynamic特性。

4.IoC工具,绝对是autofac这个最轻量级了。。。

 

对于ASP.NET MVC你可以到这里看到很多学习资料:http://www.cnblogs.com/n-pei/tag/Asp.net%20MVC/

包括ASP.NET MVC 3的系列文章。。。。微笑

 

环境的要求:

1.首先你需要的是.net framework 4的安装。你机器不需要安装asp.net mvc,只需要把对应的几个dll添加到bin目录下就行。

 

2.MongoDB的安装 如果你以前接触过MongoDB,请跳过这一段,直接看第三步。

image

 

http://www.mongodb.org/ 它的数据是以json格式存储的。

下载到对应的压缩包,然后解压到某个盘下。

image

默认的mongo是不会自己创建文件夹,而它却需要找到指定的文件夹Data\db,所以我们需要在bin目录所在的根文件夹下创建如下文件夹:

image

 

接下来就是运行db server了。

image

 

现在数据库服务器就开始运行了,因为它是在dos下运行的,所以不能关闭这个窗口,以后说明下如何把它制定为windows service,这样就需要开着窗口了。

 

3.ORM: Simple.Data这个是使用C# Dynamic属性的轻量级ORM工具,它不是很好用,但是速度是挺快的,而且不需要配置文件,支持各种数据库。。。

你可以到这里下载:http://github.com/markrendle/Simple.Data

image

 

4. IoC工具,这个Autofac我之前有好多文章都介绍了。你可以到这里下载和查看:http://code.google.com/p/autofac/

我博客中相关的文章: http://www.cnblogs.com/n-pei/tag/Autofac/

 

 

可能你已经不耐烦了,,我啰嗦这么多,,好吧,接下来开始使用MogonDB,这篇文章主要介绍如何在asp.net mvc中使用它。。。。其它模块在以后的文章中介绍。

 

首先是创建实体,这里只创建好Post和comment两个实体。

image

接下来是创建Repository模块:

Post的Repository接口:

image

对应的Save方法:

image

这里的操作都是比较繁琐的,以后会结合autofac优化这一部分。

GetAll方法和通过Id得到某个post实体的方法如下:

image

 

 

 

 

还有一部分是update某个post.这部分代码就不贴了。

 

接下来是Controller部分的代码:

Create post部分的代码:

image

添加对应的View以后,运行:

image

点击Craete按钮后:

image

保存成功,然后会自动跳转到List页面:

image

 

稍候等整个项目写的差不多了,我会把代码放到codeplex上,支持下微软,呵呵。