Showing posts with label .NET 8. Show all posts
Showing posts with label .NET 8. Show all posts

Customizing ASP.NET Core Identity Razor Pages via Scaffolding (in an MVC app)

If you are trying to add the built-in ASP.NET Core Identity Authentication scaffolding to an MVC application, you will notice that traversing between MVC controller actions and simple requests to Razor Pages doesn't work as transparently as you might expect.

To scaffold the Identity Razor Pages so that we can customize them, we first do the following:

Right-click the project and select, "Add >> New Scaffolded Item..."


Select the Identity scaffolding template


From the next screen, select the Razor pages that you would like to scaffold for overriding/customizing


Once you have the scaffolded pages you will need to run this from the Developer console to generate the necessary ASP.NET Core Identity database tables: dotnet ef database update

If you want to be able to get to the Login page from an MVC controller, you will; need to an [AllowAnonymous] attribute to the Login.cshtml Razor Page code-behind class, and you will need to call the Login page while referencing the Area path in which it is located within the project file structure.

The folder structure for an MVC project overriding the built-in Identity auth Razor pages via scaffolding


An MVC logout link that you want to redirect to an overridden Identity auth Login Razor page might look like this in your _Layout view: 

The view link in the MVC's _Layout.cshtml

The controller redirect (note: new { area = "Identity" }); "/Pages/" is ignored for some reason..

In order to reach the Login Razor page (when not being authenticated) you will need to add the [AllowAnonymous] attribute to the LoginModel class a la:

With [AllowAnonymous] users will be able to reach the overridden Identity Login page from any controller action in your MVC application


An that's it. You can customize all of the front-end and back-end code of the scaffolded Identity Razor pages. This is an example of customization that uses the extRS service to create SSRS user accounts when a user registers a new Identity account for the app:

Scaffolding the Identity Razor pages allows you focus only on the authentication design and logic that you need to customize 


I'm not sure why but much of this is not documented (and what is documented is not easily found).

Lastly, do keep in mind that in order to override certain items like "_LoginPartial", you need to rename to view, as was done with "_LoginPartial2.cshtml" in the extRS project, for example.


References: 

https://github.com/sonrai-LLC/extRS/tree/main/ExtRS.Portal

https://extrs.net




    

    

You need to eat your own dog food

I recently realized how useless my custom .NET method, "Sonrai.ExtRS.ReferenceDataService.GetGoogleNews(string searchTerm)", really was.

I had referenced Sonrai.ExtRS via Nuget and began trying to use it to improve the display in scrolling news links, in another application I maintain, tickertapes.net.   

It was useless! I mean a complete waste of code. It returned potentially useful data related to the searchTerm parameter- data from the GoogleNews API. But that is about it. It literally returned the entire XML response. 🤦‍♂️


This was the raw oil, but not the refined gasoline a consumer of a Nuget package expects...


I was parsing all this XML in the tickertapes.net app (why I do not know or care to remember), and so all the work necessary to wrangle the XML response to produce the needed news link collection was on the consumer of the Sonrai.ExtRS Nuget package- not good!

What the Nuget library should do, is all the work. What it should return, to be actually useful, is something structured like a collection of strings, each one representing a single news article, with the news headline being the text for the news article link.

And so I moved back to Sonrai.ExtRS to correct this unfortunate oversight.  

  public static async Task<List<string>> GetGoogleNewsWithLinks(string search)  
  {  
    HttpClient client = new HttpClient();  
    var content = await client.GetStringAsync(string.Format("https://news.google.com/rss/search?q={0}", search));  
    var parser = new HtmlParser();  
    var document = parser.ParseDocument(content);  
    var newsItems = document.All.Where(m => m.LocalName == "title").ToList();  
    var linkItems = document.All.Where(x => x.LocalName == "link").ToList();  
    var newsLinkItems = new List<string>();  
    for (int i = 0; i < newsItems.Count; i++)  
    {  
      newsLinkItems.Add("<a href='" + linkItems[i].NextSibling!.NodeValue + "' target='_blank'>" + newsItems[i].InnerHtml + "</a>");  
    }  
    return newsLinkItems;  
  }  


And this provides the Nuget client with something it can actually use, "turnkey"/out-of-the-box.

A list of HTML links which we can actually use; this is more like it.


The moral of the story is one that is as old as business and manufacturing: you must eat your own dog food. If there are problems or areas that need attention it is best that you find this information out before your product is released into the wild and a customer discovers the bug (or in this case, the uselessness), thus sullying your reputation as a business and software provider.

Test, test, test- always unit and/or integration test every user interaction and data movement for every story/path imaginable or support-able.

But there is nothing that replaces simply using your own product the way it is used by real users. And really using it for the purpose it was made. What you discover may help you shore up previously unknown problems and/or inspire you to make something useful that you would never otherwise think of, unless you were thinking from a user's perspective.


Reference: https://www.nuget.org/packages/Sonrai.ExtRS


PS: Imagine if James Newton-King never used NewtonSoft.Json for his own de/serializations? Or if Stack Overflow never used Dapper for the SO app/site? You need to use your creations right at the ground level (as a user/client) to verify that the functionality you designed in the abstract exactly matches how things will play out in concrete reality. Thoughts.. 💭

How to use .NET User Secrets in MSTest classes

When developing unit and integration tests, we don't necessarily want to share the secret keys and values we use as credentials for APIs, databases, etc.

So similarly to how we implement User Secret functionality in Program.cs, we can implement User Secrets and set test class variables that use the secrets via the MSTest classes' constructor (ReferenceDataTests() below):

  public static string upsId = "";  
  public static string upsSecret = "";  
  private IConfiguration _configuration { get; }  
  
  public ReferenceDataTests()  
  {  
    // set your API ids and secrets in UserSecrets (right-click project: "Manage User Secrets")  
    var builder = new ConfigurationBuilder()  
      .AddUserSecrets<ReferenceDataTests>();  
    _configuration = builder.Build(); 
    
    var secretVals = _configuration.GetChildren().ToList();  
    upsId = secretVals.Where(x => x.Key == "upsId").First().Value!;  
    upsSecret = secretVals.Where(x => x.Key == "upsSecret").First().Value!;   
  }