当前位置: 首页 > news >正文

行业论坛网站wordpress 十个

行业论坛网站,wordpress 十个,网页设计需要学什么软件知乎,巨量引擎广告投放平台官网1谈一谈.NET 的跨平台终于要写到这一篇了。跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标。我在前面的文章已经提到过了#xff0c;Microsoft Graph 服务针对一些主流的开源平台#xff08;主要用来做跨平台应用#xff09;都有支持#xff0c;例如 python,… 1谈一谈.NET 的跨平台终于要写到这一篇了。跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标。我在前面的文章已经提到过了Microsoft Graph 服务针对一些主流的开源平台主要用来做跨平台应用都有支持例如 python,nodejs 等。他们真的非常好用与此同时我虽然对他们也有一定的了解但要跟我最熟悉的 Microsoft .NET 来比较的话我自然还是更喜欢后者了。所以一直在等待合适的时间要来写 Microsoft .NET 的跨平台应用这是多么令人期待的事情啊。经过一段时间的研究我今天正式隆重地给大家介绍如何在 ASP.NET Core 平台上面构建一个 MVC 应用程序并且在里面集成 Microsoft Graph。关于Microsoft .NET 这几年的发展我是感到比较兴奋的作为一个从.NET 1.1就开始追随的骨灰级粉丝我很高兴地看到现在.NET 已经真正迈出了跨平台的脚步而且完全开源了。如果要讲这个话题恐怕我是一时半会刹不住车的所以我就此打住吧有兴趣的朋友们可以通过下面这个网址了解更多.NET 的发展情况。 这一篇文章用到的技术是最新的.NET Core 中 ASP.NET Core 提供的我使用了其中的 MVC 这个模板创建了一个简单的应用程序并且略微改造了一下使其能够采用 Azure AD 进行身份验证继而通过获得的用户凭据能实现对 Microsoft Graph 的使用。2ASP.NET Core MVC 整合了 Graph 的场景效果我已经编写好了一个完整的范例请大家通过下面的地址进行下载我后面将大致提到一些重点的功能是如何实现的 https://github.com/chenxizhang/office365dev/tree/master/samples/aspnetcoremvc在准备这个范例以及编写这个文章的时候为了全面地测试在跨平台开发方面的能力我完全采用了一台全新的 MacBook 作为工作用机开发工具我使用的是 Visual Studio Code这个直接就可以在 Mac 里面运行就像你看到的这样。好的大致背景我也交代清楚了如果大家下载了代码可以跟我一起来体验一下这个应用程序运行起来的效果吧 —— 我推荐你也用 Visual Studio Code 来打开这个应用程序。是的你不再需要安装 Visual Studio 完整版打开命令行工具我们很快要运行几个命令 请运行下面的命令下载当前项目所依赖的一些组件包dotnet restore然后运行下面的命令可以将当前项目运行起来dotnet run如果不出意外的话我写好的这个简单的应用程序会启动起来并且在本机的5000端口进行监听看起来跟我们一般的 MVC 程序真的是一样一样的此时请点击页面顶部左上角的“About”看看会发生什么呢输入正确的用户名和密码后你就可以看到该用户的基本信息了好了功能确实就是这样足够简单不是吗但是正如我猜想你应该会想到的那样只要打开了 Graph 这扇大门无穷的宝藏就等着你尽情地创造性地利用了。3代码解析下面我还是简单地讲解一下我在标准的模板基础上做过哪些定制从而实现了上面的功能的。要创建这个应用程序你需要安装 dotnet sdkhttps://www.microsoft.com/net/download/core 然后在本地命令行工具中运行 dotnet new mvc 即可首先我为项目添加了几个外部组件包这是通过修改项目定义文件aspnetcoremvc.csproj来实现的。PackageReference IncludeMicrosoft.Graph Version1.4.0/ PackageReference IncludeMicrosoft.AspNetCore.Authentication.OpenIdConnect Version1.1.0/ PackageReference IncludeMicrosoft.AspNetCore.Authentication.Cookies Version1.1.0/ PackageReference IncludeMicrosoft.IdentityModel.Clients.ActiveDirectory Version3.13.9/ PackageReference IncludeMicrosoft.AspNetCore.Session Version1.1.0/ PackageReference IncludeMicrosoft.Extensions.Caching.Memory Version1.1.2/这些组件都是托管在 nuget.org 这个网站上面甚至整个 dotnet core 的核心组件也都是开源托管在这个上面。一般添加完这些组件后都需要运行 dotnet restore 命令在本地进行还原。然后我修改了 Startup.cs 文件。这是 asp.net core 应用程序的一个标准文件用来定义程序入口加载相关服务和中间件。这里涉及的知识面太多以至于我无法一一说明有兴趣可以参考 https://asp.net/core了解。using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; //这里增加了一写命名空间导入 using Microsoft.Extensions.Caching; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Session; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.AspNetCore.Authentication; using System.Security.Claims; using Microsoft.AspNetCore.Http;namespace aspntecoremvc {public class Startup{// This method gets called by the runtime. Use this method to add services to the container.//这里引入一些服务注入一些组件public void ConfigureServices(IServiceCollection services){services.AddSession();services.AddAuthentication(sharedoptions sharedoptions.SignInScheme CookieAuthenticationDefaults.AuthenticationScheme);// Add framework services.services.AddMvc();}//这里定义了一些静态信息private readonly string ClientIde91ef175-e38d-4feb-b1ed-f243a6a81b93;private readonly string AuthorityString.Format(https://login.microsoftonline.com/{0},office365devlabs.onmicrosoft.com);private readonly string ClientSecret2F5jdoGGNn59oxeDLE9fXx5tD86uvzIji74dmLaj3YI;private readonly string GraphResourceIdhttps://graph.microsoft.com;private readonly string CallbackPath /signin-oidc;// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){loggerFactory.AddConsole();loggerFactory.AddDebug();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();}else{app.UseExceptionHandler(/Home/Error);}app.UseStaticFiles();//这里几步是最关键的定义了如何进行身份认证以及如何保存app.UseSession();app.UseCookieAuthentication();app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions{ClientId ClientId,Authority Authority,ClientSecret ClientSecret,ResponseType OpenIdConnectResponseType.CodeIdToken,CallbackPath CallbackPath,GetClaimsFromUserInfoEndpoint true,Events new OpenIdConnectEvents{OnAuthorizationCodeReceived OnAuthorizationCodeReceived}});app.UseMvc(routes {routes.MapRoute(name: default,template: {controllerHome}/{actionIndex}/{id?});});}private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context){// 将 Token 信息保存在 session 里面后续 Graph 就可以直接调用了string userObjectId (context.Ticket.Principal.FindFirst(http://schemas.microsoft.com/identity/claims/objectidentifier))?.Value;ClientCredential clientCred new ClientCredential(ClientId, ClientSecret);AuthenticationContext authContext new AuthenticationContext(Authority, new SampleSessionCache(userObjectId, context.HttpContext.Session));AuthenticationResult authResult await authContext.AcquireTokenByAuthorizationCodeAsync(context.ProtocolMessage.Code, new Uri(context.Properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]), clientCred, GraphResourceId);}private Task OnAuthenticationFailed(FailureContext context){context.HandleResponse();context.Response.Redirect(/Home/Error?message context.Failure.Message);return Task.FromResult(0);}} }我还专门定义了一个简单的类用来保存 Token 信息。为了简单起见我们将 Token 保存在 Session 里面这样的话用户登陆一次后在一个会话里面就不需要多次登录而是可以直接重用这些 Token。这个类其实我是重用了之前在 ASP.NET MVC 开发中的那个类没有什么特别要交待的请直接打开 SampleSessionCache.cs 这个文件了解即可。接下来为了便于后续在 Controller 里面快速地访问到 Graph我对 GraphServiceClient 进行了封装请参考 SDKHelper.cs 这个文件using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.Graph; using System.Net.Http.Headers; using Microsoft.AspNetCore.Http;namespace aspntecoremvc{public static class SDKHelper{//这里其实是对 ControllerBase 这个类型进行了扩展public static async TaskGraphServiceClient GetAuthenticatedClient(this ControllerBase controller){var Authority String.Format(https://login.microsoftonline.com/{0},office365devlabs.onmicrosoft.com);var ClientId e91ef175-e38d-4feb-b1ed-f243a6a81b93;var ClientSecret 2F5jdoGGNn59oxeDLE9fXx5tD86uvzIji74dmLaj3YI;var GraphResourceId https://graph.microsoft.com;string userObjectId controller.HttpContext.User.FindFirst(http://schemas.microsoft.com/identity/claims/objectidentifier)?.Value;ClientCredential clientCred new ClientCredential(ClientId, ClientSecret);AuthenticationContext authContext new AuthenticationContext(Authority, new SampleSessionCache(userObjectId, controller.HttpContext.Session));AuthenticationResult result await authContext.AcquireTokenSilentAsync(GraphResourceId,ClientId);GraphServiceClient client new GraphServiceClient(new DelegateAuthenticationProvider(async request{request.Headers.Authorization new AuthenticationHeaderValue(bearer, result.AccessToken);await Task.FromResult(0);}));return client;}}}有了上面的准备在真正需要用到 Graph 服务的地方我们的代码是非常简单的请参考 HomeController.cs 文件中的 About 方法[Authorize]//用这个标记该方法需要用户登录 public async TaskIActionResult About() {var client await this.GetAuthenticatedClient();  //获取用户详细信息然后传递给视图return View(await client.Me.Request().GetAsync()); }到这里为止我的这个例子的主要代码就解释完了。你可能会觉得这太简单了吧。如果你这样认为我一方面感到很高兴因为这是我希望呈现出来的效果另一方面我要提醒你的是由于 asp.net core 是一个还比较新的技术这方面的材料相当少其实我还是做了相当多的研究才精炼成这样的其间遇到过多少坑多少曲折迂回不足以外人道也但我很看好 asp.net core并且将持续在此之上进行投资这也几乎是可以肯定的。4结语这个例子实现的功能并没有什么惊天动地的但与咱们之前一系列的范例相呼应的是我是要帮助大家打开 Microsoft Graph 的大门至于你要怎么去利用里面丰富的宝藏我就选择的权利交给你自己。写到这里我的这个系列文章的第一个大的里程碑应该是要实现了。我用了将近四个月的时间写了十几篇跟 Office 365 开发入门以及具体的 Microsoft Graph 开发有关的文章差不多算是比较完整了。我还将继续写后续的内容例如 Office Add-insSharePoint 开发Teams 开发Bot 等有兴趣的朋友们可继续关注。 更新一作为一个不断追求代码复用的程序猿我这两天在上面这个范例基础上对代码进行了一定的封装如果你此时查看代码的话会发现已经有了较大的不同。首先我将所有公用的代码全部提取到了一个单独的项目Office365GraphCoreMVCHelper 中这里面的关键代码有一个用来读取配置文件的类型namespace Office365GraphCoreMVCHelper {public class AppSetting{public Info Office365ApplicationInfo { get; set; }public class Info{public string ClientId { get; set; }public string ClientSecret { get; set; }public string Authority { get; set; }public string GraphResourceId { get; set; }}} }一个可公用的Startup类型using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Caching; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Session; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.AspNetCore.Authentication; using System.Security.Claims; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options;namespace Office365GraphCoreMVCHelper {public class Startup{static IConfigurationRoot Configuration { get; set; }public Startup(IHostingEnvironment env){Configuration new ConfigurationBuilder().SetBasePath(env.ContentRootPath).AddJsonFile(appsettings.json).Build();}// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){//这里将配置信息注入到应用程序中services.AddOptions();services.ConfigureAppSetting(Configuration);services.AddSession();services.AddAuthentication(sharedoptions sharedoptions.SignInScheme CookieAuthenticationDefaults.AuthenticationScheme);// Add framework services.services.AddMvc();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){loggerFactory.AddConsole();loggerFactory.AddDebug();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();}else{app.UseExceptionHandler(/Home/Error);}//ConfigureMiddleware(app,env,loggerFactory);app.UseStaticFiles();app.UseSession();app.UseCookieAuthentication();//获得之前注入的配置信息var options app.ApplicationServices.GetRequiredServiceIOptionsAppSetting();app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions{ClientId options.Value.Office365ApplicationInfo.ClientId,Authority options.Value.Office365ApplicationInfo.Authority,ClientSecret options.Value.Office365ApplicationInfo.ClientSecret,ResponseType OpenIdConnectResponseType.CodeIdToken,CallbackPath /signin-oidc,GetClaimsFromUserInfoEndpoint true,Events new OpenIdConnectEvents{OnAuthorizationCodeReceived async (context) {string userObjectId (context.Ticket.Principal.FindFirst(http://schemas.microsoft.com/identity/claims/objectidentifier))?.Value;ClientCredential clientCred new ClientCredential(options.Value.Office365ApplicationInfo.ClientId, options.Value.Office365ApplicationInfo.ClientSecret);AuthenticationContext authContext new AuthenticationContext(options.Value.Office365ApplicationInfo.Authority, new SampleSessionCache(userObjectId, context.HttpContext.Session));AuthenticationResult authResult await authContext.AcquireTokenByAuthorizationCodeAsync(context.ProtocolMessage.Code, new Uri(context.Properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]), clientCred, options.Value.Office365ApplicationInfo.GraphResourceId);}}});app.UseMvc(routes {routes.MapRoute(name: default,template: {controllerHome}/{actionIndex}/{id?});});}private Task OnAuthenticationFailed(FailureContext context){context.HandleResponse();context.Response.Redirect(/Home/Error?message context.Failure.Message);return Task.FromResult(0);}} }改造过的SDKHelper类型主要增加了从配置文件中读取信息的功能using System; using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.Graph; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.AspNetCore.Builder;namespace Office365GraphCoreMVCHelper {public static class SDKHelper{public static async TaskGraphServiceClient GetAuthenticatedClient(this ControllerBase controller,IOptionsAppSetting options){var Authority options.Value.Office365ApplicationInfo.Authority;var ClientId options.Value.Office365ApplicationInfo.ClientId;var ClientSecret options.Value.Office365ApplicationInfo.ClientSecret;var GraphResourceId options.Value.Office365ApplicationInfo.GraphResourceId;string userObjectId controller.HttpContext.User.FindFirst(http://schemas.microsoft.com/identity/claims/objectidentifier)?.Value;ClientCredential clientCred new ClientCredential(ClientId, ClientSecret);AuthenticationContext authContext new AuthenticationContext(Authority, new SampleSessionCache(userObjectId, controller.HttpContext.Session));AuthenticationResult result await authContext.AcquireTokenSilentAsync(GraphResourceId, ClientId);GraphServiceClient client new GraphServiceClient(new DelegateAuthenticationProvider(async request {request.Headers.Authorization new AuthenticationHeaderValue(bearer, result.AccessToken);await Task.FromResult(0);}));return client;}} }由于有了这个公用的组件那么在aspnetcoremvc这个主程序中我可以极大地简化代码。首先我在项目文件中定义了对公用组件的引用Project SdkMicrosoft.NET.Sdk.WebPropertyGroupTargetFrameworknetcoreapp1.1/TargetFramework/PropertyGroupItemGroupPackageReference IncludeMicrosoft.AspNetCore Version1.1.2 /PackageReference IncludeMicrosoft.AspNetCore.Mvc Version1.1.3 /PackageReference IncludeMicrosoft.AspNetCore.StaticFiles Version1.1.2 /PackageReference IncludeMicrosoft.Extensions.Logging.Debug Version1.1.2 /PackageReference IncludeMicrosoft.VisualStudio.Web.BrowserLink Version1.1.2 /PackageReference IncludeMicrosoft.Graph Version1.4.0/PackageReference IncludeMicrosoft.AspNetCore.Authentication.OpenIdConnect Version1.1.0/PackageReference IncludeMicrosoft.AspNetCore.Authentication.Cookies Version1.1.0/PackageReference IncludeMicrosoft.IdentityModel.Clients.ActiveDirectory Version3.13.9/PackageReference IncludeMicrosoft.AspNetCore.Session Version1.1.0/PackageReference IncludeMicrosoft.Extensions.Caching.Memory Version1.1.2//ItemGroupItemGroup   ProjectReference Include..\Office365GraphCoreMVCHelper\Office365GraphCoreMVCHelper.csproj //ItemGroup  /Project然后我删除了该项目中的Startup类型取而代之的在Program中直接引用公用组件中定义好的那个Startupusing System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Office365GraphCoreMVCHelper;namespace aspntecoremvc {public class Program{public static void Main(string[] args){var host new WebHostBuilder().UseSetting(startupAssembly,Office365GraphCoreMVCHelper).UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).UseIISIntegration().Build();host.Run();}} }当然我们需要定义一个配置文件来保存clientId等信息该文件命名为appsettings.json{Office365ApplicationInfo:{ClientId:e91ef175-e38d-4feb-b1ed-f243a6a81b93,ClientSecret:2F5jdoGGNn59oxeDLE9fXx5tD86uvzIji74dmLaj3YI,Authority:https://login.microsoftonline.com/office365devlabs.onmicrosoft.com,GraphResourceId:https://graph.microsoft.com} }最后在HomeController或者同类需要用到Microsoft Graph的Controller中通过下面的代码来实现调用using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.Graph; using System.Net.Http.Headers; using Office365GraphCoreMVCHelper; using Microsoft.Extensions.Options;namespace aspntecoremvc.Controllers {public class HomeController : Controller{private readonly IOptionsAppSetting Options;public HomeController(IOptionsAppSetting options){this.Options options;}public IActionResult Index(){return View();}[Authorize]public async TaskIActionResult About(){var client await this.GetAuthenticatedClient(this.Options);            return View(await client.Me.Request().GetAsync());}public IActionResult Contact(){ViewData[Message] Your contact page.;return View();}public IActionResult Error(){return View();}} }        更新二我进一步上面分离出来的这个Office365GraphCoreMVCHelper的项目打包成了一个nuget的packagehttps://www.nuget.org/packages/Office365GraphCoreMVCHelper/以便实现更大范围的复用。如何使用它呢很简单请按照下面的步骤即可创建一个ASP.NET Core MVC项目dotnet new mvc增加对于Office365GraphCoreMVCHelper的引用修改csproj文件添加如下的定义下载这个包dotnet restore修改Program.cs文件使用Office365GraphCoreMVCHelper 定义好的Startup类增加下面代码中的UseSetting这一句。当前项目的Startup.cs文件可以删除。using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting;namespace testaspnetcoremvc {public class Program{public static void Main(string[] args){var host new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).UseIISIntegration().UseSetting(startupAssembly,Office365GraphCoreMVCHelper).Build();host.Run();}} }修改appsettings.json文件确保里面有如下Office365ApplicationInfo信息{Office365ApplicationInfo:{ClientId:e91ef175-e38d-4feb-b1ed-f243a6a81b93,ClientSecret:2F5jdoGGNn59oxeDLE9fXx5tD86uvzIji74dmLaj3YI,Authority:https://login.microsoftonline.com/office365devlabs.onmicrosoft.com,GraphResourceId:https://graph.microsoft.com},Logging: {IncludeScopes: false,LogLevel: {Default: Warning}} }修改HomeController在需要进行身份验证以及调用Graph API的地方使用如下的代码即可//添加几个引用 using Office365GraphCoreMVCHelper; using Microsoft.Extensions.Options; using Microsoft.AspNetCore.Authorization;//修改构造函数接受注入的配置信息 private IOptionsAppSetting settings; public HomeController(IOptionsAppSetting options) {settings options; }//在需要调用Graph API的Action中做如下修改 [Authorize] public async TaskIActionResult About() {var client await this.GetAuthenticatedClient(settings);var user client.Me.Request().GetAsync().Result;ViewData[Message] $Hello,{user.DisplayName};return View(); }原文地址https://chenxizhang.gitbooks.io/office365devguide/content/docs/crossplatform.html.NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注
http://www.zqtcl.cn/news/67470/

相关文章:

  • 怎样申请免费网站域名成都市建设质监站网站
  • 龙海市城乡建设局网站互联网编程培训
  • 购物帮做特惠的导购网站seo推广优化外包公司
  • 哪家公司做网站最好建筑公司查询
  • 网站创建需要什么用php做商城网站的设计论文
  • 研究生做网站开发wordpress伪装成破解成功
  • 做php网站用的软件电子商务网站建设实训总结报告
  • php网站开发数据列表排重课件app制作教程
  • 如何做网站收录淘宝客建网站
  • 惠城中山网站建设创建官网需要多少钱
  • 保定知名网站建设公司2016年两学一做教育网站
  • 消防做设计有什么网站中铁建设集团登录
  • 做网站都需要什么人团昆明网站建设搜王道下拉
  • 响应式网站 图片居中包装回收网站建设
  • 做网站需要哪些语言伊犁建设网站
  • 怎么看一个网站是用模板什么做的久久文化传媒有限公司招聘信息
  • 女性开源网站重庆装修设计
  • 网站建设客户常见问题集锦负面信息网站
  • 网站建设优化服务信息淄博网站建设 熊掌号
  • 计算机网站开发国外参考文献企业网站推广模式
  • 广州番禺网站公司如皋住房和城乡建设局网站
  • 不知名网站开发辽宁省最好的男科医院
  • 丽水网站建设seo网站主页图片尺寸
  • 网站内部优化工具沈阳网站托管公司
  • 企业网站的高跳出率应该如何解决手机端网站的区别吗
  • 做vr网站石家庄网站开发哪家好
  • 网站备案都需要什么导视设计师
  • 哪些网站是单页面有个找人做任务赚返佣的网站
  • 沈阳出名网站seo快速排名博客
  • 北京优化网站公司公司内部网站页面设计