Skip to content
This repository was archived by the owner on Jan 23, 2025. It is now read-only.

Commit 7f0c0bc

Browse files
committed
fixes #2 GetJsonAsync does not handle errors from Instagram.
1 parent c24b457 commit 7f0c0bc

21 files changed

+617
-81
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"catstagram",
1919
"cdnjs",
2020
"crossorigin",
21+
"fbtrace",
2122
"gitversioning",
2223
"instacat",
2324
"instagood",

docs/InstagramApi.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ While `AuthenticateAsync` can optionally return a long-lived Instagram user acce
8080
/// Exchanges short-lived access token for a long-lived access token
8181
/// </summary>
8282
/// <param name="response">A valid <see cref="OAuthResponse"/></param>
83-
/// <returns>A OAuthResponse or null if an error is caught</returns>
83+
/// <returns>A OAuthResponse or an Exception if an error is caught</returns>
8484
/// <exception cref="ArgumentNullException">An Instagram User Access Token is needed</exception>
8585
public async Task<OAuthResponse> GetLongLivedAccessTokenAsync(OAuthResponse response)
8686
{
@@ -94,7 +94,7 @@ While a the initial 1 hour or an exchanged 60 day Instagram user access token wi
9494
/// Refreshes an existing long-lived Instagram User Access Token for another 60 day one.
9595
/// </summary>
9696
/// <param name="response">A valid <see cref="OAuthResponse"/></param>
97-
/// <returns>A refreshed long-lived 60 day access token or null if an error is caught</returns>
97+
/// <returns>A refreshed long-lived 60 day access token or an Exception if an error is caught</returns>
9898
/// <exception cref="ArgumentNullException">An Instagram User Access Token is needed</exception>
9999
public async Task<OAuthResponse> RefreshLongLivedAccessToken(OAuthResponse response)
100100
{
@@ -109,7 +109,7 @@ While `AuthenticateAsync` will return an `OAuthResponse` object containing a `Us
109109
/// </summary>
110110
/// <param name="accessToken">An Instagram User Access Token</param>
111111
/// <param name="userId">Defaults to me or a valid userId</param>
112-
/// <returns>A UserInfo object or null if an error is caught</returns>
112+
/// <returns>A UserInfo object or an Exception if an error is caught</returns>
113113
public async Task<UserInfo> GetUserAsync(string accessToken, string userId = "me")
114114
{
115115

@@ -124,7 +124,7 @@ A common use case will be to get an Instagram user's image's, videos and albums
124124
/// </summary>
125125
/// <param name="accessToken">An Instagram User Access Token</param>
126126
/// <param name="userId">Default to me or pass a valid userId</param>
127-
/// <returns>A Media object or null if an error is caught</returns>
127+
/// <returns>A Media object or an Exception if an error is caught</returns>
128128
public async Task<Media> GetMediaListAsync(string accessToken, string userId)
129129
{
130130
}
@@ -138,8 +138,16 @@ You can also get an individual media object from it's identifier, a common use c
138138
/// </summary>
139139
/// <param name="accessToken">An Instagram User Access Token</param>
140140
/// <param name="mediaId">A valid Media Id</param>
141-
/// <returns>A Media object or null if an error is caught</returns>
141+
/// <returns>A Media object or an Exception if an error is caught</returns>
142142
public async Task<Media> GetMediaAsync(string accessToken, string mediaId)
143143
{
144144
}
145145
```
146+
147+
### Exceptions
148+
149+
From NuGet version 1.0.6 custom `Exceptions` are thrown whenever Instagram returns a non success `HttpStatusCode`.
150+
151+
These allow you to `try` and `catch` and handle fails gracefully as well as get an insight into what the issue was.
152+
153+
Look for `InstagramApiException` `InstagramOAuthException` and the more general `InstagramException`

readme.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,11 @@ Facebook also provides tooling to quickly generate [long-lived Instagram User Ac
280280
For more information see [User Token Generator](facebook-and-instagram-setup.md#user-token-generator).
281281

282282
![User Token Generator](https://i.imgur.com/Ql7mrk0.png)
283+
284+
### Exceptions
285+
286+
From NuGet version 1.0.6 custom `Exceptions` are thrown whenever Instagram returns a non success `HttpStatusCode`.
287+
288+
These allow you to `try` and `catch` and handle fails gracefully as well as get an insight into what the issue was.
289+
290+
Look for `InstagramApiException` `InstagramOAuthException` and the more general `InstagramException`

samples/Web/Pages/Auth/OAuth/Index.cshtml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@
1919
<body>
2020
<div class="container">
2121
<h3>Instagram Basic Display API - Redirect Url Page</h3>
22+
23+
@if(Model.ShowMessage)
24+
{
25+
<div class="alert alert-warning alert-dismissible fade show" role="alert">
26+
<strong>Message:</strong> @Model.Message
27+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
28+
<span aria-hidden="true">&times;</span>
29+
</button>
30+
</div>
31+
}
32+
2233
<div class="lead">
2334
<p>
2435
The user is sent back to this page on a successful login to instagram with a code and state.

samples/Web/Pages/Auth/OAuth/Index.cshtml.cs

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using Solrevdev.InstagramBasicDisplay.Core;
77
using Solrevdev.InstagramBasicDisplay.Core.Instagram;
88
using Web.Extensions;
9+
using Solrevdev.InstagramBasicDisplay.Core.Exceptions;
10+
using System;
911

1012
namespace Web.Pages.Auth.OAuth
1113
{
@@ -17,6 +19,9 @@ public class IndexModel : PageModel
1719
public string State { get; private set; }
1820
public UserInfo UserInfo { get; set; }
1921
public List<Media> Media { get; } = new List<Media>();
22+
public bool ShowMessage => !string.IsNullOrWhiteSpace(Message);
23+
[TempData]
24+
public string Message { get; set; }
2025

2126
public IndexModel(ILogger<IndexModel> logger, InstagramApi api)
2227
{
@@ -28,39 +33,65 @@ public async Task<IActionResult> OnGetAsync(string code, string state)
2833
{
2934
_logger.LogInformation("Auth/OAuth for state [{state}] returned the code [{code}]", state, code);
3035

31-
var response = HttpContext.Session.Get<OAuthResponse>(Strings.SessionKey) ?? await _api.AuthenticateAsync(code, state).ConfigureAwait(false);
32-
if (response == null)
36+
try
3337
{
34-
return RedirectToLoginPage();
35-
}
38+
var response = HttpContext.Session.Get<OAuthResponse>(Strings.SessionKey) ?? await _api.AuthenticateAsync(code, state).ConfigureAwait(false);
39+
if (response == null)
40+
{
41+
Message = "OAutResponse is null. Redirecting to login page";
42+
return RedirectToLoginPage();
43+
}
3644

37-
var media = await _api.GetMediaListAsync(response).ConfigureAwait(false);
38-
_logger.LogInformation("Initial media response returned with [{count}] records ", media.Data.Count);
39-
Media.Add(media);
45+
var media = await _api.GetMediaListAsync(response).ConfigureAwait(false);
46+
_logger.LogInformation("Initial media response returned with [{count}] records ", media.Data.Count);
47+
Media.Add(media);
4048

41-
//
42-
// TODO: toggle the following boolean for a quick and dirty way of getting all a user's media.
43-
//
44-
if (false)
45-
{
46-
while (!string.IsNullOrWhiteSpace(media?.Paging?.Next))
49+
//
50+
// TODO: toggle the following boolean for a quick and dirty way of getting all a user's media.
51+
//
52+
if (true)
4753
{
48-
var next = media?.Paging?.Next;
49-
var count = media?.Data?.Count;
50-
_logger.LogInformation("Getting next page [{next}]", next);
54+
while (!string.IsNullOrWhiteSpace(media?.Paging?.Next))
55+
{
56+
var next = media?.Paging?.Next;
57+
var count = media?.Data?.Count;
58+
_logger.LogInformation("Getting next page [{next}]", next);
5159

52-
media = await _api.GetMediaListAsync(next).ConfigureAwait(false);
60+
media = await _api.GetMediaListAsync(next).ConfigureAwait(false);
5361

54-
_logger.LogInformation("next media response returned with [{count}] records ", count);
62+
_logger.LogInformation("next media response returned with [{count}] records ", count);
5563

56-
Media.Add(media);
64+
Media.Add(media);
65+
}
5766
}
58-
}
5967

60-
Code = code;
61-
State = state;
62-
UserInfo = response.User;
63-
HttpContext.Session.Set(Strings.SessionKey, response);
68+
Code = code;
69+
State = state;
70+
UserInfo = response.User;
71+
HttpContext.Session.Set(Strings.SessionKey, response);
72+
73+
Message = $"{UserInfo.Username} has connected to Instagram successfully.";
74+
}
75+
catch (InstagramOAuthException ex)
76+
{
77+
Message = $"InstagramOAuthException! {ex.Message} ";
78+
_logger.LogError(ex, "Instagram OAuth error - instagram response message : [{message}] error_type : [{type}] error_code : [{code}] fb_trace [{fbTrace}]", ex.Message, ex.ErrorType, ex.ErrorCode, ex.FbTraceId);
79+
}
80+
catch (InstagramApiException ex)
81+
{
82+
Message = $"InstagramApiException! {ex.Message} ";
83+
_logger.LogError(ex, "Instagram API error - instagram response message : [{message}] error_type : [{type}] error_code : [{code}] error_sub_code : [{subCode}] fb_trace [{fbTrace}]", ex.Message, ex, ex.StackTrace, ex.Message, ex.ErrorType, ex.ErrorCode, ex.ErrorSubcode, ex.FbTraceId);
84+
}
85+
catch (InstagramException ex)
86+
{
87+
Message = $"InstagramException! {ex.Message} ";
88+
_logger.LogError(ex, "General Instagram error - instagram response message : [{message}] error_type : [{type}] error_code : [{code}] fb_trace [{fbTrace}]", ex.Message, ex, ex.StackTrace, ex.Message, ex.ErrorType, ex.ErrorCode, ex.FbTraceId);
89+
}
90+
catch (Exception ex)
91+
{
92+
Message = $"Exception! {ex.Message} ";
93+
_logger.LogError(ex, "Unknown exception calling with message [{message}] and exception [{ex}] and stack [{stack}]", ex.Message, ex, ex.StackTrace);
94+
}
6495

6596
return Page();
6697
}

samples/Web/readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Ensure `https://localhost:5001/auth/oauth` is a valid `redirect_url`
4545
cd path/to/samples/Web/
4646
```
4747

48-
**Run `dotnet` on port 5000**
48+
**Run `dotnet` on port 5001**
4949

5050
```powershell
5151
dotnet run
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Runtime.Serialization;
4+
using Solrevdev.InstagramBasicDisplay.Core.Instagram;
5+
6+
namespace Solrevdev.InstagramBasicDisplay.Core.Exceptions
7+
{
8+
/// <summary>
9+
/// Represent errors that occur while calling a Instagram API and getting a specific IGApiException type exception returned
10+
/// </summary>
11+
[Serializable]
12+
[SuppressMessage("Design", "RCS1194", Justification = "No caught exception to wrap")]
13+
public class InstagramApiException : InstagramException
14+
{
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="InstagramOAuthException"/> class.
17+
/// </summary>
18+
public InstagramApiException()
19+
{
20+
}
21+
22+
public InstagramApiException(InstagramError error) : base(error.Message)
23+
{
24+
ErrorType = error.Type;
25+
ErrorCode = error.Code;
26+
ErrorSubcode = error.ErrorSubcode;
27+
FbTraceId = error.FbTraceId;
28+
}
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="InstagramOAuthException"/> class.
32+
/// </summary>
33+
/// <param name="message">The message.</param>
34+
/// <param name="errorType">Type of the error.</param>
35+
/// <param name="errorCode">Code of the error.</param>
36+
/// <param name="errorSubcode">Subcode of the error.</param>
37+
public InstagramApiException(string message, string errorType = default, int errorCode = default, int errorSubcode = default)
38+
: base(message, errorType, errorCode, errorSubcode)
39+
{
40+
}
41+
42+
/// <summary>
43+
/// Initializes a new instance of the <see cref="InstagramOAuthException"/> class.
44+
/// </summary>
45+
/// <param name="message">The message.</param>
46+
/// <param name="innerException">The inner exception.</param>
47+
public InstagramApiException(string message, Exception innerException)
48+
: base(message, innerException)
49+
{
50+
}
51+
52+
/// <summary>
53+
/// Initializes a new instance of the <see cref="InstagramOAuthException"/> class.
54+
/// </summary>
55+
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
56+
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
57+
/// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is null. </exception>
58+
/// <exception cref="T:System.Runtime.Serialization.SerializationException">The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0). </exception>
59+
protected InstagramApiException(SerializationInfo info, StreamingContext context)
60+
: base(info, context)
61+
{
62+
}
63+
}
64+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Runtime.Serialization;
4+
using System.Text.Json.Serialization;
5+
using Solrevdev.InstagramBasicDisplay.Core.Instagram;
6+
7+
namespace Solrevdev.InstagramBasicDisplay.Core.Exceptions
8+
{
9+
/// <summary>
10+
/// Represent general errors that occur while calling a Instagram API.
11+
/// </summary>
12+
[Serializable]
13+
public class InstagramException : Exception
14+
{
15+
/// <summary>
16+
/// Gets or sets the type of the error.
17+
/// </summary>
18+
/// <value>The type of the error.</value>
19+
[JsonPropertyName("type")]
20+
public string ErrorType { get; set; }
21+
22+
/// <summary>
23+
/// Gets or sets the code of the error.
24+
/// </summary>
25+
/// <value>The code of the error.</value>
26+
[JsonPropertyName("code")]
27+
public int? ErrorCode { get; set; }
28+
29+
/// <summary>
30+
/// Gets or sets the error subcode.
31+
/// </summary>
32+
/// <value>The code of the error subcode.</value>
33+
[JsonPropertyName("error_subcode")]
34+
public int? ErrorSubcode { get; set; }
35+
36+
[JsonPropertyName("fbtrace_id")]
37+
public string FbTraceId { get; set; }
38+
39+
/// <summary>
40+
/// Initializes a new instance of the <see cref="InstagramException"/> class.
41+
/// </summary>
42+
public InstagramException()
43+
{
44+
}
45+
46+
public InstagramException(InstagramError error) : base(error.Message)
47+
{
48+
ErrorType = error.Type;
49+
ErrorCode = error.Code;
50+
ErrorSubcode = error.ErrorSubcode;
51+
FbTraceId = error.FbTraceId;
52+
}
53+
54+
/// <summary>
55+
/// Initializes a new instance of the <see cref="InstagramException"/> class.
56+
/// </summary>
57+
/// <param name="message">The message.</param>
58+
public InstagramException(string message)
59+
: base(message)
60+
{
61+
}
62+
63+
/// <summary>
64+
/// Initializes a new instance of the <see cref="InstagramException"/> class.
65+
/// </summary>
66+
/// <param name="message">The message.</param>
67+
/// <param name="errorType">Type of the error.</param>
68+
public InstagramException(string message, string errorType)
69+
: this(string.Format(CultureInfo.InvariantCulture, "({0}) {1}", errorType ?? "Unknown", message))
70+
{
71+
ErrorType = errorType;
72+
}
73+
74+
/// <summary>
75+
/// Initializes a new instance of the <see cref="InstagramException"/> class.
76+
/// </summary>
77+
/// <param name="message">The message.</param>
78+
/// <param name="errorType">Type of the error.</param>
79+
/// <param name="errorCode">Code of the error.</param>
80+
public InstagramException(string message, string errorType, int errorCode)
81+
: this(string.Format(CultureInfo.InvariantCulture, "({0} - #{1}) {2}", errorType ?? "Unknown", errorCode, message))
82+
{
83+
ErrorType = errorType;
84+
ErrorCode = errorCode;
85+
}
86+
87+
/// <summary>
88+
/// Initializes a new instance of the <see cref="InstagramException"/> class.
89+
/// </summary>
90+
/// <param name="message">The message.</param>
91+
/// <param name="errorType">Type of the error.</param>
92+
/// <param name="errorCode">Code of the error.</param>
93+
/// <param name="errorSubcode">Subcode of the error.</param>
94+
public InstagramException(string message, string errorType, int errorCode, int errorSubcode)
95+
: this(message, errorType, errorCode)
96+
{
97+
ErrorSubcode = errorSubcode;
98+
}
99+
100+
/// <summary>
101+
/// Initializes a new instance of the <see cref="InstagramException"/> class.
102+
/// </summary>
103+
/// <param name="message">The message.</param>
104+
/// <param name="innerException">The inner exception.</param>
105+
public InstagramException(string message, Exception innerException)
106+
: base(message, innerException)
107+
{
108+
}
109+
110+
/// <summary>
111+
/// Initializes a new instance of the <see cref="InstagramException"/> class.
112+
/// </summary>
113+
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
114+
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
115+
/// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is null. </exception>
116+
/// <exception cref="T:System.Runtime.Serialization.SerializationException">The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0). </exception>
117+
protected InstagramException(SerializationInfo info, StreamingContext context)
118+
: base(info, context)
119+
{
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)