作者:陈希章 发表于 2017年5月31日

什么是无人值守程序(服务)

我在此前用了几篇文章分别介绍了在桌面应用程序(控制台),Web应用程序(ASP.NET MVC),以及PowerSehll脚本中如何访问Microsoft Graph,今天这一篇要继续讲一个场景:在无人值守程序中访问Microsoft Graph。那么什么是无人值守程序呢?通常我们将此类程序定义为不需要(不允许)用户进行干预,一般用来在后台自动化运行的程序。在英文文档中,我们将其称之为daemon application,广义上说,也包括了服务这种特殊的应用程序。

无人值守程序与Microsoft Graph的集成,要遵守一般的流程,但也有自己的一些特点,总结起来有如下的步骤

  1. 注册应用程序

  2. 配置应用程序权限
  3. 获得管理员同意
  4. 获得访问令牌
  5. 使用令牌访问资源

关于这个话题,官方有一个英文的文档,请参考 https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service

注册应用程序

针对Azure AD的不同版本,注册应用程序的过程我此前已经有专门的文章介绍了,请参考

  1. 注册Azure AD 2.0应用程序(支持国际版,支持Office 365账号以及个人账号登录,功能可能有所缺失,但这个是以后的方向),文章链接
  2. 注册Azure AD 1.0应用程序(支持国际版和中国版,仅支持Office 365账号登录,功能最全),文章链接1文章链接2

本文先演示注册Azure AD 2.0应用,具体的步骤就不做截图了,唯一要提醒的是

  1. 应用程序类型设置为Web

  2. ReplyUrl可以设置为一个通用的地址,我喜欢设置为 https://developer.microsoft.com/en-us/graph/


配置应用程序权限

这是无人值守应用程序注册的时候,要特别注意的。由于该程序是没有用户参与的,所以它无法使用某个特定用户的身份做什么事情,而是使用一个统一的身份,该身份我们称为Application Identity,而相应的,我们要为程序申请的权限也是所谓的“Application Permissions”,而不是“Delegated Permissions”。

本例中,我想为应用程序申请两个权限:一个是用来获取所有用户信息的,另外一个是用来代替任何用户发送邮件。


获得管理员同意

由于无人值守的程序其实是自动化运行的,无需用户进行参与进行授权,而它进行的操作,却又有可能要代表用户的行为。所以通常这些权限都需要得到真正的Office 365 Tenant管理员同意才能真正生效。

其实细心的朋友在上图中也应该可以看出来,几乎所有Application Permission都是需要管理员同意的(Admin Consent)

要获得管理员同意,你可以将下面的这个链接发送给用户的Office 365 Tenant管理员

https://login.microsoftonline.com/common/adminconsent?client_id=dff48006-b010-4859-b5d5-68acdb821322&state=12345&redirect_uri=https://developer.microsoft.com/en-us/graph/

管理员需要在下面这样的界面中对应用程序所申请的权限进行确认

正常情况下,完成授权后页面会被导航到下面的地址,请确认admin_consent的值为true,并记录下来tenant的值。这个表示用户的Office 365 Tenant的编号,后面我们需要用到。

https://developer.microsoft.com/en-us/graph/?admin_consent=True&tenant=59723f6b-2d14-49fe-827a-8d04f9fe7a68&state=12345

获取访问令牌

无人值守应用程序,不需要用户参与进行授权,所以它获取令牌的方式也略有不同。你可以在应用程序里面使用下面的方式发起一个POST请求来获得访问令牌(Access Token)。

POST https://login.windows.net/59723f6b-2d14-49fe-827a-8d04f9fe7a68/oauth2/token
Content-Type: application/x-www-form-urlencoded
Host: login.windows.net client_id=338c8e70-d0da-444e-b877-9f427a16eb17&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret=8V59e4aBfNr6x4lN8EAMTisk3J7WRH+glZbvgMwdDQY=&grant_type=client_credentials

正常情况下,这个请求将返回如下的结果

请复制得到的这个access_token的值。请注意,默认情况下,这个access_token会在1个小时后过期。至于怎么刷新token,我会在后续文章中介绍。

使用令牌访问资源

有了这个access_token,应用程序就可以尽情地访问Microsoft Graph的资源了。例如,通过下面的请求可以获取到对应的Office 365 Tenant中的所有用户信息。


使用一个控制台程序来实现代码逻辑

上面演示的时候,我用了Fiddler这个小工具来模拟发起请求,并且快速地查看到结果。下面用一个简单的应用程序,来实现代码逻辑,给大家参考。

这个程序使用了最简单的代码实现,并添加了Newtonsoft.Json这个Package

using Newtonsoft.Json.Linq;
using System;
using System.Net.Http; namespace daemonapplication
{
class Program
{
static void Main(string[] args)
{
//准备环境
var clientId = "dff48006-b010-4859-b5d5-68acdb821322";
var client_secret = "uxO3frQOekCfdOfX2Oom4Vc";
var tenantId = "59723f6b-2d14-49fe-827a-8d04f9fe7a68"; var client = new HttpClient(); //获得令牌
var request = new HttpRequestMessage(HttpMethod.Post, $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token");
var body = new StringContent($"grant_type=client_credentials&client_id={clientId}&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret={client_secret}");
body.Headers.ContentType.MediaType = "application/x-www-form-urlencoded";
request.Content = body;
var access_token = JObject.Parse(client.SendAsync(request).Result.Content.ReadAsStringAsync().Result)["access_token"].ToString(); //访问资源
request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/users");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", access_token); var users = JObject.Parse(client.SendAsync(request).Result.Content.ReadAsStringAsync().Result)["value"]; foreach (var item in users)
{
Console.WriteLine($"displayName:{item["displayName"]},email:{item["email"]}");
} Console.Read(); }
}
}

使用Azure AD 1.0

上面的例子很简单易懂,但如果我们使用的是Azure AD 1.0(国际版同时支持1.0和2.0,中国版则只支持1.0),则注册应用程序和使用Microsoft Graph的方式会略有不同。

注册Azure 1.0 应用程序(中国版)

请参考之前的两篇文章了解如何在Azure 1.0的环境中注册应用程序

  1. 国际版 链接
  2. 中国版 链接

和上面提到的一样,有两点需要注意

  1. 应用程序类型设置为Web

  2. ReplyUrl可以设置为一个通用的地址,我喜欢设置为 https://developer.microsoft.com/en-us/graph/


配置Azure 1.0 应用程序权限(中国版)

和上面提到的一样,这里需要申请Application permission,而不是delegation permission

这里需要注意修改Manifest文件(先下载,然后编辑,最后上传),允许隐式授权


获得管理员同意 (中国版)

和Azure AD 2.0明显不同的是,在1.0中,获取管理员同意,需要使用如下的链接

https://login.chinacloudapi.cn/12c0cdab-3c40-4e86-80b9-3e6f98d2d344/oauth2/authorize?prompt=admin_consent&response_type=token&redirect_uri=https://developer.microsoft.com/en-us/graph/&resource=https://microsoftgraph.chinacloudapi.cn&client_id=3f56a5d5-7882-4290-9fd8-3908d734b3fe

此处的关键在于有一个prompt=admin_consent的参数,正常情况下,管理员进行授权确认后会跳转到下面这样的地址,里面已经包含了一个access_token

https://developer.microsoft.com/en-us/graph/#access_token=eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFDckhLdnJ4N0cyU2FaYlpoLXREbnA3Z1BvSDFZc2w5MWlxU0x4Qmdqc1ZXODhmMDR5Vm11Tm1pZGlWZGFJclY5MEhLTl9aUXlXMENERlowcWdwRnBfOWw4Wkhpb21LdkNSM19LQURMdWZ3R1NBQSIsImFsZyI6IlJTMjU2IiwieDV0IjoiWTFjenBtLXhpY2FRVFZYQzlPU2JXN3pHeHRRIiwia2lkIjoiWTFjenBtLXhpY2FRVFZYQzlPU2JXN3pHeHRRIn0.eyJhdWQiOiJodHRwczovL21pY3Jvc29mdGdyYXBoLmNoaW5hY2xvdWRhcGkuY24iLCJpc3MiOiJodHRwczovL3N0cy5jaGluYWNsb3VkYXBpLmNuLzEyYzBjZGFiLTNjNDAtNGU4Ni04MGI5LTNlNmY5OGQyZDM0NC8iLCJpYXQiOjE0OTYyNDI0MjQsIm5iZiI6MTQ5NjI0MjQyNCwiZXhwIjoxNDk2MjQ2MzI0LCJhY3IiOiIxIiwiYWlvIjoiQVNRQTIvOEFBQUFBd2E2S3Y1YmVVRGtSVTliY2pBTzBKbFVJb0xaYzk3bEtkYW5hbzRzMFJTVT0iLCJhbXIiOlsicHdkIl0sImFwcF9kaXNwbGF5bmFtZSI6ImRlYW1vbiIsImFwcGlkIjoiM2Y1NmE1ZDUtNzg4Mi00MjkwLTlmZDgtMzkwOGQ3MzRiM2ZlIiwiYXBwaWRhY3IiOiIwIiwiZmFtaWx5X25hbWUiOiLpmYgiLCJnaXZlbl9uYW1lIjoi5biM56ugIiwiaXBhZGRyIjoiMTgwLjE1Mi4yMi41MiIsIm5hbWUiOiLpmYgg5biM56ugIiwib2lkIjoiZjU1MjRmMTAtYTNlYy00Njg3LTllMzktNWFkNmU1ZTY3MDVhIiwicGxhdGYiOiIzIiwicHVpZCI6IjIwMDM3RkZFODExNjMyMDUiLCJzY3AiOiJNYWlsLlNlbmQgVXNlci5SZWFkLkFsbCIsInN1YiI6InhpckVWTFBtVG1BRFpJTW1sZTdBajZwS0NQU2JHMlNGU3EzN3JQaV9rWkUiLCJ0aWQiOiIxMmMwY2RhYi0zYzQwLTRlODYtODBiOS0zZTZmOThkMmQzNDQiLCJ1bmlxdWVfbmFtZSI6ImFyZXNAbW9kdHNwLnBhcnRuZXIub25tc2NoaW5hLmNuIiwidXBuIjoiYXJlc0Btb2R0c3AucGFydG5lci5vbm1zY2hpbmEuY24iLCJ1dGkiOiJRSEswVy0xdXcwbTlxWW9TekNvRUFBIiwidmVyIjoiMS4wIn0.EZhZhKFXzS1hVkz5HNEFSG9lcL6CSRyjqRNEMTYpM0Q4wp7UICf1_61PQFCe_5opnZlEMl-e7sHJ2W4Ni1hqjASUxOamFoQ5pBVNQ-WgEfhX_QPJXLBbyMdFguRPdrXy1AqzYGqFQ_mtmjqFa0w7nXf4LI7vgx7MRPMm5YDljnK4vk4oXC9M7fb4EcU7g26XrBUnTz6Es_IGT9SUqAXYLDjfI3dC06GqtjRrTwtd0AYwbbUPZ288j4XZ_fb8x1lj97ZpIFZh-STnIZUatIij0dFphMrFhUig0YbMtCxlfsrpZgPyuwlrrXbnj5fgWw1ABj3xKrEaWbVt5XCT4T9-aA&token_type=Bearer&expires_in=3599&session_state=022f05fb-4b3f-4f86-b593-cbda90232a7a&admin_consent=True

获取访问令牌(中国版)

这一步可以跳过,因为上面这一步已经获得了access_token

使用令牌访问资源(中国版)
using System;
using System.Net.Http;
using System.Threading.Tasks; namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
var result = GetUsers().Result;
Console.WriteLine(result); Console.Read(); } static async Task<string> GetUsers()
{
var token = "eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFDckhLdnJ4N0cyU2FaYlpoLXREbnA3Z1BvSDFZc2w5MWlxU0x4Qmdqc1ZXODhmMDR5Vm11Tm1pZGlWZGFJclY5MEhLTl9aUXlXMENERlowcWdwRnBfOWw4Wkhpb21LdkNSM19LQURMdWZ3R1NBQSIsImFsZyI6IlJTMjU2IiwieDV0IjoiWTFjenBtLXhpY2FRVFZYQzlPU2JXN3pHeHRRIiwia2lkIjoiWTFjenBtLXhpY2FRVFZYQzlPU2JXN3pHeHRRIn0.eyJhdWQiOiJodHRwczovL21pY3Jvc29mdGdyYXBoLmNoaW5hY2xvdWRhcGkuY24iLCJpc3MiOiJodHRwczovL3N0cy5jaGluYWNsb3VkYXBpLmNuLzEyYzBjZGFiLTNjNDAtNGU4Ni04MGI5LTNlNmY5OGQyZDM0NC8iLCJpYXQiOjE0OTYyNDI0MjQsIm5iZiI6MTQ5NjI0MjQyNCwiZXhwIjoxNDk2MjQ2MzI0LCJhY3IiOiIxIiwiYWlvIjoiQVNRQTIvOEFBQUFBd2E2S3Y1YmVVRGtSVTliY2pBTzBKbFVJb0xaYzk3bEtkYW5hbzRzMFJTVT0iLCJhbXIiOlsicHdkIl0sImFwcF9kaXNwbGF5bmFtZSI6ImRlYW1vbiIsImFwcGlkIjoiM2Y1NmE1ZDUtNzg4Mi00MjkwLTlmZDgtMzkwOGQ3MzRiM2ZlIiwiYXBwaWRhY3IiOiIwIiwiZmFtaWx5X25hbWUiOiLpmYgiLCJnaXZlbl9uYW1lIjoi5biM56ugIiwiaXBhZGRyIjoiMTgwLjE1Mi4yMi41MiIsIm5hbWUiOiLpmYgg5biM56ugIiwib2lkIjoiZjU1MjRmMTAtYTNlYy00Njg3LTllMzktNWFkNmU1ZTY3MDVhIiwicGxhdGYiOiIzIiwicHVpZCI6IjIwMDM3RkZFODExNjMyMDUiLCJzY3AiOiJNYWlsLlNlbmQgVXNlci5SZWFkLkFsbCIsInN1YiI6InhpckVWTFBtVG1BRFpJTW1sZTdBajZwS0NQU2JHMlNGU3EzN3JQaV9rWkUiLCJ0aWQiOiIxMmMwY2RhYi0zYzQwLTRlODYtODBiOS0zZTZmOThkMmQzNDQiLCJ1bmlxdWVfbmFtZSI6ImFyZXNAbW9kdHNwLnBhcnRuZXIub25tc2NoaW5hLmNuIiwidXBuIjoiYXJlc0Btb2R0c3AucGFydG5lci5vbm1zY2hpbmEuY24iLCJ1dGkiOiJRSEswVy0xdXcwbTlxWW9TekNvRUFBIiwidmVyIjoiMS4wIn0.EZhZhKFXzS1hVkz5HNEFSG9lcL6CSRyjqRNEMTYpM0Q4wp7UICf1_61PQFCe_5opnZlEMl-e7sHJ2W4Ni1hqjASUxOamFoQ5pBVNQ-WgEfhX_QPJXLBbyMdFguRPdrXy1AqzYGqFQ_mtmjqFa0w7nXf4LI7vgx7MRPMm5YDljnK4vk4oXC9M7fb4EcU7g26XrBUnTz6Es_IGT9SUqAXYLDjfI3dC06GqtjRrTwtd0AYwbbUPZ288j4XZ_fb8x1lj97ZpIFZh-STnIZUatIij0dFphMrFhUig0YbMtCxlfsrpZgPyuwlrrXbnj5fgWw1ABj3xKrEaWbVt5XCT4T9-aA"; HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
HttpResponseMessage response = await client.GetAsync("https://microsoftgraph.chinacloudapi.cn/v1.0/users/");
string retResp = await response.Content.ReadAsStringAsync(); return retResp;
}
}
}

如果我们将代码再演化一下,使用Microsoft.Graph进行访问的话,会更加轻松惬意,因为可以完全基于强类型的方式进行操作

using Microsoft.Graph;
using System;
using System.Threading.Tasks;
using System.Linq; namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
var result = GetUsers().Result; foreach (var item in result)
{
Console.WriteLine(item.DisplayName);
} Console.Read(); } static async Task<IGraphServiceUsersCollectionPage> GetUsers()
{
var token = "eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFDckhLdnJ4N0cyU2FaYlpoLXREbnA3Z1BvSDFZc2w5MWlxU0x4Qmdqc1ZXODhmMDR5Vm11Tm1pZGlWZGFJclY5MEhLTl9aUXlXMENERlowcWdwRnBfOWw4Wkhpb21LdkNSM19LQURMdWZ3R1NBQSIsImFsZyI6IlJTMjU2IiwieDV0IjoiWTFjenBtLXhpY2FRVFZYQzlPU2JXN3pHeHRRIiwia2lkIjoiWTFjenBtLXhpY2FRVFZYQzlPU2JXN3pHeHRRIn0.eyJhdWQiOiJodHRwczovL21pY3Jvc29mdGdyYXBoLmNoaW5hY2xvdWRhcGkuY24iLCJpc3MiOiJodHRwczovL3N0cy5jaGluYWNsb3VkYXBpLmNuLzEyYzBjZGFiLTNjNDAtNGU4Ni04MGI5LTNlNmY5OGQyZDM0NC8iLCJpYXQiOjE0OTYyNDI0MjQsIm5iZiI6MTQ5NjI0MjQyNCwiZXhwIjoxNDk2MjQ2MzI0LCJhY3IiOiIxIiwiYWlvIjoiQVNRQTIvOEFBQUFBd2E2S3Y1YmVVRGtSVTliY2pBTzBKbFVJb0xaYzk3bEtkYW5hbzRzMFJTVT0iLCJhbXIiOlsicHdkIl0sImFwcF9kaXNwbGF5bmFtZSI6ImRlYW1vbiIsImFwcGlkIjoiM2Y1NmE1ZDUtNzg4Mi00MjkwLTlmZDgtMzkwOGQ3MzRiM2ZlIiwiYXBwaWRhY3IiOiIwIiwiZmFtaWx5X25hbWUiOiLpmYgiLCJnaXZlbl9uYW1lIjoi5biM56ugIiwiaXBhZGRyIjoiMTgwLjE1Mi4yMi41MiIsIm5hbWUiOiLpmYgg5biM56ugIiwib2lkIjoiZjU1MjRmMTAtYTNlYy00Njg3LTllMzktNWFkNmU1ZTY3MDVhIiwicGxhdGYiOiIzIiwicHVpZCI6IjIwMDM3RkZFODExNjMyMDUiLCJzY3AiOiJNYWlsLlNlbmQgVXNlci5SZWFkLkFsbCIsInN1YiI6InhpckVWTFBtVG1BRFpJTW1sZTdBajZwS0NQU2JHMlNGU3EzN3JQaV9rWkUiLCJ0aWQiOiIxMmMwY2RhYi0zYzQwLTRlODYtODBiOS0zZTZmOThkMmQzNDQiLCJ1bmlxdWVfbmFtZSI6ImFyZXNAbW9kdHNwLnBhcnRuZXIub25tc2NoaW5hLmNuIiwidXBuIjoiYXJlc0Btb2R0c3AucGFydG5lci5vbm1zY2hpbmEuY24iLCJ1dGkiOiJRSEswVy0xdXcwbTlxWW9TekNvRUFBIiwidmVyIjoiMS4wIn0.EZhZhKFXzS1hVkz5HNEFSG9lcL6CSRyjqRNEMTYpM0Q4wp7UICf1_61PQFCe_5opnZlEMl-e7sHJ2W4Ni1hqjASUxOamFoQ5pBVNQ-WgEfhX_QPJXLBbyMdFguRPdrXy1AqzYGqFQ_mtmjqFa0w7nXf4LI7vgx7MRPMm5YDljnK4vk4oXC9M7fb4EcU7g26XrBUnTz6Es_IGT9SUqAXYLDjfI3dC06GqtjRrTwtd0AYwbbUPZ288j4XZ_fb8x1lj97ZpIFZh-STnIZUatIij0dFphMrFhUig0YbMtCxlfsrpZgPyuwlrrXbnj5fgWw1ABj3xKrEaWbVt5XCT4T9-aA"; GraphServiceClient client = new GraphServiceClient(
new DelegateAuthenticationProvider(async (request) =>
{
await Task.Run(() => { request.Headers.Add("Authorization", $"Bearer {token}"); });
})); client.BaseUrl = "https://microsoftgraph.chinacloudapi.cn/v1.0"; var result = client.Users.Request().GetAsync().Result; return result;
}
}
}

在无人值守程序(服务)中调用Microsoft Graph的更多相关文章

  1. 在PowerShell脚本中集成Microsoft Graph

    作者:陈希章 发表于2017年4月23日 我旗帜鲜明地表态,我很喜欢PowerShell,相比较于此前的Cmd Shell,它有一些重大的创新,例如基于.NET的类型系统,以及管道.模块的概念等等.那 ...

  2. 使用GraphHttpClient调用Microsoft Graph接口 - POST

    博客地址:http://blog.csdn.net/FoxDave 本篇接上一讲,我们继续看如何通过GraphHttpClient创建一个Office 365的组,需要使用POST请求. 为结果添加按 ...

  3. 使用GraphHttpClient调用Microsoft Graph接口 - GET

    博客地址:http://blog.csdn.net/FoxDave 使用GraphHttpClient类调用Microsoft Graph REST API,你可以使用GET,POST和PATCH请求 ...

  4. 使用GraphHttpClient调用Microsoft Graph接口 - PATCH

    博客地址:http://blog.csdn.net/FoxDave 通过前两讲的阐述我们应该大致了解了使用GraphHttpClient调用Microsoft Graph接口的模式,并介绍了使用get ...

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

    作者:陈希章 发表于 2017年6月25日 谈一谈.NET 的跨平台 终于要写到这一篇了.跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标.我在前面的文章已经提到过了,Micro ...

  6. 在Delphi开发的服务中调用指定应用程序

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://fxh7622.blog.51cto.com/63841/529033 在很多时候 ...

  7. 【Azure Developer】Python 获取Micrisoft Graph API资源的Access Token, 并调用Microsoft Graph API servicePrincipals接口获取应用ID

    问题描述 在Azure开发中,我们时常面临获取Authorization问题,需要使用代码获取到Access Token后,在调用对应的API,如servicePrincipals接口. 如果是直接调 ...

  8. 解决在IIS中调用Microsoft Office Excel组件后进程无法正常退出的问题

    来源:http://www.cnblogs.com/ahui/archive/2013/03/05/2944441.html 有一个项目用到Excel组件产生报表,本以为这个通用功能是个很简单的cas ...

  9. 微信小程序——页面中调用组件方法

    我现在有一个弹层的组件(popup),组件里面定义了显示组件(showPopup)和隐藏组件(hidePopup)的方法. 我们如何在调用组件的页面中调用组件里面的方法呢? 在调用组件的页面写如下代码 ...

随机推荐

  1. hadoop的webUI查看Live Nodes为1

    开起了两个节点,而且jps查看确实开启了,但是用web端50070查看却一直显示为1 经过排查,将虚拟机直接copy一份,但是之前配置好hadoop环境的namenode格式化(format)生成的文 ...

  2. 一:详解 HTTP 协议

    本篇文章篇幅比较长,先来个思维导图预览一下. 一张图带你看完本篇文章 一.概述 1.计算机网络体系结构分层 计算机网络体系结构分层 2.TCP/IP 通信传输流 利用 TCP/IP 协议族进行网络通信 ...

  3. C#中float, double的精度问题

    在工作中我发现了一个C#浮点数的精度问题,以下的程序运行结果并未得到我预期的结果: view source print? 01 namespace FloatTest 02 03     class ...

  4. python 中 urlparse 模块介绍

    urlparse模块主要是用于解析url中的参数  对url按照一定格式进行 拆分或拼接 1.urlparse.urlparse 将url分为6个部分,返回一个包含6个字符串项目的元组:协议.位置.路 ...

  5. flex布局元素操作详情

    之前布局一直用的是 position,float之类的,趁着国庆学习一下 flex 布局 父元素: flex-direction: row row-reverse column column-reve ...

  6. c++用指针交换数组

    对于指针一直很迷,今天看了一下指针交换数组,知识量很少,希望能帮助到大家. 利用指针来交换数组主要是为了节省时间嘛,有两种交换方式 第一种是写一个函数把数组传过去然后用swap交换,即可 代码如下: ...

  7. javascript常用的Math对象的方法

    简介 Math对象是在程序编程中用于执行一些数学任务的.Math 对象并不像 Date 和 String 那样是对象的类,因此没有构造函数 Math(),像 Math.sin() 这样的函数只是函数, ...

  8. scala时间处理

    1.获取当前时间的年份.月份.天.小时等等 val nowDay=LocalDate.now().getDayOfMonth val nowDay=LocalTime.now().getHour 2. ...

  9. 51Nod 1352 集合计数 扩展欧几里得

    基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 给出N个固定集合{1,N},{2,N-1},{3,N-2},...,{N-1,2},{N,1}.求出有多少个集合满足 ...

  10. 51Nod 1256 乘法逆元 扩展欧几里得

    基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 给出2个数M和N(M < N),且M与N互质,找出一个数K满足0 < K < N且K * M % N = ...