Added migration from 5.x to 6.x to docs

This commit is contained in:
Jonas Dellinger 2020-11-14 13:19:20 +01:00
parent 35dbc7f57d
commit 66064aa0f5
3 changed files with 168 additions and 9 deletions

View File

@ -0,0 +1,154 @@
---
id: 5_to_6
title: 5.x.x to 6.x.x
---
## SpotifyAPI.Web
### Initialization
In `5.x`, a new `SpotifyWebAPI` instance could be created without supplying necessary values, since they were implemented as properties. With `6.x`, necessary values have to be given in the constructor and `SpotifyWebAPI` has been renamed to `SpotifyClient`. Also, `SpotifyClientConfig` has been introduced to give a better configuration experience, including retry handlers, automatic authenticators and proxy configurations.
```csharp
// OLD
var spotify = new SpotifyWebAPI { AccessToken = "YourAccessToken" };
var spotify = new SpotifyWebAPI(ProxyConfig); // No access token - invalid
// NEW
var spotify = new SpotifyClient("YourAccessToken");
var config = SpotifyClientConfig
.CreateDefault()
.WithToken("YourAccessToken");
var spotify = new SpotifyClient(config);
var config = SpotifyClientConfig
.CreateDefault()
.WithAuthenticator(new ClientCredentialsAuthenticator(CLIENT_ID, CLIENT_SECRET)); // takes care of access tokens
var spotify = new SpotifyClient(config);
```
For some performance guides, have a look at the [Configuration Guide](./configuration.md)
### Proxy
In `5.x`, the proxy configuration could be passed to the `SpotifyWebAPI` constructor. In `6.x`, they're part of the HTTP Client. The built-in http client supports proxies out of the box:
```csharp
var httpClient = new NetHttpClient(new ProxyConfig("localhost", 8080)
{
User = "",
Password = "",
SkipSSLCheck = false,
});
var config = SpotifyClientConfig
.CreateDefault()
.WithHTTPClient(httpClient);
var spotify = new SpotifyClient(config);
```
### Calling API Endpoints
In `5.x`, there was one big instance to support all API endpoints. Parameters to these endpoints were passed directly as method parameters. Optional parameters were nullable and could be excluded. In `6.x`, every endpoint group (`albums`, `tracks`, `userprofile`) has their own API-Client, which is available as a property in a `SpotifyClient` instance. While URI path parameters are still passed as method parameter, query and body parameters are now passed as a grouped class instance, where required parameters are needed in the constructor and optional parameters can be supplied as properties. All endpoints are also only implemented as async methods.
```csharp
// OLD:
PrivateProfile profile = await spotify.GetPrivateProfileAsync();
var playlists = await spotify.GetUserPlaylists(profile.Id, 100, 0);
// NEW:
PrivateUser user = await spotify.UserProfile.Current();
var playlists = await spotify.Playlists.GetUsers(user.Id, new PlaylistGetUsersRequest
{
Limit = 100,
Offset = 0
});
```
All required arguments are checked for non-null values. If it's null, the methods will throw a `ArgumentNullException`
### Error/Header Handling
In `5.x`, all response models included a base error model, with properties like `Headers`, `Error` and `HasError`. This was not a good decision since response models should be clean and only contain API response data. In `6.x`, error handling is `Exception` based. For example, if the access token is invalid, calling API endpoints will throw a `APIUnauthorizedException`. If you hit the API too many times, the method will throw a `APITooManyRequestsException`. They all derive from a base exception `APIException`, which is also thrown in more general cases, e.g bad request input parameters. If you're interested in the headers of the last response, you can use `spotify.LastResponse`, **make sure there is only one thread using this instance!**
```csharp
// OLD:
PrivateProfile profile = await spotify.GetPrivateProfileAsync();
if(profile.HasError())
{
// handle error
}
var headers = profile.Headers(); // access to headers
// NEW:
try
{
PrivateProfile profile = await spotify.GetPrivateProfileAsync();
var response = spotify.LastResponse; // response.Headers
}
catch (APIUnauthorizedException e)
{
// handle unauthorized error
// e.Response contains HTTP response
// e.Message contains Spotify error message
}
catch (APIException e)
{
// handle common error
// e.Response contains HTTP response
// e.Message contains Spotify error message
}
```
More Info: [Error Handling](./error_handling)
## SpotifyAPI.Web.Auth
In `5.x`, `SpotifyAPI.Web.Auth` contained every logic related to the OAuth flows. In `6.x`, `SpotifyAPI.Web.Auth` is only required if you need a HTTP Server for handling OAuth responses. For example, if you're in a ASP.NET environment or just use the [Client Credentials](client_credentials) flow, there is no need to install `SpotifyAPI.Web.Auth` anymore.
### Authorization Code Auth
As an example, this shows how to convert a `5.x` authorization code flow to `6.x`:
```csharp
// OLD
var auth =
new AuthorizationCodeAuth(_clientId, _secretId, "http://localhost:4002", "http://localhost:4002",
Scope.PlaylistReadPrivate | Scope.PlaylistReadCollaborative);
auth.AuthReceived += AuthOnAuthReceived;
auth.Start();
auth.OpenBrowser();
private static async void AuthOnAuthReceived(object sender, AuthorizationCode payload)
{
var auth = (AuthorizationCodeAuth) sender;
auth.Stop();
Token token = await auth.ExchangeCode(payload.Code);
var spotify = new SpotifyWebAPI { AccessToken = token.AccessToken };
await PrintUsefulData(spotify);
}
// NEW
var config = SpotifyClientConfig.CreateDefault();
var server = new EmbedIOAuthServer(new Uri("http://localhost:5000/callback"), 5000);
server.AuthorizationCodeReceived += async (sender, response) =>
{
await server.Stop();
var tokenResponse = await new OAuthClient(config).RequestToken(new AuthorizationCodeTokenRequest(
_clientId, _secretId, response.Code, server.BaseUri
));
var spotify = new SpotifyClient(config.WithToken(tokenResponse.AccessToken));
}
await server.Start();
var loginRequest = new LoginRequest(server.BaseUri, _clientId, LoginRequest.ResponseType.Code)
{
Scope = new[] { Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative }
};
BrowserUtil.Open(loginRequest.ToUri());
```
While it is more code to write, there is a better seperation of concerns. For example, it is able to construct a `LoginRequest` without starting a server. This `LoginRequest` can also be used to forward the user to in a web-based context. The same auth server `EmbedIOAuthServer` can be used to receive `AuthorizationCodes` and `ImplictGrants` responses.

View File

@ -14,8 +14,8 @@ module.exports = {
'pagination',
'retry_handling',
'iplayableitem',
'unit_testing'
]
'unit_testing',
],
},
{
type: 'category',
@ -26,8 +26,8 @@ module.exports = {
'implicit_grant',
'authorization_code',
'pkce',
'token_swap'
]
'token_swap',
],
},
'showcase',
{
@ -40,9 +40,14 @@ module.exports = {
'example_cli_custom_html',
'example_cli_persistent_config',
'example_token_swap',
'example_uwp'
]
'example_uwp',
],
},
]
}
{
type: 'category',
label: 'Migration Guides',
items: ['5_to_6'],
},
],
},
};

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>