Microsoft-Graph SDK for .NET is not returning odata.nextLink after upgrading from v1.19 to latest - microsoft-graph-api

When I make a request to Graph API I get in the response the "#odata.nextLink", if I use .NET SDK v1.19 I still get the value in the AdditionalData and there is also a NextPageRequest property which has an instance to make a request to the next page. But after upgrading to the latest version 4.53, using the same query I get the response with the NextPageRequest instance but not the "#odata.nextLink" in the AdditionalData.
I took a look at the release notes and there is nothing mentioning this change.
I could take the info from the NextPageRequest but I would like to understand what is actually happening here.

I found the official document to recommend using PageIterator for Paging, so I checked the source code for different version and I think this can answer your question in some terms. I mean this is recommended by Microsoft and the way it used to get next page value should explain something....
And this is what I found in SDK version
dynamic page = _currentPage;
// There are more pages ready to be paged.
if (page.NextPageRequest != null)
{
Nextlink = page.NextPageRequest.GetHttpRequestMessage().RequestUri.AbsoluteUri;
return true;
}
And this is what I see in V1.19
if (_currentPage.AdditionalData.TryGetValue("#odata.nextLink", out var value))
{
Nextlink = value as string;
return true;
}

I found the reason for this issue, commit: https://github.com/microsoftgraph/msgraph-sdk-dotnet/commit/834549348081869cc4b97d9ead6dbbf12a516264?diff=split class GraphServiceUsersCollectionResponse
[JsonPropertyName("#odata.nextLink")]
public string NextLink { get; set; }
The addition of the above property removed the odata.nextLink from the AdditionalData dictionary. This is an old commit, I'm not sure why this has not been reported as a breaking change.
I'm going to open a ticket directly to the project.

Related

Async Function Fails when called as part of a Constructor

I'm rather new to Blazor, but I am currently trying to get access to some classes from within a class library that I've created and deployed as a Nuget package. As background, the Nuget package is an Api library, which allows me to talk to a webservice (I don't know if this is relevant or not). However, every time I go to the page where I'm testing, the page never loads and instead I left looking at the browser loading circle until I navigate away or close the application. During my testing here, it seems like it's the #inject call of my interface into the Blazor component which is causing the issue as when I remove it and try to load the page normally, the page does so.
So to demonstrate what I have setup, here is where I've added the Singletons to the DI:
builder.Services.AddSingleton<IApiConfigHelper, ApiConfigHelper>();
builder.Services.AddSingleton<IApiHelper, ApiHelper>();
builder.Services.AddSingleton<ISystemEndpoint, SystemEndpoint>();
Then on the blazor page, I have the following declarations at the top of my page:
#using Library.Endpoints
#using Library.Models
#page "/"
#inject ISystemEndpoint _systemEndpoint
Now I am leaning towards is this something to do with the Nuget package and using it with DI. I have tested the library away from this project (In a console application) and can confirm it's working as it should.
I have also created a local class library as a test to, to see if I could inject a data access class into the page and I can confirm that this works without an issue, which suggests to me that DI is working, just not with my Nuget package.
I did have a look into CORS, given that the Nuget package is accessing an external domain, and setup the following simple CORS policy in the app:
builder.Services.AddCors(policy =>
{
policy.AddPolicy("OpenCorsPolicy", opt =>
opt.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
Which is added to the app after the AddRouting call like so:
app.UseCors("OpenCorsPolicy");
However again, this wasn't the solution so if anyone is able to point me in the right direction with where I may be going wrong with this or offer any advice, I would be most grateful.
EDIT 1 - Provides details #mason queried
Regarding SystemEndpoint, the constructor is being injected with 2 things, as below:
public SystemEndpoint(IApiHelper apiHelper, IOptions<UriConfigModel> uriOptions)
{
_apiHelper = apiHelper;
_uriOptions = uriOptions.Value;
}
My Nuget Library is dependant on the following:
Azure.Identity
Azure.Security.KeyVault.Secrets
Microsoft.AspNet.WebApi.Client
Microsoft.Extensisons.Options.ConfigurationExtensions
EDIT 2 - Doing some further testing with this I have added a simple Endpoint class to my Nuget library, which returns a string with a basic message, as well as returning the values of the 2 UriConfig properties as below. I added this test to 1) sanity check that my DI was working correctly, and 2) check the values that are being assigned from appsettings to my UriConfig Object.
public class TestEndpoint : ITestEndpoint
{
private readonly IOptions<UriConfigModel> _uriConfig;
public TestEndpoint(IOptions<UriConfigModel> uriConfig)
{
_uriConfig = uriConfig;
}
public string TestMethod()
{
return $"You have successfully called the test method\n\n{_uriConfig.Value.Release} / {_uriConfig.Value.Version}";
}
}
However when adding in the dependency of IApiHelper into the Ctor, the method then breaks and fails to load the page. Looking into ApiHeloer, the Ctor has a dependency being injected into it of IApiConfigHelper. Looking at the implementation, the Ctor of ApiConfigHelper is setting up the values and parameters of the HttpClient that should make the REST calls to the external Api.
Now I believe what is breaking the code at this point is a call I'm making to Azure Key Vault, via REST, to pull out the secret values to connect to the Api. The call to KeyVault is being orchestrated via the following method, making use of the Azure.Security.KeyVault.Secrets Nuget Package, however I assume that at the heart of it, it's making a REST call to Azure on my behalf:
private async Task<KeyVaultSecret> GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = await _secretClient.GetSecretAsync(secretName);
return result.Value;
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
So that's where I stand with this at the moment. I still believe it could be down to CORS, as it seems to be falling over when making a call to an external service / domain, but I still can say 100%. As a closing thought, could it be something as simple as when I call call the above method, it's not being awaited????
So after persisting with this it seems like the reason it was failing was down to "awaiting" the call to Azure KeyVault, which was happening indirectly via the constructor of ApiConfigHelper. The resulting method for getting KeyVault value is now:
private KeyVaultSecret GetKeyVaultValue(string secretName = "")
{
try
{
if (_secretClient is not null)
{
var result = _secretClient.GetSecret(secretName);
if (result is not null)
{
return result.Value;
}
}
}
catch (ArgumentException ae)
{
Console.WriteLine(ae.Message);
}
catch (Azure.RequestFailedException rfe)
{
Console.WriteLine(rfe.Message);
}
return new(secretName, "");
}
I am now able to successfully make calls to my library and return values from the Api it interacts with.
I can also confirm that this IS NOT a CORS issue. Once I saw that removing the await was working, I then removed the CORS policy declarations from the service and the app in my Blazor's start-up code and everything continued to work without an issue.
As a final note, I must stress that this is only seems an issue when using the library with Blazor (possibly webApi projects) as I am able to use the library, awaiting the Azure call just fine in a console application.

Not able to resolve HttpResponseException in .Net 5 Web Api

I am creating an Web Api using .Net Core 5. I want to implement error handling and return the response with the error. I found a article from Microsoft which suggested the following code. When I am implementing that code "HttpResponseException" is not found and I get a suggestion to install the nuget package for Microsoft.Aspnet.WebApi.Core. Once install it conflicting with the existing nuget package. I got this message "Microsoft.AspNet.WebApi.Core 5.2.8 was restored using .netFramework, Version=4.6.1..." As I said, I am trying to handling error as per best practices and my finding was to use Microsoft documentation in which it mentioned to use HttpResponseException (applicable scenario). If HttpResponseException is obsolete for .Net 5 what other option we have? Basically in the response when error occured, I want to send the customize ReasonPhrase.
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("No product with ID = {0}", id)),
ReasonPhrase = "Product ID Not Found"
};
throw new HttpResponseException(resp);
}
return item;
}

Can't save the data to OData controller after updating to Microsoft.AspNet.WebApi.OData 5.3.x

Not sure what's changed exactly and causing this partial error (since it's not happening with retrieving the data) but after updating Microsoft.AspNet.WebApi.OData library to 5.3.x version, breeze has an issue with saving the data to OData controller. It's about the urls of the batch requests.
It can be reproduced with breeze's its own sample OData project as well;
http://www.breezejs.com/samples/breeze-web-api-odata
If you look at ExecuteRequestMessagesAsync method of the BatchHandler class, RequestUri property of the items contain OData route prefix two times.
Microsoft.AspNet.WebApi.OData library 5.2.2 url
http://localhost:55802/odata/TodoItems
Microsoft.AspNet.WebApi.OData library 5.3.1 url
http://localhost:55802/odata/odata/TodoItems
Any ideas how to solve this issue?
breeze version: 1.5.1
Oh joy. Microsoft has changed their Web API OData implementation ... again
Thanks for digging in, #coni2k, and identifying the problem.
Fortunately, you do NOT have to patch Breeze. We deliberately expose the getRoutePrefix method so you can change it externally yourself to meet your needs.
In the following example, I've incorporated your suggestion in the body of the method.
var adapter = breeze.config.getAdapterInstance('dataservice', 'webApiOdata');
adapter.getRoutePrefix = getRoutePrefix531; // plug-in alternative for THIS adapter instance.
function getRoutePrefix531(dataService) {
// Copied from breeze.debug and modified for Web API OData v.5.3.1.
if (typeof document === 'object') { // browser
var parser = document.createElement('a');
parser.href = dataService.serviceName;
} else { // node
parser = url.parse(dataService.serviceName);
}
// THE CHANGE FOR 5.3.1: Add '/' prefix to pathname
var prefix = parser.pathname;
if (prefix[0] !== '/') {
prefix = '/' + prefix;
} // add leading '/' (only in IE)
if (prefix.substr(-1) !== '/') {
prefix += '/';
} // ensure trailing '/'
return prefix;
};
As I write we're not sure how to detect which version of Web API OData you're talking to which makes it difficult for us to say a priori which version of getRoutePrefix is right for your application.
Hope to figure this out eventually. Don't dare change the default to this new version because that would break every existing app that has to talk to an older version of Web API OData. Not sure how we can win this game. We'll look at it. Frustrating for now.

NullReferenceException in DotNetOpenAuth

I tracked down what appears to be a defect causing a NullReferenceException in my ASP.NET MVC app. Did I break this, or should this break DotNetOpenAuth for the majority of installations?
I get this:
[NullReferenceException: Object reference not set to an instance of an object.]
DotNetOpenAuth.AspNet.OpenAuthSecurityManager.GetUsername(HttpContextBase context) +27
DotNetOpenAuth.AspNet.OpenAuthSecurityManager.RequestAuthentication(String returnUrl) +341
Controllers.LoginController.Index() +226
Here's the Index() method. Note it currently returns User in production and OK in development. Those are obviously temporary. The commented code is from the last URL I referenced below, where I reduced the amount of code between me and the problem. The stack trace was still similar, though.
public virtual ActionResult Index()
{
if (HttpContext == null) return Content("HttpContext");
if (HttpContext.User == null) return Content("User");
if (HttpContext.User.Identity == null) return Content("Identity");
return Content("OK");
new OpenAuthSecurityManager(HttpContext, s_fbClient, OAuthDataProvider.Instance)
.RequestAuthentication(Url.Action(Actions.Callback()));
// OAuthWebSecurity
// .RequestAuthentication("facebook", Url.Action(Actions.Callback()));
return null;
}
The exception arises because HttpContext.User is null. Here's the DotNetOpenAuth.AspNet library source for that failing method.
private static string GetUsername(HttpContextBase context) {
string username = null;
if (context.User.Identity.IsAuthenticated) {
username = context.User.Identity.Name;
}
return username ?? string.Empty;
}
User has apparently has been nullable ever since IIS Integrated Mode was available. This explains why I don't see it in development, as I'm running IIS Express with defaults, but it doesn't explain why I can't find any information about the defect. Integrated mode was released in 2007, and DotNetOpenAuth is still maintained. Microsoft docs say this about the setting:
Classic Mode: Use this mode only when the applications in the
application pool cannot run in Integrated mode.
I must be missing something, because it seems like everyone should have this issue.
Have I NuGet'ed a non-maintained library somehow? It seems odd, since it shows it was just updated a week ago. But when I follow the documentation link from NuGet, the source code I arrive at doesn't seem to even have an AspNet namespace, where my exception arose.
EDIT: The only related package I use is currently DotNetOpenAuth.AspNet (and it has 8 dependencies), last published less than a week ago. I've tried other packages also. I don't need SimpleAuth or any WebMatrix jazz. In the process of resolving this, I tried switching libraries as described http://techblog.dorogin.com/2013/06/Microsoft.AspNet.WebPages.OAuth.html
EDIT: Logged a defect related to this, but it seems this library may not be maintained. https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/317#issuecomment-29580565
EDIT: Pending stack trace, it may be the same defect as MVC 5 Owin Facebook Auth results in Null Reference Exception
This is indeed a defect in the DotNetOpenAuth.AspNet code. Unfortunately that DotNetOpenAuth library is no longer maintained. Modifying the source with:
private static string GetUsername(HttpContextBase context) {
string username = null;
if (context.User != null && context.User.Identity.IsAuthenticated) {
username = context.User.Identity.Name;
}
return username ?? string.Empty;
}
Of course does the trick.

breeze 1.3.6 cannot get metadata

The Devcurry hottowelsignalr example which utilizes breeze works fine until i upgraded breeze via nuget to 1.3.6.
F12 in chrome shows that it is trying to retrieve metadata from http://localhost/api/breeze/Metadata whereas previously it would use
http://localhost/OnlineCollaborationWithSignalR/api/breeze/Metadata. Which is the correct location of the call. It appears that the path of the application is missing from the root. i.e. (OnlineCollaborationWithSignalR)
Update
noticed the release notes for 1.3.1. And subsequently changed the routing to
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
}
and the app/viewmodels/home.js to
// service name is route to the Web API controller
var serviceName = 'breeze/Breeze';
from
// service name is route to the Web API controller
var serviceName = 'api/Breeze';
It still fails with the same error as noted above.
Can you confirm that the API responds accordingly?
Based on your routeTemplate, it should be.
http://localhost/breeze/Breeze/Metadata
UPDATE:
Yes, if your configuration is such. It should be
http://localhost/OnlineCollaborationWithSignalR/breeze/Breeze/Metadata
I am not familiar with the specific project, but I downloaded and updated it. I then made the changes that you specified, and I am getting a response from the API. Although I am getting an error (most likely unrelated), it is in fact routed properly.
For the record, the error I get is:
"The provider for invariant name 'System.Data.SqlClient' is specified multiple times in the application configuration. The invariant name must be unique for each configured provider."

Resources