跨平台应用集成(在ASP.NET Core MVC 应用程序中集成 Microsoft Graph)


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 的使用。





2
ASP.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 sdk(https://www.microsoft.com/net/download/core ),然后在本地命令行工具中运行 dotnet new mvc 即可


首先,我为项目添加了几个外部组件包,这是通过修改项目定义文件(aspnetcoremvc.csproj)来实现的。


<PackageReference Include="Microsoft.Graph" Version="1.4.0"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="1.1.0"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.0"/>
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.13.9"/>
<PackageReference Include="Microsoft.AspNetCore.Session" Version="1.1.0"/>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.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 ClientId="e91ef175-e38d-4feb-b1ed-f243a6a81b93";private readonly string Authority=String.Format("https://login.microsoftonline.com/{0}","office365devlabs.onmicrosoft.com");private readonly string ClientSecret="2F5jdoGGNn59oxeDLE9fXx5tD86uvzIji74dmLaj3YI=";private readonly string GraphResourceId="https://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: "{controller=Home}/{action=Index}/{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 Task<GraphServiceClient> 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 Task<IActionResult> 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-ins,SharePoint 开发,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.Configure<AppSetting>(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.GetRequiredService<IOptions<AppSetting>>();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: "{controller=Home}/{action=Index}/{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 Task<GraphServiceClient> GetAuthenticatedClient(this ControllerBase controller,IOptions<AppSetting> 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 Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>netcoreapp1.1</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" /><PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" /><PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.2" /><PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" /><PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.2" /><PackageReference Include="Microsoft.Graph" Version="1.4.0"/><PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="1.1.0"/><PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.0"/><PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.13.9"/><PackageReference Include="Microsoft.AspNetCore.Session" Version="1.1.0"/><PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2"/></ItemGroup><ItemGroup>   <ProjectReference Include="..\Office365GraphCoreMVCHelper\Office365GraphCoreMVCHelper.csproj" /></ItemGroup>  </Project>


然后,我删除了该项目中的Startup类型,取而代之的在Program中直接引用公用组件中定义好的那个Startup


using 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 IOptions<AppSetting> Options;public HomeController(IOptions<AppSetting> options){this.Options = options;}public IActionResult Index(){return View();}[Authorize]public async Task<IActionResult> 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/,以便实现更大范围的复用。



如何使用它呢?很简单,请按照下面的步骤即可


  1. 创建一个ASP.NET Core MVC项目

    dotnet new mvc


  2. 增加对于Office365GraphCoreMVCHelper的引用,修改csproj文件,添加如下的定义

  3. 下载这个包

    dotnet restore


  4. 修改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 IOptions<AppSetting> settings;
public HomeController(IOptions<AppSetting> options)
{settings = options;
}//在需要调用Graph API的Action中做如下修改
[Authorize]
public async Task<IActionResult> 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.mzph.cn/news/323454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用指针完成函数参数的调用

#include<stdio.h>addUp(int a,int b,int c){ cab;printf("%d\n",c); return c;}main(){int c;addUp(6,5,c);printf("%d",c); }可以在函数中完成数字的加减但是发现无法再主函数中调用&#xff0c;是个初始值22&#xff0c;所有得想个办法将函数的值…

Java 8中Stream API的这些奇技淫巧!你都Get到了吗?

转载自 Java 8中Stream API的这些奇技淫巧&#xff01;你都Get到了吗&#xff1f; Stream简介 Java 8引入了全新的Stream API。这里的Stream和I/O流不同&#xff0c;它更像具有Iterable的集合类&#xff0c;但行为和集合类又有所不同。stream是对集合对象功能的增强&#x…

基于Office 365的随需应变业务应用平台

这是我去年10月底在微软技术大会&#xff08;Microsoft Ignite 2016) 上面的演讲主题&#xff0c;承蒙大家抬爱&#xff0c;也沾了前一场明星讲师的光&#xff0c;我记得会场几乎是满座。观众中既有IT部门的技术人员&#xff0c;也有业务部门的用户&#xff0c;也有少量的开发人…

jeecg中新建接口后报错404的解决方法

大家好&#xff0c;我是雄雄。 前言 最近项目中&#xff0c;一直用的是jeecg-boot的微服务版本&#xff0c;尤其是第一次用微服务&#xff0c;所以在用的过程中&#xff0c;各种问题层出不穷&#xff0c;我们就在边学习边应用的道路上缓缓前进。 索性现在项目已经做的差不多了…

一分钟理解Java公平锁与非公平锁

转载自 一分钟理解Java公平锁与非公平锁 和朋友聊天他提到&#xff1a;ReentrantLock 的构造函数可以传递一个 bool 数据&#xff0c;true 时构造的是“公平锁”、false 时构造的是“非公平锁”。我的印象中锁是不区分类型的&#xff0c;所以认为这应该是 Java 发明的概念&…

为什么你需要将代码迁移到ASP.NET Core 2.0?

随着 .NET Core 2.0 的发布&#xff0c;.NET 开源跨平台迎来了新的时代。开发者们可以选择使用命令行、个人喜好的文本编辑器、Visual Studio 2017 15.3 和 Visual Studio Code 来开发自己的 .NET Core 2.0 项目。同时&#xff0c;微软 .NET 开发工具组也宣布了 ASP.NET Core 2…

typedef用法

#include<stdio.h> typedef struct Student {int age;int score;}St; int main(){St st{200,100};//等价于 struct Student st 直接命名&#xff0c;省略/很多不必要步骤 printf("%d",st.age);}typedef的核心在于指针而不是仅仅简化了结构 #include<stdi…

jeecg-boot中如何放开接口路由

大家好&#xff0c;我是雄雄。 前言 今天是国庆假期的第5天了&#xff0c;7天的时间&#xff0c;感觉过的挺快&#xff0c;前三天都在下雨&#xff0c;后四天降温冻的瑟瑟发抖。 这次假期完事儿了之后就到元旦&#xff0c;春节了&#xff0c;得好好的珍惜。 今天介绍一下&…

nssl1174-阶乘【!基础!数论】

前言 比赛时xjq说这道题很水&#xff0c;是个基础数论。 然后… 就连交都没交 正题 给出n个数&#xff0c;求一个最小的mmm使得 m!∏i1naiq(q∈N)\frac{m!}{\prod_{i1}^na_i}q(q\in N_)∏i1n​ai​m!​q(q∈N​) 解题思路 我们考虑因为要求在一起的乘积&#xff0c;所以个体…

带着小C看动物

大家好&#xff0c;我是雄雄。 整个国庆假期期间&#xff0c;一出门就背着我的小C书包出去。 第一天带着我对象的妈妈去医院检查了下&#xff0c;然后去原来酒店老板那喝羊汤。第二天准备去我对象家的东西&#xff0c;去超市买了一后备箱&#xff0c;晚上就去她家了趟&#xf…

弹性和瞬态故障处理库Polly介绍

前言本节我们来介绍一款强大的库Polly&#xff0c;Polly是一种.NET弹性和瞬态故障处理库&#xff0c;允许我们以非常顺畅和线程安全的方式来执诸如行重试&#xff0c;断路&#xff0c;超时&#xff0c;故障恢复等策略。 Polly针对对.NET 4.0&#xff0c;.NET 4.5和.NET Standar…

一分钟理解Java包装类型

转载自 一分钟理解Java包装类型 Java 一直标榜自己是一个纯粹的面向对象语言&#xff0c;自作聪明的为所有的值类型都提供相应的引用类型&#xff08;不明白这两个概念&#xff0c;看之前的《一分钟理解传值和传引用》&#xff09;比如&#xff1a;int 类型对应的有 Integer&…

SQL Server 2017 正式发布

SQL Server 2017 跨出了重要的一步&#xff0c;它力求通过将 SQL Server 的强大功能引入 Linux、基于 Linux 的 Docker 容器和 Windows&#xff0c;使用户可以在 SQL Server 平台上选择开发语言、数据类型、本地开发或云端开发&#xff0c;以及操作系统开发。微软拥抱开源的脚步…

关于链表的简单创建和遍历

这是网上的的源码&#xff1a; #include <stdio.h> #include <stdlib.h> #include <malloc.h>// 定义链表中的节点 typedef struct node {int member; // 节点中的成员struct node *pNext; // 指向下一个节点的指针 }Node,*…

可能是最全面的 Java G1学习笔记

转载自 可能是最全面的 Java G1学习笔记 引子 最近遇到很多朋友过来咨询G1调优的问题&#xff0c;我自己去年有专门学过一次G1&#xff0c;但是当时只是看了个皮毛&#xff0c;因此自己也有不少问题。总体来讲&#xff0c;对于G1我有几个疑惑&#xff0c;希望能够在这篇文章中…

微服务~Eureka实现的服务注册与发现及服务之间的调用

微服务里一个重要的概念就是服务注册与发现技术&#xff0c;当你有一个新的服务运行后&#xff0c;我们的服务中心可以感知你&#xff0c;然后把加添加到服务列表里&#xff0c;然后当你死掉后&#xff0c;会从服务中心把你移除&#xff0c;而你作为一个服务&#xff0c;对其它…

新高考增值评价系统业务简单介绍(超详细,图文并茂)

大家好&#xff0c;我是雄雄。 文章目录一&#xff1a;基本信息1&#xff1a;学校信息2&#xff1a;教师管理1&#xff1a;下载教师模板2&#xff1a;上传教师模板3&#xff1a;查看教师数据4&#xff1a;教师信息编辑5&#xff1a;新增教师6&#xff1a;导出教师数据3&#xf…

一步一步详解高斯日记

这是有一年的蓝桥杯的题目&#xff0c;感觉挺有趣的 意思就是高斯有个习惯&#xff0c;记日期的时候喜欢只用数字来记&#xff0c;比如如果你2001年一月一日出生的话&#xff0c;你活到2002年1 月一日&#xff0c;他就会写个366。大致意思就是你现在的时间减去个出生的时间1就是…

微服务配置中心实战:Spring + MyBatis + Druid + Nacos

转载自 微服务配置中心实战&#xff1a;Spring MyBatis Druid Nacos 很多基于 Spring MVC 框架的 Web 开发中&#xff0c;Spring MyBatis Druid 是一个黄金组合&#xff0c;在此基础上如果融入一个配置中心&#xff0c;会发生什么特别的变化呢&#xff1f; 本文将通过一…

揭晓新版《Windows Sysinternals实战指南》读书积赞活动

参与新版《Windows Sysinternals实战指南》&#xff0c;读书积赞活动的一下三位同学&#xff08;想法构成&#xff0c;我zzz&#xff0c;kergee&#xff09;&#xff0c;请加我微信geffzhang&#xff0c;把姓名&#xff0c;地址和手机号发给我&#xff0c;后续给你们寄书。.NET…