Showing posts with label .Net. Show all posts
Showing posts with label .Net. Show all posts

3/18/2022

ASP.NET Core Middleware to Update Response Body

I needed a quick way to update a mix of static HTML files and Razor views to change a message common to a lot of pages. In the example here I will update the Copyright message in HTML pages.

As usual I searched the web and found many examples, none that worked the way I needed (or worked at all!), but I did find an assortment of "bits" scattered throughout StackOverflow that helped me build a working solution.

At first glance this should have been a textbook example of ASP.NET Core middleware. Intercept the outgoing response, update the body HTML and forward on through the middleware pipeline. The first issue was the Stream object used by Response.Body object, its an Microsoft.WebTools.BrowserLink.Net.ScriptInjectionFilterStream object that does not support Seek. The first "bit" that I found in StackOverflow was to replace the Response.Body steam with my own stream, in particular a MemoryStream. Then we can send the Response object on down the middleware pipeline using next.Invoke. When the Response comes back to us though the pipeline we need to extract the HTML text from our custom stream object, do our updates to the HTML and finally get the original kind of stream back into the Response.Body with our changes.

The next problem was that I could not find a way to clear or reset the stream that represented the returned Body content. I was getting both the original page plus the updated page, i.e. my data was getting appended. .SetLength(0) and .Seek(0, SeekOrigin.Begin) did not work. ("Specified method is not supported"). StackOverflow to the rescue again. A quick solution was to recreate an empty stream object. ( context.Response.Body = new MemoryStream(); )

Remember! The order of middleware is important. If you want this to process your JavaScript, CSS and static HTML files, then add this before app.UseStaticFiles(). Most likely order:

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.Use(  the code below  );
            app.UseRouting();
            app.UseAuthorization();

 

So here's the solution as an inline "app.Use()" block added to Startup.cs or Program.cs (.NET 6). It replaces the Copyright date from "2021" to the current year.

 

Note: Code tested in .NET Core 3.1 and .NET 6.0 using Visual Studio 2022.


app.Use(async (context, next) =>
{
    using (var replacementStream = new MemoryStream())
    {
        // preprocessing the Body
        // replace {Microsoft.WebTools.BrowserLink.Net.ScriptInjectionFilterStream}
        // with a MemoryStream (seekable!)
        System.IO.Stream originalStream = context.Response.Body;
        context.Response.Body = replacementStream;

        // do any preprocessing of the Request or Response object here
        // none needed for this example

        // move on down the pipeline, but with our custom stream
        await next.Invoke();

        // we are now back from the rest of the pipeline and on the way out (TTN)

        // postprocessing the Body

        // read the returned Body
        replacementStream.Seek(0, SeekOrigin.Begin);
        using (var bufferReader = new StreamReader(replacementStream))
        {
            string body = await bufferReader.ReadToEndAsync();

            // Is this a web page?
            if (context.Response.ContentType.Contains("text/html"))
            {
                // make changes to the returned HTML
                // set the copyright to the current year
                body = body.Replace("© 2021", "© " + DateTime.Now.Year.ToString());
            }

            // discard anything in the existing body.                        
            context.Response.Body = new MemoryStream();

            // write the updated Body into the Response using our custom stream
            context.Response.Body.Seek(0, SeekOrigin.Begin);
            await context.Response.WriteAsync(body);

            // extract the Body stream into the original steam object
            //   in effect, covert from MemoryStream to 
            //   {Microsoft.WebTools.BrowserLink.Net.ScriptInjectionFilterStream}
            context.Response.Body.Position = 0;
            await context.Response.Body.CopyToAsync(originalStream);

            // now send put the original stream back into the Response
            // and exit to the rest of the middleware pipeline
            context.Response.Body = originalStream;
        }
    }
});


 

Here's the same solution as a Middleware class.

  1. Add the class below.
  2. Update Startup.cs or Program.cs (.NET 6) to add an app.UseReplaceText line.

 Startup.cs or Program.cs:

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseReplaceText();  // custom middleware
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => ....

The class:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Threading.Tasks;

namespace WebAppToDemoMiddleware
{
    public class ReplaceTextMiddleware
    {
        private readonly RequestDelegate _next;

        public ReplaceTextMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        // 
        public async Task InvokeAsync(HttpContext context)
        {
            using (var replacementStream = new MemoryStream())
            {
                // preprocessing the Body
                // replace {Microsoft.WebTools.BrowserLink.Net.ScriptInjectionFilterStream}
                // with a MemoryStream (seekable!)
                System.IO.Stream originalStream = context.Response.Body;
                context.Response.Body = replacementStream;

                // do any preprocessing of the Request or Response object here
                // none needed for this example

                // move on down the pipeline, but with our custom stream
                await _next(context);

                // we are now back from the rest of the pipeline and on the way out

                // postprocessing the Body

                // read the returned Body
                replacementStream.Seek(0, SeekOrigin.Begin);
                using (var replacementStreamReader = new StreamReader(replacementStream))
                {
                    string body = await replacementStreamReader.ReadToEndAsync();

                    //if (body.Contains("Welcome"))
                    // Is this a web page?
                    if (context.Response.ContentType.Contains("text/html"))
                    {
                        // make changes to the returned HTML
                        // set the copyright to the current year
                        body = body.Replace("© 2021", "© " + DateTime.Now.Year.ToString());
                    }

                    // discard anything in the existing body.                        
                    context.Response.Body = new MemoryStream();

                    // write the updated Body into the Response using our custom stream
                    context.Response.Body.Seek(0, SeekOrigin.Begin);
                    await context.Response.WriteAsync(body);

                    // extract the Body stream into the original steam object
                    //   in effect, covert from MemoryStream to 
                    //   {Microsoft.WebTools.BrowserLink.Net.ScriptInjectionFilterStream}
                    context.Response.Body.Position = 0;
                    await context.Response.Body.CopyToAsync(originalStream);

                    // now send put the original stream back into the Response
                    // and exit to the rest of the middleware pipeline
                    context.Response.Body = originalStream;
                }
            }

        }
    }

    // Add an extension class so we can use this as app.UseReplaceText()
    public static class RequestCultureMiddlewareExtensions
    {
        public static IApplicationBuilder UseReplaceText(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ReplaceTextMiddleware>();
        }
    }
}
..

8/03/2020

How to count lines of code using PowerShell

I've got a project... scattered through a number of folders are a bunch of C# files. How many lines of code do I have? (Bragging rights you know...)

You could open the project in Visual Studio and use the Analyze, Calculate Code Metrics. But that requires Visual Studio, and does not give me all the options I want. (But in many ways is better! Lines by file and by method are available there.)

I wanted to do this on raw files using PowerShell, so assuming your project is in C:\myProject and you only wanted to count ".cs" files...

  dir C:\myProject -Recurse *.cs | Get-Content | measure

Get-Content returns the lines from the file as an array, and Measure-Object counts things.

Want to count characters too?

  dir C:\myProject -Recurse *.cs |
    Get-Content |
      measure
-Line -Character

Your boss won't let you take credit for blank lines?

  dir C:\myProject -Recurse *.cs |
    Get-Content |
      where { $_.trim() -ne "" } |
        measure
-Line -Character

Your boss won't let you take credit for blank lines or comments? (I would pay extra for good comments!)

  dir C:\myProject -Recurse *.cs |
    Get-Content |
      where { $_.trim() -ne "" -and $_.trim() -notlike "//*" } |
        measure
-Line -Character

    (Sorry, but in this quick little exercise I did not deal with /* and */ block comments, only // comments.)

Want to include multiple file types?

  dir C:\myProject -Recurse -Include *.cs, *.cshtml |
    Get-Content |
      where { $_.trim() -ne "" -and $_.trim() -notlike "//*" } |
        measure
-Line -Character


Boy I've been working hard... and using wizards to write code... 😉

   Lines Words Characters Property
   ----- ----- ---------- --------
    2563           117084

7/20/2019

The controller for path '/' was not found or does not implement IController #VisualStudio #MVC



The controller for path '/' was not found or does not implement IController


Some error messages are no help at all!


When running in Debug mode Visual Studio reports:

An exception of type 'System.Web.HttpException' occurred in System.Web.dll but was not handled in user code.
Additional information: Execution of the child request failed. Please examine the InnerException for more information.

The inner exception:

{System.Web.HttpException (0x80004005): The controller for path '/' was not found or does not implement IController.
   at System.Web.Mvc.DefaultControllerFactory.GetControllerInstance


A "forest and trees" problem...


A web search returns many possibilities for the error, but none that applied to my project. It turns out I had one of those "forest and trees" problems! I had typed Html.Action instead of Html.ActionLink in a Layouts page. In effect I was asking the layouts view to load a view that used the layouts view that then loaded the layouts view that loaded the view that used the layouts view… Yup, an endless loop.


The error message was not of much help and sent me on a wild goose chase. Once I realized what was going on I made one small edit and was back in business.



.

10/22/2016

Forest and Trees Problem: "A network-related or instance-specific error occurred while establishing a connection to SQL Server"

 

A "Can't see the tree for the forest" problem.


image_thumb[11]
There's an old phrase, "Can't see the forest for the trees", that in reverse, "Can't see the tree for the forest", applies to a recent "demo fail" of mine.

During a break in a C# class I was delivering last week I typed up a little demo for accessing SQL Server, and got an error. A quick read of the error said that it couldn't find the server, and it hinted at a protocol error.  

Additional information: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)

    image_thumb[12]

 

Seeing the big "Named Pipes" tree standing there… I Binged and Googled and found all kinds of stuff… that did not help.

While the answer to the problem was clearly stated in the message above, "Verify the instance name is correct", I didn't see it as I was looking at all of the other "trees" in that little code forest. The "tree" that I needed to deal with in this case was a C# beginner error of putting a backslash in a string. (I copy and pasted it without looking at it!) The back slash is an escape character to flag the next character as a special code. In this case "\v" is the code for a Vertical Tab. So, I had created a connection string looking for a server named "(localdb)VerticalTab11.0".

image

What made this little error a bit painful was that in this class I had mentioned escape characters in C#, and how to deal with them, at least four times! Oh well…

To solve the problem, escape the escape character ("(localdb)\\v11.0") or mark the entire string as a literal string with the At sign ("con.ConnectionString = @"Data Source=(localdb)\v11.0 …").

   image

For a list of the C# escape characters see this MSDN article:
   https://msdn.microsoft.com/en-us/library/h21280bw.aspx

.

1/07/2015

Still exam procrastinating? Second Shot is back!

 

Time for New Year's resolutions? Or just finish some of last years…

Take any Microsoft Certified Professional (MCP) exam between January 5, 2015, and May 31, 2015. If you don't pass, get a free retake!

All of the Microsoft Certified Solutions Associate (MCSA), Microsoft Certified Solutions Expert (MCSE), Microsoft Certified Solutions Developer (MCSD), and Microsoft Specialist certification exams are eligible. Microsoft Technology Associate (MTA) exams and Microsoft Office Specialist (MOS) exams do not qualify for this promotion.

Details here: https://www.microsoft.com/learning/en-us/second-shot.aspx

 

.

12/07/2014

Just how much is PI in .Net?

 

<Silly blog article>

 

The .Net Framework includes a math library that includes a constant for PI. Just how much is PI?

The MSDN article says:

   image

 

The Visual Studio debugger says:

   image

The Visual Studio Autos and Locals windows say:

   image

And when written from Console.WriteLine:

   image

Or does it?  Reformatted with "R" WriteLine matches the value reported from the debugger.

   image

and for the final test:

   image

 

So who is right? According to Wikipedia the first 50 digits are 3.14159265358979323846264338327950288419716939937510. Based on that number the MSDN documentation describes a more accurate number than returned by Math.PI.

 

From the .Net double documentation: here and here

  • Double-precision numbers store an approximation of a real number.
  • Holds signed IEEE 64-bit (8-byte) double-precision floating-point numbers
  • A Double value has up to 15 decimal digits of precision, although a maximum of 17 digits is maintained internally
  • When you work with floating-point numbers, remember that they do not always have a precise representation in memory.
  • The documentation's examples only have 18 digits, so PI as a double should be apx 3.14159265358979324

 

In the end…

3.14159265358979323846264338327950288419716939937510  Wikipedia
3.14159265358979323846   MSDN documentation for Math.PI
3.14159265358979324        What I would guess based on the documentation for doubles
3.1415926535897931          Returned in Visual Studio debugging tools
3.1415926535897931          Returned by the ToString() conversion through WriteLine formatted with "R"
3.14159265358979             Returned by the ToString() conversion through WriteLine

and just for fun… JavaScript says:
3.141592653589793

 

So…  for the important question… is the value of PI in the documentation for Math.PI wrong?  Smile

   image

 

</Silly blog article>

Now I have to get back to some real work!

.

11/13/2014

.NET 2015–Open Source? Mac? Linux?

 

Got to take a look at this… (nuff said)

Announcing .NET 2015 - .NET as Open Source, .NET on Mac and Linux, and Visual Studio Community

 

Visual Studio Community edition. Free!   ("This is not Express. This is basically Pro.")

http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_2

"This edition of Visual Studio is available at no cost for non-enterprise application development."

.

Interesting stuff coming!

 

.

9/21/2014

Developer Apprentice Program in .NET

 

Know someone who is unemployed who would make a great programmer?

 

MAX Technical Training is doing something quite interesting!  Again!

image

Dislocated workers who live in Hamilton, Butler, Warren and Clermont counties and are not working may be eligible to take Developer Apprentice training in .NET programming. Good candidates include those with a background in IT who want to update their programming skills or those who enjoy math, problem solving and/or puzzles. Musicians and those who enjoy learning foreign languages tend to make good programmers.

The Hamilton Co. session is now going to happen on September 23.  Warren Co. is September 24, and the program is open to Clermont and Butler Co. folks as well. 

When: September 23, 2014 at 10 am
Where: OhioMeansJobs Hamilton County, 1916 Central Parkway, Cincinnati  45214

When: September 24, 2014 from 9 am – 10 am
Where: OhioMeansJobs Warren County, 300 East Silver St., Lebanon, OH  45036

"Interested in becoming a Developer or know someone who is?"

"Whether you are an employer looking hire a .NET programmer or a Job Seeker looking to build a new career - this is the program to meet both your needs."

"This intense 9 week "boot camp" immerses you in 42 intense full-days of training. To be accepted, you must pass two aptitude tests and a screening interview with the program director." 

"This will be MAX's fourth program. All of our past apprentices who have completed the program are successfully employed in developer related positions with Great American Insurance, Paycor, Western and Southern, or Assurex."

For more information: http://www.maxtrain.com/DynamicPage.aspx?DynamicContentID=166

 

.

9/12/2014

Developer Apprentice Program in .NET

 

MAX Technical Training is doing something quite interesting!  Again!

 

"Interested in becoming a Developer or know someone who is?"

"Whether you are an employer looking hire a .NET programmer or a Job Seeker looking to build a new career - this is the program to meet both your needs."

"This intense 9 week "boot camp" immerses you in 42 intense full-days of training. To be accepted, you must pass two aptitude tests and a screening interview with the program director." 

"This will be MAX's fourth program. All of our past apprentices who have completed the program are successfully employed in developer related positions with Great American Insurance, Paycor, Western and Southern, or Assurex."

 

IMPORTANT DATES:

September 17: If you are currently unemployed and live in Hamilton County please attend our Recruitment Fair on September 17 at 9 am.

September 24: For Butler, Warren and Clermont County unemployed residents, please attend our Recruitment Fair on September 24 at 9 am.

For more information: http://www.maxtrain.com/DynamicPage.aspx?DynamicContentID=166

12/29/2013

Is VB.Net dead?

 

Just a random thought at the start of a new year… Is VB.Net dead? Deprecated? Just not interesting to Microsoft Learning?

It seems the newer 204** series Microsoft MOC classes only cover C# (and JS / HTML5). I've also been reading that C# is the only language option in most of the newer certification exams.

On the other hand, it looks like Visual Studio is still treating VB as an equal to C#. There are no new VB and C# language features in VS 2013, but VB is still there. The VB team blog says "We are actively working on the next versions of Visual Basic and C#" (*).

What about VBScript? As of Internet Explorer 11, VBScript is considered deprecated. (MSDN article)

This will be entertaining to watch as many of my web and SharePoint class attendees are still working in VB shops!

Mike

5/11/2011

Get Certified Now! (Exams are going to cost more soon…)

 

The primary Microsoft certification exams have cost $125 for a long time now. Starting July 1st they are going up to $150. You can lock in the current pricing for a while, get discounts from your favoriate MCT (10%) or take an exam or two at a 25% discount while you are at TechEd (details) next week! You can also get your exams as part of a certification bootcamp. (here’s two I’m leading: SharePoint Admin and SharePoint Dev)

See here for details: http://borntolearn.mslearn.net/btl/b/weblog/archive/2011/04/22/so-about-that-price-increase.aspx

 

.

9/28/2010

SharePoint and ASP.NET Vulnerability – Security Update Available

 

Note that this is not just limited to SharePoint. It impacts anything running ASP.NET except for 1.1 SP3.

 

UPDATE!

Out of Band Release to Address Microsoft Security Advisory 2416728

http://blogs.msdn.com/b/sharepoint/archive/2010/09/28/out-of-band-release-to-address-microsoft-security-advisory-2416728.aspx

 

This security update is rated Important for all supported editions of ASP.NET except Microsoft .NET Framework 1.0 Service Pack 3.

See here for versions and details: http://www.microsoft.com/technet/security/bulletin/MS10-070.mspx

 

 

SharePoint developers and server administrators need to follow up on this Microsoft Security Advisory.

Read this ASAP!

http://weblogs.asp.net/scottgu/archive/2010/09/18/important-asp-net-security-vulnerability.aspx

and this:

http://blogs.msdn.com/b/sharepoint/archive/2010/09/21/security-advisory-2416728-vulnerability-in-asp-net-and-sharepoint.aspx

 

“This vulnerability affects Microsoft SharePoint 2010 and Microsoft SharePoint Foundation 2010.  The vulnerability is in ASP.NET. We recommend that all SharePoint 2010 customers apply the workaround as soon as possible”

.

9/20/2010

SharePoint and ASP.NET Vulnerability

 

UPDATE!

Out of Band Release to Address Microsoft Security Advisory 2416728

http://blogs.msdn.com/b/sharepoint/archive/2010/09/28/out-of-band-release-to-address-microsoft-security-advisory-2416728.aspx

 

SharePoint developers and server administrators need to follow up on this Microsoft Security Advisory.

Read this ASAP!

http://weblogs.asp.net/scottgu/archive/2010/09/18/important-asp-net-security-vulnerability.aspx

and this:

http://blogs.msdn.com/b/sharepoint/archive/2010/09/21/security-advisory-2416728-vulnerability-in-asp-net-and-sharepoint.aspx

 

“This vulnerability affects Microsoft SharePoint 2010 and Microsoft SharePoint Foundation 2010.  The vulnerability is in ASP.NET. We recommend that all SharePoint 2010 customers apply the workaround as soon as possible”

.

8/02/2010

Are you an unemployed developer? (Project Phoenix)

 

For complete details: http://sqlblog.com/blogs/arnie_rowland/archive/2010/07/30/like-a-phoenix-rising-from-the-ashes.aspx

 

 

Project Phoenix is inviting unemployed or underemployed developers to propose a software project for a non-profit agency, school, or church. The idea is that we will provide a package of the latest software, tools, and training resources to help you improve your skills, get up to date with current technologies, gain practical experience, potentially earn a recommendation for your efforts, and in general, enjoy the feeling of accomplishing something useful for others. We are not giving out a 'free lunch', just supporting your efforts to personally gain from your own 'sweat equity'.

Project Criteria:

  • Client is a USA IRS 503(c)3 non-profit, school, or church, OR a Canadian CRA registered charity,
  • Solves a problem or satisfies a need for the client,
  • Client desires the project and is supportive,
  • Uses any combination of .NET 4.0, Windows Server 2008, SQL Server 2008, or Windows Phone 7,
  • May be a new software solution, or an upgrade to an existing software solution,
  • Additional consideration given projects that will be posted on Codeplex with a GPL license.

All 24 selected developers will receive the following award benefits:

Award timelines:

Every two weeks for the remainder of 2010, two (2) additional developers and projects will be selected.

For complete details: http://sqlblog.com/blogs/arnie_rowland/archive/2010/07/30/like-a-phoenix-rising-from-the-ashes.aspx

7/23/2010

Worst license dialog box?

 

Below is the license screen for the .NET Framework 4 Setup. Big, almost empty dialog box, and they only display three and a half lines of the license agreement at a time…  Maybe they added the print and save buttons so you could actually read it.

 

image

 

.

6/11/2010

Planning WAY Ahead… Tech-Ed 2011!

Tech-Ed 2010 is over today… and I missed out this year  :-(

But,

Tech-Ed 2011 has been scheduled!

May 16-19 2011  in  Atlanta

 

Sign up for EARLY discounts by June 30th, 2010

http://northamerica.msteched.com/registration

 

Home page: http://northamerica.msteched.com

 

See you there!

 

.

2/11/2010

SharePoint: It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level

 

“It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.”

 

If you are working on a SharePoint application page project then this error is most likely because you have a web.config file in your project.

So:

  • Don’t deploy the web.config to the LAYOUTS folder
  • Or if you need the web.config for any reason (testing, etc) then delete the authentication section:
        <authentication mode="Windows"/>

 

.

1/06/2010

Enumerate an enumeration!

 

A quick way to find all of the values of an enumeration (that Microsoft may have not completely documented).

System.Enum.GetNames returns an array of strings with the names

System.Enum.Parse looks up the numeric value using the string name

 

This example lists the value of a SharePoint enumeration named SpBasePermissions:

 

C# example:

//show name, decimal and hex
foreach (string enumName in Enum.GetNames(typeof(SPBasePermissions)))
{
    Console.WriteLine(String.Format("Item: {0,-25} Value: {1,20} {1,20:X}",
        enumName, (ulong)Enum.Parse(typeof(SPBasePermissions), enumName)));
}

 

VB.Net example:

For Each enumName As String In [Enum].GetNames(GetType(SPBasePermissions))
    Console.WriteLine(String.Format("Item: {0,-23} Value: {1,20:D} {1,20:X}", _
        enumName, [Enum].Parse(GetType(SPBasePermissions), enumName)))
Next

 

Result:

image

11/03/2009

Exam Discounts

This is a note for people who have attended my classes. (Everyone else go see their own MCT!)

If you are thinking about Microsoft certification, check with me before signing up for an exam. I can usually get you a discount on both the exam and on practice exams.

Currently:

  • 10% off of the exam
  • 40% on any MeasureUp 60 Day Online Practice Test

Not all exams qualify, but most MCP exams do. MeasureUp does not have a practice test of every Microsoft exam.

If you would like a discount voucher, email me at the email address I gave you during class or just call MAX Technical Training.

 

Know any college or tech school students? They can 55% off of exams here:
http://www.prometric.com/microsoft/student

.

Note to spammers!

Spammers, don't waste your time... all posts are moderated. If your comment includes unrelated links, is advertising, or just pure spam, it will never be seen.