Asp.net mvc5 解析route源码实现自己的route系统

 

url route


路由系统的责任是找到匹配的路由,创建路由数据,并将请求分配给一个处理程序。
选择动作是 MVC 的处理程序的实现细节。它使用路由数据和从传入请求其他信息来选择要执行的操作

实例


源码:http://pan.baidu.com/s/1i3lfbaH

具体实现


//存放url 路径片段
public abstract class PathSegment
{
// Methods
protected PathSegment()
{ } }
  // 主要存放 url template 的 "/"
//比如{controller}/{action}中 "/" public sealed class SeparatorPathSegment : PathSegment
{
// Methods
public SeparatorPathSegment()
{
} }
    //存放 url  tamplate  subpathsegment
//比如A{controller}/{action}中 "controller" 或 action 或 A
public abstract class PathSubsegment
{
// Methods
protected PathSubsegment() {}
}
 //存放 url  tamplate  subpathsegment
//比如A{controller}/{action}中 "controller" 或 action
public sealed class ParameterSubsegment : PathSubsegment
{
public ParameterSubsegment(string parameterName) {
if (parameterName.StartsWith("*", StringComparison.Ordinal))
{
this.ParameterName = parameterName.Substring(1);
this.IsCatchAll = true;
}
else
{
this.ParameterName = parameterName;
} }
public bool IsCatchAll { get; private set; }
public string ParameterName { get; private set; }
}
  //存放 url  tamplate  subpathsegment
//比如A{controller}/{action}中 A
public sealed class LiteralSubsegment : PathSubsegment
{
// Methods
public LiteralSubsegment(string literal)
{
this.Literal = literal;
} // Properties
public string Literal { get; private set; }
}

以上是存放被分割的url 片段的类

myRoute 类

  public class MyRoute: RouteBase

        {

        private ParsedRoute _parsedRoute;
private string _url;
private const string HttpMethodParameterName = "httpMethod"; public MyRoute(string url, IRouteHandler routeHandler) {
this.Url = url;
this.RouteHandler = routeHandler; }
public MyRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) {
this.Url = url;
this.Defaults = defaults;
this.RouteHandler = routeHandler; }
public MyRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
{
this.Url = url;
this.Defaults = defaults;
this.Constraints = constraints;
this.RouteHandler = routeHandler; }
public MyRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) {
this.Url = url;
this.Defaults = defaults;
this.Constraints = constraints;
this.DataTokens = dataTokens;
this.RouteHandler = routeHandler; }
//获取routedate
public override RouteData GetRouteData(HttpContextBase httpContext) {
string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
if (values == null)
{
return null;
}
RouteData data = new RouteData(this, this.RouteHandler);
foreach (KeyValuePair<string, object> pair in values)
{
data.Values.Add(pair.Key, pair.Value);
}
if (this.DataTokens != null)
{
foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
{
data.DataTokens[pair2.Key] = pair2.Value;
}
}
return data;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
throw new NotImplementedException();
}
public RouteValueDictionary Constraints { get; set; }
public RouteValueDictionary DataTokens { get; set; }
public RouteValueDictionary Defaults { get; set; }
public IRouteHandler RouteHandler { get; set; }
public string Url
{
get
{
return (this._url ?? string.Empty);
}
set
{
//解析路由也就是分割url
this._parsedRoute = RouteParser.Parse(value);
this._url = value;
}
} }

RouteParesed 类负责解析url 模板

关键地方都有注释

  public static class RouteParser
{
// 解析route uel
public static ParsedRoute Parse(string routeUrl)
{
if (routeUrl == null)
{
routeUrl = string.Empty;
}
//判断网址是否有效
if (IsInvalidRouteUrl(routeUrl))
{
// throw new ArgumentException(SR.GetString("Route_InvalidRouteUrl"), "routeUrl");
} //根据/拆分网址
IList<string> pathSegments = SplitUrlToPathSegmentStrings(routeUrl); //验证部分url
Exception exception = ValidateUrlParts(pathSegments);
if (exception != null)
{
throw exception;
}
//拆分的url加入到PathSegments
return new ParsedRoute(SplitUrlToPathSegments(pathSegments));
} public static Exception ValidateUrlParts(IList<string> pathSegments)
{
HashSet<string> usedParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
bool? nullable = null;
bool flag = false;
foreach (string str in pathSegments)
{
bool flag2;
if (flag)
{
//return new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString("Route_CatchAllMustBeLast"), new object[0]), "routeUrl");
}
if (!nullable.HasValue)
{
nullable = new bool?(IsSeparator(str));
flag2 = nullable.Value;
}
else
{
flag2 = IsSeparator(str);
if (flag2 && nullable.Value)
{
// return new ArgumentException(SR.GetString("Route_CannotHaveConsecutiveSeparators"), "routeUrl");
}
nullable = new bool?(flag2);
}
if (!flag2)
{
Exception exception2;
IList<PathSubsegment> pathSubsegments = ParseUrlSegment(str, out exception2);
if (exception2 != null)
{
return exception2;
}
exception2 = ValidateUrlSegment(pathSubsegments, usedParameterNames, str);
if (exception2 != null)
{
return exception2;
}
flag = pathSubsegments.Any<PathSubsegment>(seg => (seg is ParameterSubsegment) && ((ParameterSubsegment)seg).IsCatchAll);
}
}
return null;
} private static Exception ValidateUrlSegment(IList<PathSubsegment> pathSubsegments, HashSet<string> usedParameterNames, string pathSegment)
{
bool flag = false;
Type type = null;
foreach (PathSubsegment subsegment in pathSubsegments)
{
if ((type != null) && (type == subsegment.GetType()))
{
//return new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString("Route_CannotHaveConsecutiveParameters"), new object[0]), "routeUrl");
}
type = subsegment.GetType();
if (!(subsegment is LiteralSubsegment))
{
ParameterSubsegment subsegment2 = subsegment as ParameterSubsegment;
if (subsegment2 != null)
{
string parameterName = subsegment2.ParameterName;
if (subsegment2.IsCatchAll)
{
flag = true;
}
if (!IsValidParameterName(parameterName))
{
object[] args = new object[] { parameterName };
//return new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_InvalidParameterName"), args), "routeUrl");
}
if (usedParameterNames.Contains(parameterName))
{
object[] objArray2 = new object[] { parameterName };
//return new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_RepeatedParameter"), objArray2), "routeUrl");
}
usedParameterNames.Add(parameterName);
}
}
}
if (flag && (pathSubsegments.Count != 1))
{
//return new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString/("Route_CannotHaveCatchAllInMultiSegment"), new object[0]), "routeUrl"); }
return null;
} private static bool IsValidParameterName(string parameterName)
{
if (parameterName.Length == 0)
{
return false;
}
for (int i = 0; i < parameterName.Length; i++)
{
switch (parameterName[i])
{
case '/':
case '{':
case '}':
return false;
}
}
return true;
} public static bool IsInvalidRouteUrl(string routeUrl)
{
if (!routeUrl.StartsWith("~", StringComparison.Ordinal) && !routeUrl.StartsWith("/", StringComparison.Ordinal))
{
return (routeUrl.IndexOf('?') != -1);
}
return true;
} //判断是否是分隔符
public static bool IsSeparator(string s)
{
return string.Equals(s, "/", StringComparison.Ordinal);
} //
public static IList<PathSegment> SplitUrlToPathSegments(IList<string> urlParts)
{ List<PathSegment> list = new List<PathSegment>();
foreach (string str in urlParts)
{
//判读是否是分割符
if (IsSeparator(str))
{
//如果是分隔符/就加入pathsegment
list.Add(new SeparatorPathSegment());
}
else
{ Exception exception;
//解析 segment
IList<PathSubsegment> subsegments = ParseUrlSegment(str, out exception);
list.Add(new ContentPathSegment(subsegments));
}
}
return list;
} public static IList<PathSubsegment> ParseUrlSegment(string segment, out Exception exception)
{
int startIndex = 0;
List<PathSubsegment> list = new List<PathSubsegment>();
while (startIndex < segment.Length)
{
//获得{索引位置
//比如{controller} num2=0
int num2 = IndexOfFirstOpenParameter(segment, startIndex);
if (num2 == -1)
{ //获取LiteralSubsegment string str3 = GetLiteral(segment.Substring(startIndex));
if (str3 == null)
{
object[] args = new object[] { segment };
//exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), args), "routeUrl");
// return null;
}
if (str3.Length > 0)
{
//加入获取LiteralSubsegment list.Add(new LiteralSubsegment(str3));
}
break;
}
//获得}索引位置
int index = segment.IndexOf('}', num2 + 1);
if (index == -1)
{
object[] objArray2 = new object[] { segment };
//exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), objArray2), "routeUrl");
//return null;
}
//获取LiteralSubsegment string
string literal = GetLiteral(segment.Substring(startIndex, num2 - startIndex));
if (literal == null)
{
object[] objArray3 = new object[] { segment };
//exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), objArray3), "routeUrl");
//return null;
}
if (literal.Length > 0)
{
list.Add(new LiteralSubsegment(literal));
}
//获取参数名称
string parameterName = segment.Substring(num2 + 1, (index - num2) - 1); //加入ParameterSubsegment
list.Add(new ParameterSubsegment(parameterName)); //继续解析segment ,如果startIndex= segment.length表示解析完毕
startIndex = index + 1;
}
exception = null;
return list;
} //去除 url template 中的{}获得文字
public static string GetLiteral(string segmentLiteral)
{
string str = segmentLiteral.Replace("{{", "").Replace("}}", "");
if (!str.Contains("{") && !str.Contains("}"))
{
return segmentLiteral.Replace("{{", "{").Replace("}}", "}");
}
return null;
}
private static int IndexOfFirstOpenParameter(string segment, int startIndex)
{
while (true)
{
startIndex = segment.IndexOf('{', startIndex);
if (startIndex == -1)
{
return -1;
}
if (((startIndex + 1) == segment.Length) || (((startIndex + 1) < segment.Length) && (segment[startIndex + 1] != '{')))
{
return startIndex;
}
startIndex += 2;
}
}
public static IList<string> SplitUrlToPathSegmentStrings(string url)
{
List<string> list = new List<string>();
if (!string.IsNullOrEmpty(url))
{
int index;
for (int i = 0; i < url.Length; i = index + 1)
{
index = url.IndexOf('/', i);
if (index == -1)
{
string str2 = url.Substring(i);
if (str2.Length > 0)
{
list.Add(str2);
}
return list;
}
string item = url.Substring(i, index - i);
if (item.Length > 0)
{
list.Add(item);
}
list.Add("/");
}
}
return list;
}
} }

ParsedRoute 类负责解析请求的url

并且返回匹配url的值也就是route的值

关键地方有注释

  public class ParsedRoute
{ public ParsedRoute(IList<PathSegment> pathSegments) { this.PathSegments = pathSegments; } private static string EscapeReservedCharacters(Match m)
{
return ("%" + Convert.ToUInt16(m.Value[0]).ToString("x2", CultureInfo.InvariantCulture)); }
private static bool ForEachParameter(IList<PathSegment> pathSegments, Func<ParameterSubsegment, bool> action) { for (int i = 0; i < pathSegments.Count; i++)
{
PathSegment segment = pathSegments[i];
if (!(segment is SeparatorPathSegment))
{
ContentPathSegment segment2 = segment as ContentPathSegment;
if (segment2 != null)
{
foreach (PathSubsegment subsegment in segment2.Subsegments)
{
if (!(subsegment is LiteralSubsegment))
{
ParameterSubsegment arg = subsegment as ParameterSubsegment;
if ((arg != null) && !action(arg))
{
return false;
}
}
}
}
}
}
return true; }
private static ParameterSubsegment GetParameterSubsegment(IList<PathSegment> pathSegments, string parameterName) { ParameterSubsegment foundParameterSubsegment = null;
ForEachParameter(pathSegments, delegate (ParameterSubsegment parameterSubsegment) {
if (string.Equals(parameterName, parameterSubsegment.ParameterName, StringComparison.OrdinalIgnoreCase))
{
foundParameterSubsegment = parameterSubsegment;
return false;
}
return true;
});
return foundParameterSubsegment; }
private static bool IsParameterRequired(ParameterSubsegment parameterSubsegment, RouteValueDictionary defaultValues, out object defaultValue) {
if (parameterSubsegment.IsCatchAll)
{
defaultValue = null;
return false;
}
return !defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultValue); }
private static bool IsRoutePartNonEmpty(object routePart) { string str = routePart as string;
if (str != null)
{
return (str.Length > 0);
}
return ((int)routePart > null); }
public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues)
{
//分割virtualPath
IList<string> source = RouteParser.SplitUrlToPathSegmentStrings(virtualPath);
if (defaultValues == null)
{
defaultValues = new RouteValueDictionary();
}
RouteValueDictionary matchedValues = new RouteValueDictionary();
bool flag = false;
bool flag2 = false;
for (int i = 0; i < this.PathSegments.Count; i++)
{
PathSegment segment = this.PathSegments[i];
if (source.Count <= i)
{
flag = true;
}
string a = flag ? null : source[i];
if (segment is SeparatorPathSegment)
{
if (!flag && !string.Equals(a, "/", StringComparison.Ordinal))
{//判断是否为分割符号“/”
return null;
}
}
else
{
ContentPathSegment contentPathSegment = segment as ContentPathSegment;
//判断是否为ContentPathSegment if (contentPathSegment != null)
{
if (contentPathSegment.IsCatchAll)
{
//判断有没有可变参数
this.MatchCatchAll(contentPathSegment, source.Skip<string>(i), defaultValues, matchedValues);
flag2 = true;
}
else if (!this.MatchContentPathSegment(contentPathSegment, a, defaultValues, matchedValues))
{
return null;
}
}
}
}
if (!flag2 && (this.PathSegments.Count < source.Count))
{
for (int j = this.PathSegments.Count; j < source.Count; j++)
{
if (!RouteParser.IsSeparator(source[j]))
{
return null;
}
}
}
if (defaultValues != null)
{
foreach (KeyValuePair<string, object> pair in defaultValues)
{
if (!matchedValues.ContainsKey(pair.Key))
{
matchedValues.Add(pair.Key, pair.Value);
}
}
}
return matchedValues; } private void MatchCatchAll(ContentPathSegment contentPathSegment, IEnumerable<string> remainingRequestSegments, RouteValueDictionary defaultValues, RouteValueDictionary matchedValues) { object obj2;
string str = string.Join(string.Empty, remainingRequestSegments.ToArray<string>());
ParameterSubsegment subsegment = contentPathSegment.Subsegments[0] as ParameterSubsegment;
if (str.Length > 0)
{
obj2 = str;
}
else
{
defaultValues.TryGetValue(subsegment.ParameterName, out obj2);
}
matchedValues.Add(subsegment.ParameterName, obj2); }
private bool MatchContentPathSegment(ContentPathSegment routeSegment, string requestPathSegment, RouteValueDictionary defaultValues, RouteValueDictionary matchedValues) { //判断requestPathSegment 是否为空
//如果为空直接用默认值
if (string.IsNullOrEmpty(requestPathSegment))
{
if (routeSegment.Subsegments.Count <= 1)
{
object obj2;
ParameterSubsegment subsegment3 = routeSegment.Subsegments[0] as ParameterSubsegment;
if (subsegment3 == null)
{
return false;
}
if (defaultValues.TryGetValue(subsegment3.ParameterName, out obj2))
{
matchedValues.Add(subsegment3.ParameterName, obj2);
return true;
}
}
return false;
} //获取pathsegment.length 长度
int length = requestPathSegment.Length; int num2 = routeSegment.Subsegments.Count - 1;
ParameterSubsegment subsegment = null;
LiteralSubsegment subsegment2 = null; //循环routeSegment
while (num2 >= 0)
{
int num3 = length;
//判断是否为ParameterSubsegment
ParameterSubsegment subsegment4 = routeSegment.Subsegments[num2] as ParameterSubsegment;
if (subsegment4 != null)
{
subsegment = subsegment4;
}
else
{
//判断是否为LiteralSubsegment
LiteralSubsegment subsegment5 = routeSegment.Subsegments[num2] as LiteralSubsegment;
if (subsegment5 != null)
{
subsegment2 = subsegment5;
int startIndex = length - 1;
//判断索引是否为空
if (subsegment != null)
{
//startIndex-1 表示从倒数第二个字符开始索引
startIndex--;
}
if (startIndex < 0)
{
return false;
}
//搜索指定LiteralSubsegment
int num5 = requestPathSegment.LastIndexOf(subsegment5.Literal, startIndex, StringComparison.OrdinalIgnoreCase);
//num5=-1 表示没有索引到LiteralSubsegment
if (num5 == -1)
{
return false;
} if ((num2 == (routeSegment.Subsegments.Count - 1)) && ((num5 + subsegment5.Literal.Length) != requestPathSegment.Length))
{
return false;
}
num3 = num5;
}
}
// subsegment!=null subsegment2!=null subsegment4==null num2=0
//表示找到最后一LiteralSubsegment
if ((subsegment != null) && (((subsegment2 != null) && (subsegment4 == null)) || (num2 == 0)))
{
int num6;
int num7;
if (subsegment2 == null)
{
if (num2 == 0)
{
num6 = 0;
}
else
{
num6 = num3;
}
num7 = length;
}
else if ((num2 == 0) && (subsegment4 != null))
{
num6 = 0;
num7 = length;
}
else
{
//Substring起始位置
num6 = num3 + subsegment2.Literal.Length;
//截取的字符个数
num7 = length - num6;
}
string str = requestPathSegment.Substring(num6,num7);
if (string.IsNullOrEmpty(str))
{
return false;
}
//加入匹配值
matchedValues.Add(subsegment.ParameterName, str);
subsegment = null;
subsegment2 = null;
}
length = num3;
num2--;
}
if (length != 0)
{
return (routeSegment.Subsegments[0] is ParameterSubsegment);
}
return true; }
private static bool RoutePartsEqual(object a, object b) {
string str = a as string;
string str2 = b as string;
if ((str != null) && (str2 != null))
{
return string.Equals(str, str2, StringComparison.OrdinalIgnoreCase);
}
if ((a != null) && (b != null))
{
return a.Equals(b);
}
return (a == b); }
private static string UrlEncode(string str)
{
return Regex.Replace(Uri.EscapeUriString(str), "([#;?:@&=+$,])", new MatchEvaluator(ParsedRoute.EscapeReservedCharacters));
} // Properties
private IList<PathSegment> PathSegments { get; set; } }
}

以上是route的大概流程

(1)先通过url  tamplate 解析 url

(2) 解析请求的url 比较匹配赋值

(3) 再根据匹配到的值映射到相应的controller 和 Action 进行处理

 
分类: MVC

mvc5 解析route源码实现自己的route系统的更多相关文章

  1. Asp.net mvc5 解析route源码实现自己的route系统

    url route 路由系统的责任是找到匹配的路由,创建路由数据,并将请求分配给一个处理程序. 选择动作是 MVC 的处理程序的实现细节.它使用路由数据和从传入请求其他信息来选择要执行的操作 实例 源 ...

  2. Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试

    摘要:由于近期打算修改Python解释器以实现pyc文件的加密/解密,出于保密的要求,解密之后的数据只能放在内存中,不能写入到文件中.但是后续的解析pyc文件的代码又只能接受FILE*作为入参,所以就 ...

  3. HtmlAgilityPack --解析Html源码

    最近项目需要从网络上抓取一下数据解析Html源码,奈何正则表达式难写,于是网上搜索找到了“ HtmlAgilityPack”类库,敏捷开发,果然效率非同寻常. 在此做笔记,写下心得,顺便给自己总结一下 ...

  4. 浩哥解析MyBatis源码(十)——Type类型模块之类型处理器

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6715063.html 1.回顾 之前的两篇分别解析了类型别名注册器和类型处理器注册器,此二 ...

  5. 解析 ViewTreeObserver 源码(下)

    继上篇内容,本文介绍 ViewTreeObserver 的使用,以及体会其所涉及的观察者模式,期间会附带回顾一些基础知识.最后,我们简单聊一下 Android 的消息传递,附高清示意图,轻松捋清整个传 ...

  6. Jsoup解析网页源码时常用的Element(s)类

    Jsoup解析网页源码时常用的Element(s)类 一.简介 该类是Node的直接子类,同样实现了可克隆接口.类声明:public class Element extends Node 它表示由一个 ...

  7. 用Beautiful Soup解析html源码

    #xiaodeng #python3 #用Beautiful Soup解析html源码 html_doc = """ <html> <head> ...

  8. 二十三、并发编程之深入解析Condition源码

    二十三.并发编程之深入解析Condition源码   一.Condition简介 1.Object的wait和notify/notifyAll方法与Condition区别 任何一个java对象都继承于 ...

  9. Django(49)drf解析模块源码分析

    前言 上一篇分析了请求模块的源码,如下: def initialize_request(self, request, *args, **kwargs): """ Retu ...

随机推荐

  1. 【数据结构&amp;&amp;等差数列】KMP简介和算法的实现(c++ &amp;&amp; java)

    KMP算法假定了解案件的原则,其实很easy. KMP算法简述 关于根据自己的理解在这里. KMP该算法由三个发明人的名称(Knuth.Morris.Pratt)的首字母组成,又称字符串查找算法. 个 ...

  2. C++习题 复数类--重载运算符+

    Description 定义一个复数类Complex,重载运算符"+",使之能用于复数的加法运算.将运算符函数重载为非成员.非友元的普通函数.编写程序,求两个复数之和. Input ...

  3. 【原创】shadowebdict开发日记:基于linux的简明英汉字典(四)

    全系列目录: [原创]shadowebdict开发日记:基于linux的简明英汉字典(一) [原创]shadowebdict开发日记:基于linux的简明英汉字典(二) [原创]shadowebdic ...

  4. Gradle学习系列之一——Gradle快速入门(转)

    这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...

  5. 修改系统环境变量 cmd命令

    详细大家对cmd的使用都有了一些简单的了解,但是困扰大家的主要的问题就是: cmd命令修改环境变量有两种方式:1. 短期内有效,在关闭dos窗口后就自动失效 2.长期有效,关闭dos窗口后还有效 下面 ...

  6. C++ Primer 学习笔记_2_高速入口(继续)

     P15习题 //题1.14: 试分析假设v1 == v2的情况下,该程序的输出结果 #include <iostream> int main() { std::cout <&l ...

  7. 《Java程序猿面试笔试宝典》之Java与C/C++有什么异同

    Java与C++都是面向对象语言,都使用了面向对象思想(比如封装.继承.多态等),因为面向对象有很多非常好的特性(继承.组合等),使得二者都有非常好的可重用性. 须要注意的是,二者并不是全然一样,以下 ...

  8. unity3d中让物体显示和隐藏

    unity3d中让物体显示和隐藏的方法 gameObject.renderer.enabled //是控制一个物体是否在屏幕上渲染或显示  而物体实际还是存在的 仅仅是想当于隐身 而物体本身的碰撞体还 ...

  9. 告别乱码,针对GBK、UTF-8两种编码的智能URL解码器的java实现(转)

    效果图 字符 字符是早于计算机而存在,从人类有文明那时起,人们就用一个个符号代表世间万象.如ABC,如“一.二.三”. 字符集 字符集是所有字符的集合. XXX字符集 给字符集中的每一个字符套上一个序 ...

  10. 【Bootstrap】自己主动去适应PC、平面、手机Bootstrap网格系统

    酒吧格英语作为一门系统"grid systems",也有人翻译成"网络格系统".使用固定格子设计布局,其风格整齐而简洁,在二战结束后人气,流风格之中的一个. 1 ...