命名空间:microsoft.graph
跟踪随时间推移在 driveItem 及其子项中的更改。
应用程序首先调用不带任何参数的 delta。
服务开始枚举驱动器的层次结构,返回项目页面和 @odata.nextLink 或 @odata.deltaLink,如下所述。
应用程序应该使用 @odata.nextLink 继续调用,直到不再返回 @odata.nextLink,或响应内容为空更改集。
接收完所有更改后,可将这些更改应用于本地状态。
若要在将来检查更改,请通过上一响应中的 delta 再次调用 @odata.deltaLink。
返回具有 deleted 方面 的已删除邮件。
应从本地状态中删除设置此属性的项目。
注意:如果在同步所有更改后文件夹为空,则仅应在本地删除此文件夹。
权限
为此 API 选择标记为最低特权的权限。
只有在应用需要它时,才使用更高的特权权限。 有关委派权限和应用程序权限的详细信息,请参阅权限类型。 要了解有关这些权限的详细信息,请参阅 权限参考。
| 权限类型 |
最低特权权限 |
更高特权权限 |
| 委派(工作或学校帐户) |
Files.Read |
Files.Read.All、Files.ReadWrite、Files.ReadWrite.All、Sites.Read.All、Sites.ReadWrite.All |
| 委派(个人 Microsoft 帐户) |
Files.Read |
Files.Read.All、Files.ReadWrite、Files.ReadWrite.All |
| 应用程序 |
Files.Read.All |
Files.ReadWrite.All、Sites.Read.All、Sites.ReadWrite.All |
HTTP 请求
GET /drives/{drive-id}/root/delta
GET /groups/{groupId}/drive/root/delta
GET /me/drive/root/delta
GET /sites/{siteId}/drive/root/delta
GET /users/{userId | userPrincipalName}/drive/root/delta
函数参数
| 参数 |
类型 |
说明 |
| 令牌 |
字符串 |
可选。 如果未指定,则枚举层次结构的当前状态。 如果为 latest,则返回具有最新增量令牌的空响应。 如果为之前的增量令牌,则返回自该令牌起的新状态。 |
可选查询参数
此方法支持 $select、 $expand和 $topOData 查询参数 来自定义响应。
| 名称 |
说明 |
| Authorization |
持有者 {token}。 必填。 详细了解 身份验证和授权。 |
| deltaExcludeParent |
字符串。 如果包含此请求标头,则响应将包括已更改的项,而不是层次结构中的父项。 |
请求正文
请勿提供此方法的请求正文。
响应
如果成功,此方法在响应正文中返回 200 OK 响应代码和 DriveItem 资源集合。
除了 DriveItems 集合,此响应还包括以下属性之一:
| 名称 |
值 |
说明 |
|
@odata.nextLink |
url |
如果当前集中存在更多更改,用于检索下一个可用更改页的 URL。 |
|
@odata.deltaLink |
url |
返回当前所有更改后返回的 URL,而不是 @odata.nextLink。 用于在将来读取下一组更改。 |
示例
示例 1:初始请求
下面是如何调用此 API 以建立本地状态的示例。
请求
下面是初始请求的示例。
GET https://graph.microsoft.com/v1.0/me/drive/root/delta
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Drives["{drive-id}"].Items["{driveItem-id}"].Delta.GetAsDeltaGetResponseAsync();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest major version. Current major version is $v1.*
// Dependencies
import (
"context"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
//other-imports
)
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=go
delta, err := graphClient.Drives().ByDriveId("drive-id").Items().ByDriveItemId("driveItem-id").Delta().GetAsDeltaGetResponse(context.Background(), nil)
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
var result = graphClient.drives().byDriveId("{drive-id}").items().byDriveItemId("{driveItem-id}").delta().get();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
const options = {
authProvider,
};
const client = Client.init(options);
let delta = await client.api('/me/drive/root/delta')
.get();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
<?php
use Microsoft\Graph\GraphServiceClient;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$result = $graphServiceClient->drives()->byDriveId('drive-id')->items()->byDriveItemId('driveItem-id')->delta()->get()->wait();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
Import-Module Microsoft.Graph.Files
Get-MgDriveItemDelta -DriveId $driveId -DriveItemId $driveItemId
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
result = await graph_client.drives.by_drive_id('drive-id').items.by_drive_item_id('driveItem-id').delta.get()
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
响应
以下示例显示了相应的响应。
HTTP/1.1 200 OK
Content-type: application/json
{
"value": [
{
"id": "0123456789abc",
"name": "folder2",
"folder": { }
},
{
"id": "123010204abac",
"name": "file.txt",
"file": { }
},
{
"id": "2353010204ddgg",
"name": "file5.txt",
"deleted": { }
}
],
"@odata.nextLink": "https://graph.microsoft.com/v1.0/me/drive/delta(token=1230919asd190410jlka)"
}
此响应包含第一页的更改,@odata.nextLink 属性指示当前的项目集中有更多项目。
在检索完所有项目页之前,你的应用程序应继续请求 @odata.nextLink 的 URL 值。
示例 2:集合中的最后一页
下面是如何调用此 API 以更新本地状态的示例。
请求
下面是初始请求之后的示例请求。
GET https://graph.microsoft.com/v1.0/me/drive/root/delta(token='MzslMjM0OyUyMzE7MzsyM2YwNDVhMS1lNmRmLTQ1N2MtOGQ5NS1hNmViZDVmZWRhNWQ7NjM3OTQzNzQwODQ3NTcwMDAwOzU4NTk2OTY0NDslMjM7JTIzOyUyMzA')
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Drives["{drive-id}"].Items["{driveItem-id}"].DeltaWithToken("{token}").GetAsDeltaWithTokenGetResponseAsync();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest major version. Current major version is $v1.*
// Dependencies
import (
"context"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
//other-imports
)
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=go
token := "{token}"
delta, err := graphClient.Drives().ByDriveId("drive-id").Items().ByDriveItemId("driveItem-id").DeltaWithToken(&token).GetAsDeltaWithTokenGetResponse(context.Background(), nil)
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
var result = graphClient.drives().byDriveId("{drive-id}").items().byDriveItemId("{driveItem-id}").deltaWithToken("{token}").get();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
const options = {
authProvider,
};
const client = Client.init(options);
let delta = await client.api('/me/drive/root/delta(token='MzslMjM0OyUyMzE7MzsyM2YwNDVhMS1lNmRmLTQ1N2MtOGQ5NS1hNmViZDVmZWRhNWQ7NjM3OTQzNzQwODQ3NTcwMDAwOzU4NTk2OTY0NDslMjM7JTIzOyUyMzA')')
.get();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
<?php
use Microsoft\Graph\GraphServiceClient;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$result = $graphServiceClient->drives()->byDriveId('drive-id')->items()->byDriveItemId('driveItem-id')->deltaWithToken('{token}', )->get()->wait();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
result = await graph_client.drives.by_drive_id('drive-id').items.by_drive_item_id('driveItem-id').delta_with_token("{token}").get()
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
响应
以下示例显示了相应的响应。
HTTP/1.1 200 OK
Content-type: application/json
{
"value": [
{
"id": "0123456789abc",
"name": "folder2",
"folder": { },
"deleted": { }
},
{
"id": "123010204abac",
"name": "file.txt",
"file": { }
}
],
"@odata.deltaLink": "https://graph.microsoft.com/v1.0/me/drive/root/delta?(token='MzslMjM0OyUyMzE7MzsyM2YwNDVhMS1lNmRmLTQ1N2MtOGQ5NS1hNmViZDVmZWRhNWQ7NjM3OTQzNzQwODQ3NTcwMDAwOzU4NTk2OTY0NDslMjM7JTIzOyUyMzA')"
}
此响应表示名为 folder2 的项目已被删除,并在初始请求和此请求之间添加或修改了 file.txt 项目以更新本地状态。
项目的最后一页包括 @odata.deltaLink 属性,该属性提供 URL,以后可用于检索自当前项目集以来的更改。
可能会发生本服务无法为特定标记提供更改列表的情况(例如,如果客户端在连接断开很长时间后尝试重新使用旧标记,或如果服务器状态已更改并需要新标记)。
在这些情况下,服务返回一个 HTTP 410 Gone 错误,其中包含一个错误响应,其中包含以下错误代码之一,以及一个 Location 标头,其中包含从头开始启动新增量枚举的新 nextLink。
完成全部枚举后,将返回的项目与本地状态进行比较,并遵循以下说明。
| 错误类型 |
说明 |
resyncChangesApplyDifferences |
将任何本地项替换为服务器的版本 (包括删除) (如果确定服务在上次同步时本地更改是最新的)。 上载服务器并不清楚的任意本地更改。 |
resyncChangesUploadDifferences |
上传服务未返回的任何本地项目,并上传任何与服务器版本不同的文件, (不确定哪一个是最新的) ,请保留这两个副本。 |
示例 3:检索当前 deltaLink
在某些情况下,请求当前 deltaLink 值可能非常有用(无需首先枚举驱动器中已有的所有项)。
如果应用只需了解更改,不需要了解现有项,这将非常有用。
若要检索最新的 deltaLink,请使用查询字符串参数 ?token=latest 调用 delta。
注意: 如果尝试维护文件夹或驱动器中项目的完整本地表示形式,则必须使用 delta 进行初始枚举。
如果在枚举期间发生任何写入操作,则无法保证其他方法(例如通过文件夹的 children 集合分页)返回每一个项目。
使用 delta 是确保读取所需全部数据的唯一方法。
请求
GET /me/drive/root/delta?token=latest
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Drives["{drive-id}"].Items["{driveItem-id}"].Delta.GetAsDeltaGetResponseAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Token = "latest";
});
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest major version. Current major version is $v1.*
// Dependencies
import (
"context"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
graphdrives "github.com/microsoftgraph/msgraph-sdk-go/drives"
//other-imports
)
requestToken := "latest"
requestParameters := &graphdrives.ItemItemsItemDeltaRequestBuilderGetQueryParameters{
Token: &requestToken,
}
configuration := &graphdrives.ItemItemsItemDeltaRequestBuilderGetRequestConfiguration{
QueryParameters: requestParameters,
}
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=go
delta, err := graphClient.Drives().ByDriveId("drive-id").Items().ByDriveItemId("driveItem-id").Delta().GetAsDeltaGetResponse(context.Background(), configuration)
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
var result = graphClient.drives().byDriveId("{drive-id}").items().byDriveItemId("{driveItem-id}").delta().get(requestConfiguration -> {
requestConfiguration.queryParameters.token = "latest";
});
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
const options = {
authProvider,
};
const client = Client.init(options);
let delta = await client.api('/me/drive/root/delta?token=latest')
.get();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
<?php
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Graph\Generated\Drives\Item\Items\Item\Delta\DeltaRequestBuilderGetRequestConfiguration;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new DeltaRequestBuilderGetRequestConfiguration();
$queryParameters = DeltaRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->token = "latest";
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->drives()->byDriveId('drive-id')->items()->byDriveItemId('driveItem-id')->delta()->get($requestConfiguration)->wait();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
Import-Module Microsoft.Graph.Files
Get-MgDriveItemDelta -DriveId $driveId -DriveItemId $driveItemId -Token "latest"
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
from msgraph.generated.drives.item.items.item.delta.delta_request_builder import DeltaRequestBuilder
from kiota_abstractions.base_request_configuration import RequestConfiguration
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
query_params = DeltaRequestBuilder.DeltaRequestBuilderGetQueryParameters(
token = "latest",
)
request_configuration = RequestConfiguration(
query_parameters = query_params,
)
result = await graph_client.drives.by_drive_id('drive-id').items.by_drive_item_id('driveItem-id').delta.get(request_configuration = request_configuration)
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
响应
HTTP/1.1 200 OK
Content-type: application/json
{
"value": [ ],
"@odata.deltaLink": "https://graph.microsoft.com/v1.0/me/drive/root/delta?token=1230919asd190410jlka"
}
示例 4:使用时间戳检索增量结果
在某些情况下,客户端可能知道某个特定时间之前的驱动器状态,但没有该时间点的 deltaLink。 在这种情况下,客户端可以使用查询字符串参数?token=2021-09-29T20%3A00%3A00Z值的 token URL 编码时间戳或“?token=2021-09-29T12%3A00%3A00%2B8%3A00”进行调用delta。
仅 OneDrive for Business 和 SharePoint 支持使用时间戳代替令牌。
注意: 客户端应尽可能使用 delta 查询提供的 deltaLink,而不是生成自己的令牌。 仅当 deltaLink 未知时才应使用此功能。
请求
GET /me/drive/root/delta?token=2021-09-29T20%3A00%3A00Z
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Drives["{drive-id}"].Items["{driveItem-id}"].Delta.GetAsDeltaGetResponseAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Token = "2021-09-29T20:00:00Z";
});
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest major version. Current major version is $v1.*
// Dependencies
import (
"context"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
graphdrives "github.com/microsoftgraph/msgraph-sdk-go/drives"
//other-imports
)
requestToken := "2021-09-29T20:00:00Z"
requestParameters := &graphdrives.ItemItemsItemDeltaRequestBuilderGetQueryParameters{
Token: &requestToken,
}
configuration := &graphdrives.ItemItemsItemDeltaRequestBuilderGetRequestConfiguration{
QueryParameters: requestParameters,
}
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=go
delta, err := graphClient.Drives().ByDriveId("drive-id").Items().ByDriveItemId("driveItem-id").Delta().GetAsDeltaGetResponse(context.Background(), configuration)
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
var result = graphClient.drives().byDriveId("{drive-id}").items().byDriveItemId("{driveItem-id}").delta().get(requestConfiguration -> {
requestConfiguration.queryParameters.token = "2021-09-29T20:00:00Z";
});
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
const options = {
authProvider,
};
const client = Client.init(options);
let delta = await client.api('/me/drive/root/delta?token=2021-09-29T20:00:00Z')
.get();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
<?php
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Graph\Generated\Drives\Item\Items\Item\Delta\DeltaRequestBuilderGetRequestConfiguration;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new DeltaRequestBuilderGetRequestConfiguration();
$queryParameters = DeltaRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->token = "2021-09-29T20:00:00Z";
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->drives()->byDriveId('drive-id')->items()->byDriveItemId('driveItem-id')->delta()->get($requestConfiguration)->wait();
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
Import-Module Microsoft.Graph.Files
Get-MgDriveItemDelta -DriveId $driveId -DriveItemId $driveItemId -Token "2021-09-29T20:00:00Z"
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
from msgraph.generated.drives.item.items.item.delta.delta_request_builder import DeltaRequestBuilder
from kiota_abstractions.base_request_configuration import RequestConfiguration
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
query_params = DeltaRequestBuilder.DeltaRequestBuilderGetQueryParameters(
token = "2021-09-29T20:00:00Z",
)
request_configuration = RequestConfiguration(
query_parameters = query_params,
)
result = await graph_client.drives.by_drive_id('drive-id').items.by_drive_item_id('driveItem-id').delta.get(request_configuration = request_configuration)
有关如何将 SDK 添加到项目并创建 authProvider 实例的详细信息,请参阅 SDK 文档。
响应
HTTP/1.1 200 OK
Content-type: application/json
{
"value": [
{
"id": "0123456789abc",
"name": "folder2",
"folder": { },
"deleted": { }
},
{
"id": "123010204abac",
"name": "file.txt",
"file": { }
}
],
"@odata.deltaLink": "https://graph.microsoft.com/v1.0/me/drive/root/delta?(token='1230919asd190410jlka')"
}
增量源显示每项的最新状态,而不是每个更改的最新状态。 如果项目重命名两次,它只显示一次并且使用最新名称。
由于种种原因,同一项可能会在 delta 源中多次出现。 应使用最后一次出现的项。
项 parentReference 上的 属性不包括 路径的值。 这是因为重命名文件夹不会导致从 delta 返回文件夹的任何后代。
使用增量时,应始终按 ID 跟踪项目。
Delta 查询不会返回某些 DriveItem 属性,具体取决于作和服务类型,如下表所示。
OneDrive for Business
| 操作类型 |
Delta 查询忽略的属性 |
| 创建/修改 |
ctag |
| 删除 |
ctag, name |
OneDrive(消费者)
| 操作类型 |
Delta 查询忽略的属性 |
| 创建/修改 |
不适用 |
| 删除 |
ctag, size |
扫描权限层次结构
默认情况下,增量查询响应包括查询中更改的所有项的共享信息,即使这些项从父项继承其权限并且本身没有直接共享更改也是如此。 这通常会导致后续调用来获取每个项目的权限详细信息,而不仅仅是共享信息已更改的项。 通过向增量查询请求添加 Prefer: hierarchicalsharing 标头,能够帮助你更好地了解权限更改的发生方式。
Prefer: hierarchicalsharing提供 标头后,会为权限层次结构的根目录以及显式具有共享更改的项返回共享信息。 如果共享更改是从项中删除共享,则会发现一个空共享方面,以区分继承自父项的项目和唯一但没有共享链接的项目。 你还将在权限层次结构的根目录上看到此空共享方面,该层次结构未共享以建立初始范围。
在许多扫描场景中,你可能会对权限的更改特别感兴趣。 若要在增量查询响应中明确哪些更改是由权限更改造成的,可提供 Prefer: deltashowsharingchanges 标头。 提供此标头后,由于权限更改而出现在增量查询响应中的所有项都具有 @microsoft.graph.sharedChanged":"True" OData 注释。 此功能适用于 SharePoint 和 OneDrive for Business 帐户,但不适用于 OneDrive 消费者版本的帐户。
注意
标头的使用 Prefer: deltashowsharingchanges 要求使用 Prefer: deltashowremovedasdeleted 和 Prefer: deltatraversepermissiongaps。 这些标头值可以在一个标头中连接在一起:Prefer: deltashowremovedasdeleted, deltatraversepermissiongaps, deltashowsharingchanges。
为了正确处理权限,应用程序需要请求 Sites.FullControl.All 权限。
有关扫描方案的详细信息,请参阅发现 文件和大规模检测更改的最佳做法。
错误响应
除了上面详述的重新同步错误之外,还请参阅错误响应,详细了解错误返回方式。
相关内容
使用增量查询跟踪Microsoft Graph 数据更改的大规模发现文件和检测更改的最佳做法