Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2737eab
Samples - WIP
annelo-msft Mar 2, 2024
e1f7a82
README updates
annelo-msft Mar 4, 2024
6bae518
Merge remote-tracking branch 'upstream/main' into clientmodel-samples
annelo-msft Mar 4, 2024
e43c767
Configuration samples
annelo-msft Mar 4, 2024
e0fee6f
updates before generating snippets
annelo-msft Mar 4, 2024
9dbf76b
update snippets
annelo-msft Mar 4, 2024
433c3fc
readme updates
annelo-msft Mar 4, 2024
a7934ed
intermediate backup
annelo-msft Mar 4, 2024
f7adcbf
updates
annelo-msft Mar 4, 2024
2d4ec41
fix
annelo-msft Mar 4, 2024
b9e406f
updates
annelo-msft Mar 4, 2024
8408cf3
nit
annelo-msft Mar 4, 2024
b316e9c
nit
annelo-msft Mar 4, 2024
f0c2877
fix links
annelo-msft Mar 5, 2024
456c6b5
updates from PR feedback
annelo-msft Mar 5, 2024
c0db649
revert engsys file
annelo-msft Mar 5, 2024
75d0161
update product
annelo-msft Mar 5, 2024
f6c136c
Merge remote-tracking branch 'upstream/main' into clientmodel-samples
annelo-msft Mar 5, 2024
262e923
add sample client implementation
annelo-msft Mar 5, 2024
552b485
add input model to sample client method
annelo-msft Mar 5, 2024
d83555e
change API key in samples
annelo-msft Mar 5, 2024
6d99f19
add inline comments to sample client and change defaults on HttpClien…
annelo-msft Mar 6, 2024
ee42d04
update impressions link
annelo-msft Mar 6, 2024
e8b6e09
restructure to address PR feedback
annelo-msft Mar 6, 2024
81eae19
nits
annelo-msft Mar 6, 2024
095324c
nits
annelo-msft Mar 6, 2024
47a47ca
nits
annelo-msft Mar 6, 2024
4ef9646
Merge remote-tracking branch 'upstream/main' into clientmodel-samples
annelo-msft Mar 6, 2024
91d3223
small updates from PR feedback
annelo-msft Mar 6, 2024
863cec6
add comment
annelo-msft Mar 7, 2024
7a02ba2
rework convenience methods section in README
annelo-msft Mar 7, 2024
c9fc7d0
more updates; add dotnet-api slug
annelo-msft Mar 7, 2024
f459730
Add sample showing response classifier
annelo-msft Mar 8, 2024
3c75fa9
updates:
annelo-msft Mar 8, 2024
8040105
reference error response configuration sample from README
annelo-msft Mar 8, 2024
24cc903
Merge remote-tracking branch 'upstream/main' into clientmodel-samples
annelo-msft Mar 8, 2024
a49b27b
update samples README
annelo-msft Mar 8, 2024
5f00c79
update md files
annelo-msft Mar 8, 2024
49896a6
show creation of BinaryContent from model in RequestOptions sample
annelo-msft Mar 8, 2024
81e2e20
add examples of different way to create BinaryContent
annelo-msft Mar 8, 2024
3aed272
show protocol method implementation and message.Apply(options)
annelo-msft Mar 8, 2024
d7a36c7
updates
annelo-msft Mar 8, 2024
6c56e7c
nits
annelo-msft Mar 8, 2024
2719c61
nits
annelo-msft Mar 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/common/scripts/Test-SampleMetadata.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ begin {
"sql-server-2008",
"surface-duo",
"sway",
"system-clientmodel",
"vs-app-center",
"vs-code",
"vs-mac",
Expand Down
136 changes: 106 additions & 30 deletions sdk/core/System.ClientModel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

## Getting started

Typically, you will not need to install `System.ClientModel`.
it will be installed for you when you install one of the client libraries using it.
Typically, you will not need to install `System.ClientModel`. It will be installed for you when you install a client library that uses it.

### Install the package

Expand All @@ -25,67 +24,144 @@ None needed for `System.ClientModel`.

### Authenticate the client

The `System.ClientModel` package provides a `KeyCredential` type for authentication.
The `System.ClientModel` package provides an `ApiKeyCredential` type for authentication.

## Key concepts

The main shared concepts of `System.ClientModel` include:
The main concepts in `System.ClientModel` include:

- Configuring service clients (`ClientPipelineOptions`).
- Accessing HTTP response details (`ClientResult`, `ClientResult<T>`).
- Exceptions for reporting errors from service requests in a consistent fashion (`ClientResultException`).
- Customizing requests (`RequestOptions`).
- Providing APIs to read and write models in different formats (`ModelReaderWriter`).
- Handling exceptions that result from failed requests (`ClientResultException`).
- Customizing HTTP requests (`RequestOptions`).
- Reading and writing models in different formats (`ModelReaderWriter`).

Below, you will find sections explaining these shared concepts in more detail.

## Examples

### Send a message using the MessagePipeline
### Configuring service clients

`System.ClientModel`-based clients, or **service clients**, provide a constructor that takes a service endpoint and a credential used to authenticate with the service. They also provide a constructor overload that takes an endpoint, a credential, and an instance of `ClientPipelineOptions`.
Passing `ClientPipelineOptions` when a client is created will configure the pipeline that the client uses to send and receive HTTP requests and responses. Client pipeline options can be used to override default values such as the network timeout used to send or retry a request.

```C# Snippet:ClientModelConfigurationReadme
MapsClientOptions options = new()
{
NetworkTimeout = TimeSpan.FromSeconds(120),
};

string key = Environment.GetEnvironmentVariable("MAPS_API_KEY") ?? string.Empty;
ApiKeyCredential credential = new(key);
MapsClient client = new(new Uri("https://atlas.microsoft.com"), credential, options);
```

For more information on client configuration, see [Client configuration samples](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/samples/Configuration.md)

### Accessing HTTP response details

A very basic client implementation might use the following approach:
Service clients have methods that are used to call cloud services to invoke service operations. These methods on a client are called **service methods**. Service clients expose two types of service methods: _convenience methods_ and _protocol methods_.

```csharp
ApiKeyCredential credential = new ApiKeyCredential(key);
ApiKeyAuthenticationPolicy authenticationPolicy = ApiKeyAuthenticationPolicy.CreateBearerAuthorizationPolicy(credential);
ClientPipeline pipeline = ClientPipeline.Create(pipelineOptions, authenticationPolicy);
**Convenience methods** provide a convenient way to invoke a service operation. They are methods that take a strongly-typed model as input and return a `ClientResult<T>` that holds a strongly-typed representation of the service response. Details from the HTTP response can be obtained from the return value.

PipelineMessage message = pipeline.CreateMessage();
message.Apply(requestOptions);
message.MessageClassifier = PipelineMessageClassifier.Create(stackalloc ushort[] { 200 });
**Protocol method** are low-level methods that take parameters that correspond to the service HTTP API and return a `ClientResult` holding only the raw HTTP response details. These methods also take an optional `RequestOptions` value that allows the client pipeline and the request to be configured for the duration of the call.

PipelineRequest request = message.Request;
request.Method = "GET";
request.Uri = new Uri("https://www.example.com/");
request.Headers.Add("Accept", "application/json");
The following sample illustrates how to call a convenience method and access both the strongly-typed output model and the details of the HTTP response.

pipeline.Send(message);
Console.WriteLine(message.Response.Status);
```C# Snippet:ClientResultTReadme
// Create a client
string key = Environment.GetEnvironmentVariable("MAPS_API_KEY") ?? string.Empty;
ApiKeyCredential credential = new(key);
MapsClient client = new(new Uri("https://atlas.microsoft.com"), credential);

// Call a service method, which returns ClientResult<T>
IPAddress ipAddress = IPAddress.Parse("2001:4898:80e8:b::189");
ClientResult<IPAddressCountryPair> result = await client.GetCountryCodeAsync(ipAddress);

// ClientResult<T> has two members:
//
// (1) A Value property to access the strongly-typed output
IPAddressCountryPair value = result.Value;
Console.WriteLine($"Country is {value.CountryRegion.IsoCode}.");

// (2) A GetRawResponse method for accessing the details of the HTTP response
PipelineResponse response = result.GetRawResponse();

Console.WriteLine($"Response status code: '{response.Status}'.");
Console.WriteLine("Response headers:");
foreach (KeyValuePair<string, string> header in response.Headers)
{
Console.WriteLine($"Name: '{header.Key}', Value: '{header.Value}'.");
}
```

For more information on client service methods, see [Client service method samples](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/samples/ServiceMethods.md).

### Handling exceptions that result from failed requests

When a service call fails, service clients throw a `ClientResultException`. The exception exposes the HTTP status code and the details of the service response if available.

```C# Snippet:ClientResultExceptionReadme
try
{
IPAddress ipAddress = IPAddress.Parse("2001:4898:80e8:b::189");
ClientResult<IPAddressCountryPair> result = await client.GetCountryCodeAsync(ipAddress);
}
// Handle exception with status code 404
catch (ClientResultException e) when (e.Status == 404)
{
// Handle not found error
Console.Error.WriteLine($"Error: Response failed with status code: '{e.Status}'");
}
```

### Customizing HTTP requests

Service clients expose low-level _protocol methods_ that allow callers to customize the details of HTTP requests. Protocol methods take an optional `RequestOptions` value that allows callers to add a header to the request, or to add a policy to the client pipeline that can modify the request in any way before sending it to the service. `RequestOptions` also allows passing a `CancellationToken` to the method.

```C# Snippet:RequestOptionsReadme
// Create RequestOptions instance
RequestOptions options = new();

// Set CancellationToken
options.CancellationToken = cancellationToken;

// Add a header to the request
options.AddHeader("CustomHeader", "CustomHeaderValue");

// Call protocol method to pass RequestOptions
ClientResult output = await client.GetCountryCodeAsync(ipAddress.ToString(), options);
```

For more information on customizing requests, see [Protocol method samples](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/samples/ServiceMethods.md#protocol-methods)

### Read and write persistable models

As a library author you can implement `IPersistableModel<T>` or `IJsonModel<T>` which will give library users the ability to read and write your models.
Client library authors can implement the `IPersistableModel<T>` or `IJsonModel<T>` interfaces on strongly-typed model implementations. If they do, end-users of service clients can then read and write those models in cases where they need to persist them to a backing store.

Example writing an instance of a model.
The example below shows how to write a persistable model to `BinaryData`.

```C# Snippet:Readme_Write_Simple
InputModel model = new InputModel();
BinaryData data = ModelReaderWriter.Write(model);
```

Example reading a model from json
The example below shows how to read JSON to create a strongly-typed model instance.

```C# Snippet:Readme_Read_Simple
string json = @"{
""x"": 1,
""y"": 2,
""z"": 3
}";
""x"": 1,
""y"": 2,
""z"": 3
}";
OutputModel? model = ModelReaderWriter.Read<OutputModel>(BinaryData.FromString(json));
```

## Troubleshooting

You can troubleshoot `System.ClientModel`-based clients by inspecting the result of any `ClientResultException` thrown from a pipeline's `Send` method.
You can troubleshoot service clients by inspecting the result of any `ClientResultException` thrown from a client's service method.

For more information on client service method errors, see [Handling exceptions that result from failed requests](#handling-exceptions-that-result-from-failed-requests).

## Next steps

Expand Down
89 changes: 89 additions & 0 deletions sdk/core/System.ClientModel/samples/Configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# System.ClientModel-based client configuration samples

## Configuring retries

To modify the retry policy, create a new instance of `ClientRetryPolicy` and set it on the `ClientPipelineOptions` passed to the client constructor.

By default, clients will retry a request three times using an exponential retry strategy with an initial delay of 0.8 seconds and a maximum delay of one minute.

```C# Snippet:ConfigurationCustomizeRetries
MapsClientOptions options = new()
{
RetryPolicy = new ClientRetryPolicy(maxRetries: 5),
};

string key = Environment.GetEnvironmentVariable("MAPS_API_KEY") ?? string.Empty;
ApiKeyCredential credential = new(key);
MapsClient client = new(new Uri("https://atlas.microsoft.com"), credential, options);
```

## Add a custom policy to the pipeline

Azure SDKs provides a way to add policies to the pipeline at three positions, `PerCall`, `PerTry`, and `BeforeTranspor`.

- `PerCall` policies run once per request

```C# Snippet:ConfigurationAddPerCallPolicy
MapsClientOptions options = new();
options.AddPolicy(new StopwatchPolicy(), PipelinePosition.PerCall);
```

- `PerTry` policies run each time a request is tried

```C# Snippet:ConfigurationAddPerTryPolicy
options.AddPolicy(new StopwatchPolicy(), PipelinePosition.PerTry);
```

- `BeforeTransport` policies run after all other policies in the pipeline and before the request is sent by the transport.

Adding policies at the `BeforeTransport` position should be done with care since changes made to the request by a before-transport policy will not be visible to any logging policies that come before it in the pipeline.

```C# Snippet:ConfigurationAddBeforeTransportPolicy
options.AddPolicy(new StopwatchPolicy(), PipelinePosition.BeforeTransport);
```

## Implement a custom policy

To implement a policy create a class that derives from `PipelinePolicy` and overide its `ProcessAsync` and `Process` methods. The request can be accessed via `message.Request`. The response is accessible via `message.Response`, but will have a value only after `ProcessNextAsync`/`ProcessNext` has been called.

```C# Snippet:ConfigurationCustomPolicy
public class StopwatchPolicy : PipelinePolicy
{
public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
{
Stopwatch stopwatch = new();
stopwatch.Start();

await ProcessNextAsync(message, pipeline, currentIndex);

stopwatch.Stop();

Console.WriteLine($"Request to {message.Request.Uri} took {stopwatch.Elapsed}");
}

public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
{
Stopwatch stopwatch = new();
stopwatch.Start();

ProcessNext(message, pipeline, currentIndex);

stopwatch.Stop();

Console.WriteLine($"Request to {message.Request.Uri} took {stopwatch.Elapsed}");
}
}
```

## Provide a custom HttpClient instance

In some cases, users may want to provide a custom instance of the `HttpClient` used by a client's transport to send and receive HTTP messages. To provide a custom `HttpClient`, create a new instance of `HttpClientPipelineTransport` and pass the custom `HttpClient` instance to its constructor.

```C# Snippet:ConfigurationCustomHttpClient
using HttpClient httpClient = new();

MapsClientOptions options = new()
{
Transport = new HttpClientPipelineTransport(httpClient)
};
```
16 changes: 16 additions & 0 deletions sdk/core/System.ClientModel/samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
page_type: sample
languages:
- csharp
products:
- system.clientmodel
name: System.ClientModel samples for .NET
description: Samples for the System.ClientModel library
---

# System.ClientModel Samples

- [Client Configuration](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/samples/Configuration.md)
- [Convenience Methods](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/samples/ConvenienceMethods.md)
- [Protocol Methods](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/samples/ProtocolMethods.md)
- [Client Pipeline](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/samples/Pipeline.md)
Loading