[ASP.NET MVC Mavericks Road] 07 - Routage d'URL

黄舟
Libérer: 2016-12-30 14:30:57
original
1258 Les gens l'ont consulté

[ASP.NET
MVC Mavericks Road]07 - Routage d'URL

Nous savons que dans ASP.NET Web Forms, une requête d'URL correspond souvent à une page aspx, et une page aspx est un Le fichier physique qui contient le traitement de la demande.

Dans ASP.NET MVC, une requête d'URL est traitée par une action dans le contrôleur correspondant, et le routage d'URL indique à MVC comment localiser le contrôleur et l'action appropriés.

De manière générale, le Routage d'URL comprend deux fonctions principales : analyser les URL et générer des URL. Cet article se concentrera sur ces deux points majeurs.

Répertoire de cet article


Comment le routage d'URL est défini

Commençons par une URL simple comme la suivante :
http://mysite.com/Admin/Index
Après le nom de domaine, "/" est utilisé par défaut pour segmenter l'URL. Le système de routage peut savoir que les fragments Admin et Index de cette URL correspondent respectivement aux noms du Contrôleur et de l'Action via une chaîne au format {contrôleur}/{action}.

Par défaut, le nombre de segments séparés par "/" dans le format de routage est le même que le nombre de segments suivant le nom de domaine de l'URL. Par exemple, seuls deux segments correspondront pour le {contrôleur}. /{action}format. Comme le montre le tableau suivant :

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

Le routage d'URL est défini dans la méthode RegisterRoutes dans le fichier RouteConfig.cs sous le dossier App_Start du projet MVC. un vide Une simple définition de routage d'URL générée par le système lors du projet MVC :

public static void RegisterRoutes(RouteCollection routes) {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

    routes.MapRoute( 
        name: "Default", 
        url: "{controller}/{action}/{id}", 
        defaults: new { controller = "Home", action = "Index",  id = UrlParameter.Optional } 
    );
}
Copier après la connexion

La méthode statique RegisterRoutes est appelée dans la méthode Application_Start dans le fichier Global.asax.cs en plus de la définition de. Routage d'URL, il contient également les définitions de certaines autres fonctionnalités principales de MVC :

protected void Application_Start() { 
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration); 
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
    RouteConfig.RegisterRoutes(RouteTable.Routes); 
    BundleConfig.RegisterBundles(BundleTable.Bundles); 
}
Copier après la connexion


Ce qui est transmis dans la méthode RouteConfig.RegisterRoutes est la propriété statique Routes de la classe RouteTable, qui renvoie une instance de RouteCollection. En fait, la manière "originale" de définir une route peut s'écrire ainsi :

public static void RegisterRoutes(RouteCollection routes) { 

    Route myRoute = new Route("{controller}/{action}", new MvcRouteHandler()); 
    routes.Add("MyRoute", myRoute); 
}
Copier après la connexion

Lors de la création de l'objet Route, une chaîne de format URL et un objet MvcRouteHandler sont utilisés comme paramètres du constructeur. Différentes technologies ASP.NET ont différents RouteHandlers et MVC utilise MvcRouteHandler.

Cette façon d'écrire est un peu lourde. Une méthode de définition plus simple est :

public static void RegisterRoutes(RouteCollection routes) { 

    routes.MapRoute("MyRoute", "{controller}/{action}"); 
}
Copier après la connexion
Copier après la connexion

Cette méthode est concise et facile à lire. Généralement, nous utiliserons cette méthode pour définir des itinéraires.

Préparation des échantillons

À titre de démonstration, préparons d'abord une démo. Créez une application MVC standard, puis ajoutez trois contrôleurs simples, à savoir HomeController, CustomerController et AdminController. Le code est le suivant :


HomeController


CustomerController
<. 🎜>
AdminController

Ajoutez une vue partagée nommée ActionName.cshtml à ces trois contrôleurs sous le dossier /Views/Shared Le code est le suivant :


ActionName.cshtml.

Nous avons supprimé la définition du Rounting d'URL générée automatiquement par le projet dans le fichier RouteConfig.cs, puis sur la base des connaissances en matière de définition de routage mentionnées ci-dessus, nous avons écrit nous-mêmes la définition la plus simple :

public static void RegisterRoutes(RouteCollection routes) { 

    routes.MapRoute("MyRoute", "{controller}/{action}"); 
}
Copier après la connexion
Copier après la connexion
Le programme s'exécute et l'URL se trouve dans Admin/Index. Regardez les résultats en cours d'exécution :

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

Ce que cette démo génère est le nom du contrôleur et de l'action appelés. .

Définir les valeurs par défaut pour les variables de fragment

Dans ce qui précède, nous devons localiser l'URL d'un contrôleur et d'une action spécifiques, sinon, le programme signalera une erreur, car MVC ne sait pas quelle action exécuter. Nous pouvons demander à MVC d'utiliser une certaine valeur par défaut en spécifiant une valeur par défaut lorsque l'URL ne fournit pas de fragment correspondant. Spécifiez les valeurs par défaut pour le contrôleur et l'action comme suit :

routes.MapRoute("MyRoute", "{controller}/{action}",  new { controller = "Home", action = "Index" });
Copier après la connexion
Copier après la connexion

这个Demo输出的是被调用的Controller和Action名称。


给片段变量定义默认值

在上面我们必须把URL定位到特定Controller和Action,否则程序会报错,因为MVC不知道去执行哪个Action。 我们可以通过指定默认值来告诉MVC当URL没有给出对应的片段时使用某个默认的值。如下给controller和action指定默认值:

routes.MapRoute("MyRoute", "{controller}/{action}",  new { controller = "Home", action = "Index" });
Copier après la connexion
Copier après la connexion

这时候如果在URL中不提供action片段的值或不提供controller和action两个片段的值,MVC将使用路由定义中提供的默认值:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL



它的各种匹配情况如下表所示:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

注意,对于上面的URL路由的定义,我们可以只给action一个片段指定默认值,但是不能只给controller一个片段指定默认值,即如果我们给Controller指定了默认值,就一定也要给action指定默认值,否则URL只有一个片段时,这个片段匹配给了controller,action将找不到匹配。

定义静态片段

并不是所有的片段都是用来作为匹配变量的,比如,我们想要URL加上一个名为Public的固定前缀,那么我们可以这样定义:

routes.MapRoute("", "Public/{controller}/{action}",  new { controller = "Home", action = "Index" });
Copier après la connexion


这样,请求的URL也需要一个Public前缀与之匹配。我们也可以把静态的字符串放在大括号以外的任何位置,如:

routes.MapRoute("", "X{controller}/{action}",  new { controller = "Home", action = "Index" });
Copier après la connexion


在一些情况下这种定义非常有用。比如当你的网站某个链接已经被用户普遍记住了,但这一块功能已经有了一个新的版本,但调用的是不同名称的controller,那么你把原来的controller名称作为现在controller的别名。这样,用户依然使用他们记住的URL,而导向的却是新的controller。如下使用Shop作为Home的一个别名:

routes.MapRoute("ShopSchema", "Shop/{action}",  new { controller = "Home" });
Copier après la connexion

这样,用户使用原来的URL可以访问新的controller:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

自定义片段变量

自定义片段变量的定义和取值

contrlloer和action片段变量对MVC来说有着特殊的意义,在定义一个路由时,我们必须有这样一个概念:contrlloer和action的变量值要么能从URL中匹配得到,要么由默认值提供,总之一个URL请求经过路由系统交给MVC处理时必须保证contrlloer和action两个变量的值都有。当然,除了这两个重要的片段变量,我们也可从通过自定义片段变量来从URL中得到我们想要的其它信息。如下自定义了一个名为Id的片段变量,而且给它定义了默认值:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}",
    new {
        controller = "Home",
        action = "Index",
        id = "DefaultId"
});
Copier après la connexion

我们在HomeController中增加一个名为CustomVariable的ACtion来演示一下如何取自定义的片段变量:

public ActionResult CustomVariable() {
    ViewBag.Controller = "Home";
    ViewBag.Action = "CustomVariable";
    ViewBag.CustomVariable = RouteData.Values["id"];
    return View("ActionName");
}
Copier après la connexion

可以通过 RouteData.Values[segment] 来取得任意一个片段的变量值。

再稍稍改一下ActionName.cshtml 来看一下我们取到的自定义片段变量的值:

...
<p>The controller is: @ViewBag.Controller</p> 
<p>The action is: @ViewBag.Action</p> 
<p>The custom variable is: @ViewBag.CustomVariable</p>
...
Copier après la connexion

将URL定位到 /Home/CustomVariable/Hello 将得到如下结果:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

自定义的片段变量用处很大,也很灵活,下面介绍一些常见的用法。

将自定义片段变量作为Action方法的参数

我们可以将自定义的片段变量当作参数传递给Action方法,如下所示:

public ActionResult CustomVariable(string id) { 
    ViewBag.Controller = "Home"; 
    ViewBag.Action = "CustomVariable"; 
    ViewBag.CustomVariable = id; 
    return View("ActionName"); 
}
Copier après la connexion

效果和上面是一样的,只不过这样省去了用 RouteData.Values[segment] 的方式取自定义片段变量的麻烦。这个操作背后是由模型绑定来做的,模型绑定的知识我将在后续博文中进行讲解。

指定自定义片段变量为可选

指定自定片段变量为可选,即在URL中可以不用指定片段的值。如下面的定义将Id定义为可选:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new {
        controller = "Home",
        action = "Index",        id = UrlParameter.Optional});
Copier après la connexion

定义为可选以后,需要对URL中没有Id这个片段值的情况进行处理,如下:

public ActionResult CustomVariable(string id) { 
    ViewBag.Controller = "Home"; 
    ViewBag.Action = "CustomVariable"; 
    ViewBag.CustomVariable = id == null ? "<no value>" : id; 
    return View("ActionName"); 
}
Copier après la connexion

当Id是整型的时候,参数的类型需要改成可空的整型(即int? id)。

为了省去判断参数是否为空,我们也可以把Action方法的id参数也定义为可选,当没有提供Id参数时,Id使用默认值,如下所示:

public ActionResult CustomVariable(string id = "DefaultId") { 
    ViewBag.Controller = "Home"; 
    ViewBag.Action = "CustomVariable"; 
    ViewBag.CustomVariable = id; 
    return View("ActionName"); 
}
Copier après la connexion

这样其实就是和使用下面这样的方式定义路由是一样的:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "DefaultId" });
Copier après la connexion


定义可变数量的自定义片段变量

我们可以通过 catchall 片段变量加 * 号前缀来定义匹配任意数量片段的路由。如下所示:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", 
    new { controller = "Home", action = "Index",  id = UrlParameter.Optional });
Copier après la connexion

这个路由定义的匹配情况如下所示:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL


使用*catchall,将匹配的任意数量的片段,但我们需要自己通过“/”分隔catchall变量的值来取得独立的片段值。


路由约束

正则表达式约束

通过正则表达式,我们可以制定限制URL的路由规则,下面的路由定义限制了controller片段的变量值必须以 H 打头:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },    new { controller = "^H.*" });
Copier après la connexion


定义路由约束是在MapRoute方法的第四个参数。和定义默认值一样,也是用匿名类型。

我们可以用正则表达式约束来定义只有指定的几个特定的片段值才能进行匹配,如下所示:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { controller = "^H.*", action = "^Index$|^About$" }
);
Copier après la connexion

这个定义,限制了action片段值只能是Index或About,不区分大小写。

Http请求方式约束

我们还可以限制路由只有当以某个特定的Http请求方式才能匹配。如下限制了只能是Get请求才能进行匹配:

routes.MapRoute("MyRoute", "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { controller = "^H.*", httpMethod = new HttpMethodConstraint("GET") }
);
Copier après la connexion

通过创建一个 HttpMethodConstraint 类的实例来定义一个Http请求方式约束,构造函数传递是允许匹配的Http方法名。这里的httpMethod属性名不是规定的,只是为了区分。

这种约束也可以通过HttpGet或HttpPost过滤器来实现,后续博文再讲到滤器的内容。

自定义路由约束

如果标准的路由约束满足不了你的需求,那么可以通过实现 IRouteConstraint 接口来定义自己的路由约束规则。

我们来做一个限制浏览器版本访问的路由约束。在MVC工程中添加一个文件夹,取名Infrastructure,然后添加一个 UserAgentConstraint 类文件,代码如下:

public class UserAgentConstraint : IRouteConstraint {
        
    private string requiredUserAgent;

    public UserAgentConstraint(string agentParam) {
        requiredUserAgent = agentParam;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName,
        RouteValueDictionary values, RouteDirection routeDirection) {
            
        return httpContext.Request.UserAgent != null 
            && httpContext.Request.UserAgent.Contains(requiredUserAgent);
    }
}
Copier après la connexion

这里实现IRouteConstraint的Match方法,返回的bool值告诉路由系统请求是否满足自定义的约束规则。我们的UserAgentConstraint类的构造函数接收一个浏览器名称的关键字作为参数,如果用户的浏览器包含注册的关键字才可以访问。接一来,我们需要注册自定的路由约束:

public static void RegisterRoutes(RouteCollection routes) {

    routes.MapRoute("ChromeRoute", "{*catchall}",
        new { controller = "Home", action = "Index" },
        new { customConstraint = new UserAgentConstraint("Chrome") }
    );
}
Copier après la connexion

下面分别是IE10和Chrome浏览器请求的结果:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

[ASP.NET MVC Mavericks Road] 07 - Routage dURL


定义请求磁盘文件路由

并不是所有的URL都是请求controller和action的。有时我们还需要请求一些资源文件,如图片、html文件和JS库等。

我们先来看看能不能直接请求一个静态Html文件。在项目的Content文件夹下,添加一个html文件,内容随意。然后把URL定位到该文件,如下图:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

我们看到,是可以直接访问一静态资源文件的。

默认情况下,路由系统先检查URL是不是请求静态文件的,如果是,服务器直接返回文件内容并结束对URL的路由解析。我们可以通过设置 RouteCollection的 RouteExistingFiles 属性值为true 让路由系统对静态文件也进行路由匹配,如下所示:

public static void RegisterRoutes(RouteCollection routes) {
    
    routes.RouteExistingFiles = true;

    routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional
    });
}
Copier après la connexion

设置了routes.RouteExistingFiles = true后,还需要对IIS进行设置,这里我们以IIS Express为例,右键IIS Express小图标,选择“显示所有应用程序”,弹出如下窗口:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

点击并打开配置文件,Control+F找到UrlRoutingModule-4.0,将这个节点的preCondition属性改为空,如下所示:

<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition=""/>
Copier après la connexion


然后我们运行程序,再把URL定位到之前的静态文件:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL


这样,路由系统通过定义的路由去匹配RUL,如果路由中没有定义该静态文件的匹配,则会报上面的错误。

一旦定义了routes.RouteExistingFiles = true,我们就要为静态文件定义路由,如下所示:

public static void RegisterRoutes(RouteCollection routes) {
    
    routes.RouteExistingFiles = true;    routes.MapRoute("DiskFile", "Content/StaticContent.html",
        new { controller = "Customer", action = "List", });

    routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
Copier après la connexion

这个路由匹配Content/StaticContent.html的URL请求为controller = Customer, action = List。我们来看看运行结果:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

这样做的目的是为了可以在Controller的Action中控制对静态资源的请求,并且可以阻止对一些特殊资源文件的访问。

设置了RouteExistingFiles属性为true后,我们要为允许用户请求的资源文件进行路由定义,如果每种资源文件都去定义相应的路由,就会显得很繁琐。

我们可以通过RouteCollection类的IgnoreRoute方法绕过路由定义,使得某些特定的静态文件可以由服务器直接返回给给浏览器,如下所示:

public static void RegisterRoutes(RouteCollection routes) {
    
    routes.RouteExistingFiles = true;    routes.IgnoreRoute("Content/{filename}.html");

    routes.MapRoute("DiskFile", "Content/StaticContent.html",
        new { controller = "Customer", action = "List", });

    routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
Copier après la connexion

这样,只要是请求Content目录下的任何html文件都能被直接返回。这里的IgnoreRoute方法将创建一个RouteCollection的实例,这个实例的Route Handler 为 StopRoutingHandler,而不是 MvcRouteHandler。运行程序定位到Content/StaticContent.html,我们又看到了之前的静态面面了。


生成URL(链接)

前面讲的都是解析URL的部分,现在我们来看看如何通过路由系统在View中生成URL。

生成指向当前controller的action链接

在View中生成URL的最简单方法就是调用Html.ActionLink方法,如下面在 Views/Shared/ActionName.cshtml 中的代码所示:

...
<p>The controller is: @ViewBag.Controller</p>
<p>The action is: @ViewBag.Action</p>
<p>
    @Html.ActionLink("This is an outgoing URL", "CustomVariable")
</p>
Copier après la connexion

这里的Html.ActionLink方法将会生成指向View对应的Controller和第二个参数指定的Action,我们可以看看运行后页面是如何显示的:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

经过查看Html源码,我们发现它生成了下面这样的一个html链接:

<a href="/Home/CustomVariable">This is an outgoing URL</a>
Copier après la connexion

这样看起来,通过Html.ActionLink生成URL似乎并没有直接在View中自己写一个标签更直接明了。 但它的好处是,它会自动根据路由配置来生成URL,比如我们要生成一个指向HomeContrller中的CustomVariable Action的连接,通过Html.ActionLink方法,只需要给出对应的Controller和Action名称就行,我们不需要关心实际的URL是如何组织的。举个例子,我们定义了下面的路由:

运行程序,我们发现它会自动生成下面这样的连接:

所以我们要生成指向某个Action的链接时,最好使用Html.ActionLink方法,否则你很难保证你手写的连接就能定位到你想要的Action。

生成其他controller的action链接

上面我们给Html.ActionLink方法传递的第二个参数只告诉了路由系统要定位到当前View对应的Controller下的Action。Html.ActionLink方法可以使用第三个参数来指定其他的Controller,如下所示:

它会自动生成如下链接:


生成带有URL参数的链接

有时候我们想在连接后面加上参数以传递数据,如 ?id=xxx 。那么我们可以给Html.ActionLink方法指定一个匿名类型的参数,如下所示:

它生成的Html如下:


指定链接的Html属性

通过Html.ActionLink方法生成的链接是一个a标签,我们可以在方法的参数中给标签指定Html属性,如下所示:

这里的class加了@符号,是因为class是C#关键字,@符号起到转义的作用。它生成 的Html代码如下:


生成完整的标准链接

前面的都是生成相对路径的URL链接,我们也可以通过Html.ActionLink方法生成完整的标准链接,方法如下:


这是Html.ActionLink方法中最多参数的重载方法,它允许我们提供请求的协议(https)和目标服务器地址(myserver.mydomain.com)等。它生成的链接如下:

生成URL字符串

用Html.ActionLink方法生成一个html链接是非常有用而常见的,如果要生成URL字符串(而不是一个Html链接),我们可以用 Url.Action 方法,使用方法如下:

它显示到页面是这样的:

[ASP.NET MVC Mavericks Road] 07 - Routage dURL

根据指定的路由名称生成URL

我们可以根据某个特定的路由来生成我们想要的URL,为了更好说明这一点,下面给出两个URL的定义:


对于这样的两个路由,对于类似下面这样的写法:

始终会生成这样的链接:

也就是说,永远无法使用第二个路由来生成App前缀的链接。这时候我们需要通过另一个方法Html.RouteLink来生成URL了,方法如下:

它会生成如下链接:

这个链接指向的是HomeController下的Index Action。但需要注意,通过这种方式来生成URL是不推荐的,因为它不能让我们从直观上看到它生成的URL指向的controller和action。所以,非到万不得已的情况才会这样用。

在Action方法中生成URL

通常我们一般在View中才会去生成URL,但也有时候我们需要在Action中生成URL,方法如下:

public ViewResult MyActionMethod() { 
    
    string myActionUrl = Url.Action("Index", new { id = "MyID" }); 
    string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "Index" }); 
    
    //... do something with URLs... 
    return View(); 
}
Copier après la connexion

其中 myActionUrl 和 myRouteUrl 将会被分别赋值 /Home/Index/MyID 和 / 。

更多时候我们会在Action方法中将客户端浏览器重定向到别的URL,这时候我们使用RedirectToAction方法,如下:

public RedirectToRouteResultMyActionMethod() { 
    return RedirectToAction("Index");
}
Copier après la connexion

RedirectToAction的返回结果是一个RedirectToRouteResult类型,它使MVC触发一个重定向行为,并调用指定的Action方法。RedirectToAction也有一些重载方法,可以传入controller等信息。也可以使用RedirectToRoute方法,该方法传入的是object匿名类型,易读性强,如:

public RedirectToRouteResult MyActionMethod() {
    return RedirectToRoute(new { controller = "Home", action = "Index", id = "MyID" });
}
Copier après la connexion

URL方案最佳实践

下面是一些使用URL的建议:

最好能直观的看出URL的意义,不要用应用程序的具体信息来定义URL。比如使用 /Articles/Report 比使用 /Website_v2/CachedContentServer/FromCache/Report 好。

使用内容标题比使用ID好。比如使用 /Articles/AnnualReport 比使用 /Articles/2392 好。如果一定要使用使用ID(比如有时候可能需要区分相同的标题),那么就两者都用,如 /Articles/2392/AnnualReport ,它看起来很长,但对用户更友好,而且更利于SEO。

对于Web页面不要使用文件扩展名(如 .aspx 或 .mvc)。但对于特殊的文件使用扩展名(如 .jpg、.pdf 和 .zip等)。

尽可能使用层级关系的URL,如 /Products/Menswear/Shirts/Red,这样用户就能猜到父级URL。

不区分大小写,这样方便用户输入。

正确使用Get和Post。Get一般用来从服务器获取只读的信息,当需要操作更改状态时使用Post。

尽可能避免使用标记符号、代码、字符序列等。如果你想要用标记进行分隔,就使用中划线(如 /my-great-article),下划线是不友好的,另外空格和+号都会被URL编码。

不要轻易改变URL,尤其对于互联网网站。如果一定要改,那也要尽可能长的时间保留原来的URL。

尽量让URL使用统一的风格或习惯。

 以上就是[ASP.NET MVC 小牛之路]07 - URL Routing的内容,更多相关内容请关注PHP中文网(www.php.cn)!


source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal