November 21, 2022

SQL Server - Change Authentication mode from Windows to Sql Server Authentication

During SQL Server installation, the wizard allows to select the authentication mode. At that time you can pick the required authentication scheme as per your needs.

In case if you have selected the Windows Authentication, the server will not configure the SQL Authentication mode and will not setup the default 'sa' account.

At later time if you want to enable the SQL Authentication, following steps will be required:

  1. Enable SQL Server Authentication Mode:

    • In SQL Server Management Studio Object Explorer, right-click the Server node, and then click Properties.

    • On the Security page, under Server authentication, select SQL Server and Windows Authentication mode. Then click OK.

    • When it asks you to restart the SQL Server, click OK.
  2. Enable Existing Login account sa:

    • By default, the sa account remains disable (if Windows Authentication mode is selected during SQL Server installation).

      First step is to enable sa login.

      ALTER LOGIN sa ENABLE ;
      GO
      
    • Second step is to set strong password for login sa.

      ALTER LOGIN sa WITH PASSWORD = '[enter-strong-password-here]';
      GO
      

October 24, 2022

.NET Core - asp-append-version tag helper

To have better user experience and improve website's performance, modern browsers used to cache the static files like css, js and images. While it helps your website to perform better but it could also lead to serve stale files. If you have made any changes to your css, js or image files, the browsers will keep showing the old images until it refreshes it cache. Sometimes the users have to do a full refresh (Ctrl + F5 or Ctrl + R) for the browser to get latest or fresh content of these files.

In this case we need to tell the browser to get the new files from server to make sure that the user will always be served with latest files without doing full refresh on the browser.

Some ways to acheive this behavior could be:

  1. Every time you make any changes in the file content, also change the file name and its references.
  2. Another way is to add a random string behind the js, css or image url.

Here we will use the second approach to add a random string for these files urls.

The good news is that ASP.NET Core has in-build feature for this issue. We can use the tag helper:

asp-append-version="true" 

To enable this feature ON for particualr static file, we just need to add the above asp-append-version tag-helper/attribute as below.

CSS
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />    
JS 
<script src="~/js/site.js" asp-append-version="true"></script>
Image
<img src="~/images/logo.png" asp-append-version="true" />

After making these changes you can check the rendered html from browser source code or inspect element using browser's developer tools. It will be found that there is a random string attached behind file name.

If you make any change within the file content for css/js etc, when the next time browser will render it, it will fetch the fresh file from server because of different random string attached to the file URL.

CSS
https://localhost:7163/css/site.css?v=1Ip96t8Xk8GevF37Kz0Wu_1tuNyEZJcH5PYrmiK1fNs

JS
https://localhost:7163/js/site.js?v=mC13cRUFjf1YiinDRFWfv-eOXac88lLptssglYLhYtQ

You will see a different random string appended each time the changes are made to the file cotent, which makes sure that the browser will fetch the latest copy from server.

October 19, 2022

Azure Devops - OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443

While working with repos in Azure DevOps, sometimes it will display the following error when try to communicate with remote repo by push/pull commands.

OpenSSL SSL_connect: Connection was reset in connection to dev.azure.com:443

Usually you can get work around this error if you keep trying opening/closing Visual Studio etc.

With Git repos in Azure-DevOps, I found that it has something to do with IPv6. After I disable the IPv6 from network connections, it works.

You can disable IPv6 by the following way:

  1. Open your Network Connections.

  2. Right click on your current network and select Properties.

  3. In the Network Properties dialog box, deselect Internet Prorotcol Version 6 (TCP/IPv6).

  4. Click OK to close the dialog.

The same work around can fix the following error when trying to clone a git repo from Azure DevOps.

unable to access 'https://dev.azure.com/myorganization/myproject/_git/webproject/':
Recv failure: Connection was reset

September 19, 2022

How to add a custom HTTP header to WCF method call?

I have an environment with SOA architecture spans a number of WCF services. Some of these services need to identity its clients. Based on different client's identity or scope, the service needs to run different conditional code. One way to achieve this functionality is to add a custom http-header in each WCF method call on client side, which will enable WCF service to identity the client for any given request.

In this exmaple, I will add a custom http-header named Client-Id, using custom implemenation of IClientMessageInspector interace.

You need to create two classes

  1. One is CustomHeaderEndpointBehavior to implment IEndpointBehavior interface.
  2. Second is CustomHeaderMessageInspector which implements the IClientMessageInspector interface.

The CustomHeaderEndpointBehavior class will add our custom CustomHeaderMessageInspector to ClientMessageInspectors collection for clientRuntime object.

clientRuntime.ClientMessageInspectors.Add(customHeaderMessageInspector);

In CustomHeaderMessageInspector class, we can add the custom header in BeforeSendRequest() method.

Complete code listing for CustomHeaderMessageInspector class is:

public class CustomHeaderMessageInspector : IClientMessageInspector
{
   private const string CLIENT_ID_HTTP_HEADER = "Client-Id";
   private string _clientId;

   public CustomHeaderMessageInspector(string clientId)
   {
      this._clientId = clientId;
   }

   public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, 
                                 object correlationState)
   {

   }

   public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, 
                                   System.ServiceModel.IClientChannel channel)
   {
      HttpRequestMessageProperty httpRequestMessage;
      object httpRequestMessageObject;
		
      if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, 
                                         out httpRequestMessageObject))
      {
         httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
         if (string.IsNullOrEmpty(httpRequestMessage.Headers[CLIENT_ID_HTTP_HEADER]))
         {
            httpRequestMessage.Headers[CLIENT_ID_HTTP_HEADER] = this._clientId;
         }
      }
      else
      {
         httpRequestMessage = new HttpRequestMessageProperty();
         httpRequestMessage.Headers.Add(CLIENT_ID_HTTP_HEADER, this._clientId);
         request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
      }

      return request;
   }
}

Code listing for CustomHeaderEndpointBehavior class is:

public class CustomHeaderEndpointBehavior : IEndpointBehavior
{
   private string _clientId;

   public CustomHeaderEndpointBehavior(string clientId)
   {
      this._clientId = clientId;
   }

   public void AddBindingParameters(ServiceEndpoint endpoint, 
               System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
   {

   }

   public void ApplyClientBehavior(ServiceEndpoint endpoint, 
               System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
   {
      CustomHeaderMessageInspector inspector = new CustomHeaderMessageInspector(this._clientId);

      clientRuntime.ClientMessageInspectors.Add(inspector);
   }

   public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
               System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
   {

   }

   public void Validate(ServiceEndpoint endpoint)
   {

   }
}

When calling the service methods on the client side, you can simply add the custom EndpointBehavior (CustomHeaderEndpointBehavior) on the service client instance object.

ServiceReference1.Service1Client service1Client = new ServiceReference1.Service1Client();

var requestInterceptor = new CustomHeaderEndpointBehavior();
service1Client.Endpoint.EndpointBehaviors.Add(requestInterceptor);

Now you can call any service method, the custom htpp-header will be automatically added to each request.

Related Post(s):

How to trace soap xml from Web Service client?

In this post I will explain how to trace the raw xml for each request and response sent between client/server for Soap/WCF Services.

I found this idea/code from stackoverflow post:

how to trace soap xml as a webservice client in netcore? .

You need to create two classes

  1. One is InspectorBehavior to implment IEndpointBehavior interface.
  2. Second is MyMessageInspector which implements the IClientMessageInspector interface.

The InspectorBehavior class will use the ApplyClientBehavior() method to add the real message inspector by calling Add() method on ClientMessageInspectors collection for clientRuntime object.

clientRuntime.ClientMessageInspectors.Add(myMessageInspector);

Further in MyMessageInspector class, we can retreive and save the Request and Response Xml content in local variables/properties in these two methods.

  • BeforeSendRequest
  • AfterReceiveReply

Complete code listing for MyMessageInspector class is:

public class MyMessageInspector : IClientMessageInspector
{
   public string LastRequestXML { get; private set; }
   public string LastResponseXML { get; private set; }

   public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, 
                                 object correlationState)
   {
      LastResponseXML = reply.ToString();
   }

   public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, 
                                   System.ServiceModel.IClientChannel channel)
   {
      LastRequestXML = request.ToString();
      return request;
   }
}

Code listing for InspectorBehavior class is:

public class InspectorBehavior : IEndpointBehavior
{
   public string LastRequestXML
   {
      get
      {
         return myMessageInspector.LastRequestXML;
      }
   }

   public string LastResponseXML
   {
      get
      {
         return myMessageInspector.LastResponseXML;
      }
   }

   private MyMessageInspector myMessageInspector = new MyMessageInspector();

   public void AddBindingParameters(ServiceEndpoint endpoint, 
               System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
   {

   }

   public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
               EndpointDispatcher endpointDispatcher)
   {

   }

   public void Validate(ServiceEndpoint endpoint)
   {

   }

   public void ApplyClientBehavior(ServiceEndpoint endpoint, 
               ClientRuntime clientRuntime)
   {
      clientRuntime.ClientMessageInspectors.Add(myMessageInspector);
   }
}

I used the WFC service instance created by Visual Studio template with default two methods GetData() and GetDataUsingDataContract();

When calling the service methods on the client side, you can simply add the custom EndpointBehavior (InspectorBehavior) on the service client instance object.

ServiceReference1.Service1Client service1Client = new ServiceReference1.Service1Client();

var requestInterceptor = new InspectorBehavior();
service1Client.Endpoint.EndpointBehaviors.Add(requestInterceptor);

After service method call, you can get the Request and Response Xml content by requestInterceptor's properties:

string str1 = service1Client.GetDataAsync(1).GetAwaiter().GetResult();
string req1Xml = requestInterceptor.LastRequestXML;
string res1Xml = requestInterceptor.LastResponseXML;

Here is the sample of raw sample I received by above call.

Request

<?xml version="1.0" encoding="utf-16"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"
    >http://tempuri.org/IService1/GetData</Action>
  </s:Header>
  <s:Body>
    <GetData xmlns="http://tempuri.org/">
      <value>1</value>
    </GetData>
  </s:Body>
</s:Envelope>

Response

<?xml version="1.0" encoding="utf-16"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <GetDataResponse xmlns="http://tempuri.org/">
      <GetDataResult>You entered: 1</GetDataResult>
    </GetDataResponse>
  </s:Body>
</s:Envelope>

Call the second method, and get the Request and Response Xml content by same properties:

ServiceReference1.CompositeType compositeType = new ServiceReference1.CompositeType();
compositeType.BoolValue = true;
compositeType.StringValue = "some text";

ServiceReference1.CompositeType compositeTypeRes = 
       service1Client.GetDataUsingDataContractAsync(compositeType).GetAwaiter().GetResult();
string req2Xml = requestInterceptor.LastRequestXML;
string res2Xml = requestInterceptor.LastResponseXML;

Here is the sample of raw sample I received by above call.

Request

<?xml version="1.0" encoding="utf-16"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"
    >http://tempuri.org/IService1/GetDataUsingDataContract</Action>
  </s:Header>
  <s:Body>
    <GetDataUsingDataContract xmlns="http://tempuri.org/">
      <composite xmlns:d4p1="http://schemas.datacontract.org/2004/07/WcfService1" 
      xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <d4p1:BoolValue>true</d4p1:BoolValue>
        <d4p1:StringValue>some text</d4p1:StringValue>
      </composite>
    </GetDataUsingDataContract>
  </s:Body>
</s:Envelope>

Response

<?xml version="1.0" encoding="utf-16"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <GetDataUsingDataContractResponse xmlns="http://tempuri.org/">
      <GetDataUsingDataContractResult xmlns:a="http://schemas.datacontract.org/2004/07/WcfService1" 
      xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:BoolValue>true</a:BoolValue>
        <a:StringValue>some textSuffix</a:StringValue>
      </GetDataUsingDataContractResult>
    </GetDataUsingDataContractResponse>
  </s:Body>
</s:Envelope>

References:

Related Post(s):