ARM \ Logic App Deployment with Azure DevOps

Azure-Cloud-Hero-Server-LogicApps

Microsoft’s documentation refers to Logic Apps as being iPaaS or integration Platform-as-a-Service. The “i” in iPaaS indicates the strength of Logic Apps; not only are Azure systems integrated but external and third-party systems can be included in your Logic Apps, including Twitter, Slack, Office 365, and many others. This integration is done using a set of Microsoft-provided connectors. However, if a connector does not exist, then you can still integrate your logic app to external systems via their APIs.

Go to the Azure portal https://portal.azure.com and create the logic app.

LogicApp

Virtually every resource in Azure can be extracted into an ARM Template (Azure Resource Manager Template), allowing you to spin up an environment using the Json based template.

Configure parameters

Open your favourite code editor (my personal is VS Code or Visual Studio) and examine the template you just downloaded. You will notice a number of parameters in the template.

deploy and grab that much earned beer.

For my deployments, I use Azure DevOps. There is a great task Microsoft have added called Azure Resource Manager Deployment allowing you to automate your deployments for multiple environments.

azure_devops_and_logicapps_release

Azure WebJobs API

This API is accessed the same way as the git endpoint. e.g. if your git URL is https://yoursite.scm.azurewebsites.net/yoursite.git, then the API to get the list of deployments will be https://yoursite.scm.azurewebsites.net/deployments.

The credentials you use are the same as when you git push. See Deployment-credentials for more details.

List all web jobs
GET /api/webjobs

Triggered Jobs

List all triggered jobs
GET /api/triggeredwebjobs

Response

[
  {
    name: "jobName",
    runCommand: "...\run.cmd",
    type: "triggered",
    url: "http://.../triggeredwebjobs/jobName",
    history_url: "http://.../triggeredwebjobs/jobName/history",
    extra_info_url: "http://.../",
    scheduler_logs_url: "https://.../vfs/data/jobs/triggered/jobName/job_scheduler.log",
    settings: { },
    using_sdk: false,
    latest_run:
      {
        id: "20131103120400",
        status: "Success",
        start_time: "2013-11-08T02:56:00.000000Z",
        end_time: "2013-11-08T02:57:00.000000Z",
        duration: "00:01:00",
        output_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/output_20131103120400.log",
        error_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/error_20131103120400.log",
        url: "http://.../triggeredwebjobs/jobName/history/20131103120400",
        trigger: "Schedule - 0 0 0 * * *"
      }
  }
]
List all triggered jobs in swagger format
GET /api/triggeredwebjobsswagger

Response

{
  "swagger": "2.0",
  "info": {
    "version": "v1",
    "title": "WebJobs"
  },
  "host": "placeHolder",
  "schemes": [
    "https"
  ],
  "paths": {
    "/api/triggeredjobs/jobName/run": {
      "post": {
        "deprecated": false,
        "operationId": "jobName",
        "consumes": [],
        "produces": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "default": {
            "description": "Success"
          }
        },
        "parameters": [
          {
            "name": "arguments",
            "in": "query",
            "description": "Web Job Arguments",
            "required": false,
            "type": "string"
          }
        ]
      }
    }
  }
}
List all triggered jobs in swagger format###
GET /api/triggeredwebjobsswagger

Response

[
  {
    name: "jobName",
    runCommand: "...\run.cmd",
    type: "triggered",
    url: "http://.../triggeredwebjobs/jobName",
    history_url: "http://.../triggeredwebjobs/jobName/history",
    extra_info_url: "http://.../",
    latest_run:
      {
        id: "20131103120400",
        status: "Success",
        start_time: "2013-11-08T02:56:00.000000Z",
        end_time: "2013-11-08T02:57:00.000000Z",
        duration: "00:01:00",
        output_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/output_20131103120400.log",
        error_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/error_20131103120400.log",
        url: "http://.../triggeredwebjobs/jobName/history/20131103120400"
      }
  }
]
Get a specific triggered job by name
GET /api/triggeredwebjobs/{job name}

Response

[
{
  "swagger": "2.0",
  "info": {
    "version": "v1",
    "title": "WebJobs"
  },
  "host": "placeHolder",
  "schemes": [
    "https"
  ],
  "paths": {
    "/api/triggeredjobs/jobName/run": {
      "post": {
        "deprecated": false,
        "operationId": "jobName",
        "consumes": [],
        "produces": [],
        "responses": {
          "200": {
            "description": "Success"
          },
          "default": {
            "description": "Success"
          }
        },
        "parameters": [
          {
            "name": "arguments",
            "in": "query",
            "description": "Web Job Arguments",
            "required": false,
            "type": "string"
          }
        ]
      }
    }
  }
}
]
Upload a triggered job as zip

Using a zip file containing the files for it, or just a single file (e.g foo.exe).

PUT /api/zip/site/wwwroot/App_Data/jobs/triggered/{job name}/

or

PUT /api/triggeredwebjobs/{job name}

Use Content-Type: application/zip for zip otherwise it's treated as a regular script file.

The file name should be in the Content-Dispostion header, example:

Content-Disposition: attachement; filename=run.cmd

Note: the difference between the two techniques is that the first just adds files into the folder, while the second first deletes any existing content before adding new files.

Delete a triggered job
DELETE /api/vfs/site/wwwroot/App_Data/jobs/triggered/{job name}?recursive=true

or

DELETE /api/triggeredwebjobs/{job name}
Invoke a triggered job
POST /api/triggeredwebjobs/{job name}/run

To run with arguments use the arguments parameters that will be added to the script when invoked. It also gets passed to the WebJob as the WEBJOBS_COMMAND_ARGUMENTS environment variable.

POST /api/triggeredwebjobs/{job name}/run?arguments={arguments}

Note: if the site has multiple instances, the job will run on one of them arbitrarily. This is the same behavior as regular requests sent to the site.

In the http response, you get back a location attribute, with a URL to the details of the run that was started. e.g.

Location: https://mysite.scm.azurewebsites.net/api/triggeredwebjobs/SomeJob/history/201605192149381933
List all triggered job runs history
GET /api/triggeredwebjobs/{job name}/history

Response

{
  runs:
    [
      {
        id: "20131103120400",
        status: "Success",
        start_time: "2013-11-08T02:56:00.000000Z",
        end_time: "2013-11-08T02:57:00.000000Z",
        duration: "00:01:00",
        output_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/output_20131103120400.log",
        error_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/error_20131103120400.log",
        url: "http://.../triggeredwebjobs/jobName/history/20131103120400",
        trigger: "Schedule - 0 0 0 * * *"
      },
      ...
    ]
}

Note: The job history is kept in D:\home\data\jobs\triggered\jobName folder. Each history of job is kept under different folder by datetime of the execution. The api returns all job history in datetime descending order (meaning latest on top). We only keep most recent 50 job history (configurable by WEBJOBS_HISTORY_SIZE appSettings).

Get a specific run for a specific triggered job
GET /api/triggeredwebjobs/{job name}/history/{id}

Response

{
  id: "20131103120400",
  status: "Success",
  start_time: "2013-11-08T02:56:00.000000Z",
  end_time: "2013-11-08T02:57:00.000000Z",
  duration: "00:01:00",
  output_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/output_20131103120400.log",
  error_url: "http://.../vfs/data/jobs/triggered/jobName/20131103120400/error_20131103120400.log",
  url: "http://.../triggeredwebjobs/jobName/history/20131103120400",
  trigger: "Schedule - 0 0 0 * * *"
}

Continuous Jobs

List all continuous jobs
GET /api/continuouswebjobs

Response

[
  {
    name: "jobName",
    status: "Running",
    runCommand: "...\run.cmd",
    log_url: "http://.../vfs/data/jobs/continuous/jobName/job.log",
    extra_info_url: "http://.../",
    url: "http://.../continuouswebjobs/jobName",
    type: "continuous"
  }
]
Get a specific continuous job by name
GET /api/continuouswebjobs/{job name}

Response

{
  name: "jobName",
  status: "Running",
  runCommand: "...\run.cmd",
  log_url: "http://.../vfs/data/jobs/continuous/jobName/job.log",
  extra_info_url: "http://.../",
  url: "http://.../continuouswebjobs/jobName",
  type: "continuous"
}

The status can take the following values:

  • Initializing
  • Starting
  • Running
  • PendingRestart
  • Stopped
  • Aborted
  • Abandoned
  • Success
  • Failure
Upload a continuous job as zip

Using a zip file containing the files for it.

PUT /api/zip/site/wwwroot/App_Data/jobs/continuous/{job name}/

or

PUT /api/continuouswebjobs/{job name}

Use Content-Type: application/zip for zip otherwise it's treated as a regular script file.

The file name should be in the Content-Dispostion header, example:

Content-Disposition: attachement; filename=run.cmd

Note: the difference between the two techniques is that the first just adds files into the folder, while the second first deletes any existing content before adding new files.

Delete a continuous job
DELETE /api/vfs/site/wwwroot/App_Data/jobs/continuous/{job name}?recursive=true

or

DELETE /api/continuouswebjobs/{job name}
Start a continuous job
POST /api/continuouswebjobs/{job name}/start
Stop a continuous job
POST /api/continuouswebjobs/{job name}/stop
Get continuous job settings
GET /api/continuouswebjobs/{job name}/settings

Response

{
  "is_singleton": true
}
Set a continuous job as singleton

If a continuous job is set as singleton it'll run only on a single instance opposed to running on all instances. By default, it runs on all instances.

PUT /api/continuouswebjobs/{job name}/settings

Body

{
  "is_singleton": true
}

To set a continuous job as singleton during deployment (without the need for the REST API) you can simply create a file called settings.job with the content: { "is_singleton": true } and put it at the root of the (specific) WebJob directory.

Set the schedule for a triggered job

You can set the schedule for invoking a triggered job by providing a cron expression made of 6 fields (second, minute, hour, day, month, day of the week).

PUT /api/triggeredwebjobs/{job name}/settings

Body

{
  "schedule": "0 */2 * * * *"
}

To set the schedule for a triggered job during deployment (without the need for the REST API) you can simply create a file called settings.job with the content: { "schedule": "0 */2 * * * *" } and put it at the root of the (specific) WebJob directory.

Adding an external Microsoft login to IdentityServer4

This article shows how to implement a Microsoft Account as an external provider in an IdentityServer4 project using ASP.NET Core Identity with a SQLite database.

Setting up the App Platform for the Microsoft Account

To setup the app, login using your Microsoft account and open the My Applications link

https://apps.dev.microsoft.com/?mkt=en-gb#/appList

id4-microsoft-apps

Click the Add an app button. Give the application a name and add your email. This app is called microsoft_id4_enrico.

id4-microsoft-apps-registration

After you clicked the create button, you need to generate a new password. Save this somewhere for the application configuration. This will be the client secret when configuring the application.

id4-microsoft-apps-myapp

Now Add a new platform. Choose a Web type.

id4-microsoft-apps-platform

Now add the redirect URL for you application. This will be the https://YOUR_URL/signin-microsoft

id4-microsoft-apps-platform2

Add the Permissions as required

id4-microsoft-apps-permission

id4-microsoft-apps-permission-list

pplication configuration

Note: The samples are at present not updated to ASP.NET Core 2.0

Clone the IdentityServer4 samples and use the 6_AspNetIdentity project from the quickstarts.
Add the Microsoft.AspNetCore.Authentication.MicrosoftAccount package using Nuget as well as the ASP.NET Core Identity and EFCore packages required to the IdentityServer4 server project.

The application uses SQLite with Identity. This is configured in the Startup class in the ConfigureServices method.

services.AddDbContext<ApplicationDbContext>(options =>
       options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
 
services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders()
    .AddIdentityServer();

Now the AddMicrosoftAccount extension method can be use to add the Microsoft Account external provider middleware in the Configure method in the Startup class. The SignInScheme is set to “Identity.External” because the application is using ASP.NET Core Identity. The ClientId is the Id from the app ‘microsoft_id4_damienbod’ which was configured on the my applications website. The ClientSecret is the generated password.

services.AddAuthentication()
     .AddMicrosoftAccount(options => {
          options.ClientId = _clientId;
          options.SignInScheme = "Identity.External";
          options.ClientSecret = _clientSecret;
      });
 
services.AddMvc();
 
...
 
services.AddIdentityServer()
     .AddSigningCredential(cert)
     .AddInMemoryIdentityResources(Config.GetIdentityResources())
     .AddInMemoryApiResources(Config.GetApiResources())
     .AddInMemoryClients(Config.GetClients())
     .AddAspNetIdentity<ApplicationUser>()
     .AddProfileService<IdentityWithAdditionalClaimsProfileService>();

And the Configure method also needs to be configured correctly.

If you receive an error like "unauthorize_access", remember that RedirectUri is required in IdentityServer configuration and clients.

Adding Swagger to Web API project

swagger_help_pages

Adding Swagger to your Web API does not replace ASP.NET Web API help pages (here the nuget package for Microsoft ASP.NET Web Api Help Page). You can have both running side by side, if desired.

To add Swagger to an ASP.NET Web Api, we will install an open source project called Swashbuckle via nuget.

Install-Package Swashbuckle –Version 5.2.1

After the package is installed, navigate to App_Start in the Solution Explorer. You’ll notice a new file called SwaggerConfig.cs. This file is where Swagger is enabled and any configuration options should be set here.

swagger_config-1

Configuring Swagger

At minimum you’ll need this line to enable Swagger and Swagger UI.

GlobalConfiguration.Configuration
  .EnableSwagger(c => c.SingleApiVersion("v1", "A title for your API"))
  .EnableSwaggerUi();

Start a new debugging session (F5) and navigate to http://localhost:[PORT_NUM]/swagger. You should see Swagger UI help pages for your APIs.

swagger_ui

Expanding an api and clicking the “Try it out!” button will make a call to that specific API and return results.

swagger_get_superhero-1

And then you see the response:

swagger_get_response

Enable Swagger to use XML comments

The minimum configuration is nice to get started but let’s add some more customization. We can tell Swashbuckle to use XML comments to add more details to the Swagger metadata. These are the same XML comments that ASP.NET Help Pages uses.

First, enable XML documentation file creation during build. In Solution Explorer right-click on the Web API project and click Properties. Click the Build tab and navigate to Output. Make sure XML documentation file is checked. You can leave the default file path. In my case its bin\SwaggerDemoApi.XML

build_xml_docs

Next, we need to tell Swashbuckle to include our XML comments in the Swagger metadata. Add the following line to SwaggerConfig.cs. Make sure to change the file path to the path of your XML documentation file.

GlobalConfiguration.Configuration
  .EnableSwagger(c =>
    {
      c.SingleApiVersion("v1", "SwaggerDemoApi");
      c.IncludeXmlComments(string.Format(@"{0}\bin\SwaggerDemoApi.XML",           
                           System.AppDomain.CurrentDomain.BaseDirectory));
    })
  .EnableSwaggerUi();

Finally, if you haven’t already, add XML comments to your Models and API methods.

xml_comments

Run the project and navigate back to /swagger. You should see more details added to your API documentation. I’ve highlighted a few below with their corresponding XML comment.

swagger_xml_comments

Under Response Class, click Model. You should see any XML comments added to your models.

swagger_xml_comments_model

Describing Enums As Strings

My Superhero class contains an Enum property called Universe which represents which comic universe they belong to.

universe_enum

By default, Swagger displays these Enum values as their integer value. This is not very descriptive. Let’s change it to display the string representation.

GlobalConfiguration.Configuration
  .EnableSwagger(c =>
  {
    c.SingleApiVersion("v1", "SwaggerDemoApi");
    c.IncludeXmlComments(string.Format(@"{0}\bin\SwaggerDemoApi.XML", 
                         System.AppDomain.CurrentDomain.BaseDirectory));
    c.DescribeAllEnumsAsStrings();
  })
  .EnableSwaggerUi();

If I look at Swagger now, the Universe Enum values are displayed as strings.

swagger_xml_comments_enum

These are just a few of the many configuration options you can specify in Swashbuckle to create your Swagger metadata. I encourage you to review the other options on Swashbuckle’s GitHub.

Happy coding!

Microsoft officially announces 'Windows Sandbox' for running applications in isolation

Windows-Sandbox-Screenshot-open

Microsoft officially took the wraps off a feature expected to come to Windows 10 19H1 early next year that it has rechristened as "Windows Sandbox." This feature, which will be part of Windows 10 Pro and Enterprise editions, creates "an isolated, temporary desktop environment" where users can run potentially suspicious software, officials said in a blog post on December 18.

Earlier this year, Microsoft was rumored to be readying a new security feature for Windows 10 that was called, at that time, "InPrivate Desktop." InPrivate Desktop got a mention in Microsoft's Insider Feedback Hub during a bug-bash q1uest in August. The codename for InPrivate Desktop was "Madrid."

Quick start

  1. Install Windows 10 Pro or Enterprise, Insider build 18305 or newer
  2. Enable virtualization:
    • If you are using a physical machine, ensure virtualization capabilities are enabled in the BIOS.
    • If you are using a virtual machine, enable nested virtualization with this PowerShell cmdlet:
    • Set-VMProcessor -VMName <VMName> -ExposeVirtualizationExtensions $true
  3. Open Windows Features, and then select Windows Sandbox. Select OK to install Windows Sandbox. You might be asked to restart the computer. Optional-Windows-Features-dlg
  4. Using the Start menu, find Windows Sandbox, run it and allow the elevation
  5. Copy an executable file from the host
  6. Paste the executable file in the window of Windows Sandbox (on the Windows desktop)
  7. Run the executable in the Windows Sandbox; if it is an installer go ahead and install it
  8. Run the application and use it as you normally do
  9. When you’re done experimenting, you can simply close the Windows Sandbox application. All sandbox content will be discarded and permanently deleted
  10. Confirm that the host does not have any of the modifications that you made in Windows Sandbox.

Enable Developer Options on Samsung Galaxy S9

The Developer Options has many useful features that certainly elevate the usability, and user experience of an Android phone. In this tutorial, we are going to learn how to activate/enable Developer Options on Samsung Galaxy S9, and how to use it effectively. All Samsung smartphones come with an option, but it usually it comes hidden within the operating system. But it doesn’t mean that you can’t access it. And importantly, that is not a tough task either. The method is almost same as we perform on other Samsung’s smartphones.

  1. Open the app menu, and then tap on Settings.
  2. Scroll to locate ‘Device information‘ and tap on it.
  3. Next, locate the ‘Software info‘ and tap on it.
  4. Under the hood of this menu, find the ‘Build number‘.
  5. Now tap 7 times on ‘Build number’. Each time you tap on ‘Build number’, it displays a screen with a message about remaining taps to become a developer.
  6. This will activate the Developer Options on your phone:galaxy-s9-developer-options

Now, the Developer Options will appear under Settings.

How to Automatically Backup Your Web Server Files With WinSCP over FTP

WinSCPYou’ve heard it time and time again: back up your data. There are plenty of backup solutions, but nothing is better than an easy and free solution. So with a few lines of code and a very helpful program called WinSCP, we’re going to set up an automatic sync between your FTP server and your home computer.

Requirements

  • An FTP server and access credentials
  • A Windows computer
  • A copy of WinSCP (Windows only)

Setting Up WinSCP

Once you've downloaded your copy of WinSCP, install it on your computer. It also wouldn't be a bad idea to start remembering your FTP address, username, and password while WinSCP is installing.

From the login screen, enter your host name (i.e. ftp.puresourcecode.com), username, and password. You can use either FTP or SFTP, but be sure to change the port number the appropriate port your FTP host has provided you. Then click Save to save the profile. Open the profile to log into your FTP server. This will verify that you typed the right credentials in the profile.

image

Create the script

We’re going to create a simple script that logs into your FTP server, tells WinSCP where to download the missing files to on your computer, and then creates a log file that records FTP activity. Go ahead and open Notepad on your computer. Copy the following script template into Notepad:

option batch on
option confirm off
open ftp.puresourcecode.com
cd backups
option transfer binary
get /testremote* f:\backups\testlocal*
synchronize local f:\backups\testlocal testremote
exit

WinSCP will answer all prompts negatively to avoid holding up the script. Then it will automatically overwrite files when prompted. It then logs into your FTP server using the profile we created earlier, changes directories (if needed), and transfers binary (as opposed to ASCII). Finally, it reads the remote FTP directory and transfers files to the specified local directory. For even more options, check out the official WinSCP scripting page.

Now before you modify or run this script, we suggest creating test directories on both the remote and local targets. The last thing you want is to accidentally wipe your FTP server without any backups. So create a directory on your computer called “testlocal” (we created it under f:\backups) and another called “testremote” on your FTP server (we created it at the root). Once you run the script and it executes successfully, save it as “sync.txt” to your computer (we saved ours in our testlocal folder).

Automating the script

Now you’re ready to automate the script. For this we can use Task Scheduler.

image

Click on Create Basic Task and follow the following screens. First insert a name for your task.

image

Select when you want to start the task.

image

Select Start a program in the next screen.

image

Now click on Browse... and find WinSCP executable file in your program folder.

image

Underneath, add

/console /script=f:\backup\sync.txt /log=f:\backup\log.txt 

to Add arguments. Be sure to change the arguments if your sync.txt is in a different location and if you want to generate the log file in a different location. Click Next.

So the task is created!

Connect ASP.NET MVC 4.6.2 project to IdentityServer4

I have a website running on ASP.NET MVC 4.5.2. I have an IdentityServer4 server running but when I try and authenticate against it I get an:

invalid_request

I googled a bit but I can’t find a solution. Finally, I found the way.

First, in your IdentityServer4 you have to create a new client:

public static IEnumerable GetClients() {
    return new List<client> {
        new Client {
            ClientId = "yourid",
            AllowedScopes = new List<string> { "openid" },
            AllowedGrantTypes = GrantTypes.Hybrid,
            RedirectUris = new List { "http://yoururl/signin-oidc" },
        }
    }
}

When you added the new client, you can update your other MVC project. Under App_Start open Startup.Auth.cs and add this code:

using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;

namespace PSC
{
    public partial class Startup
    {
        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Cookies"
            });

            app.UseOpenIdConnectAuthentication(
              new OpenIdConnectAuthenticationOptions
              {
                Authority = "https://yourIdentityServerUrl",
                ClientId = "yourid",
                ResponseType = "id_token code",
                SignInAsAuthenticationType = "Cookies",
                RedirectUri = "http://yoururl/signin-oidc",
                Scope = "openid",
              });
        }
    }
}

You have to add a Nuget package called Microsoft.Owin.Security.OpenIdConnect.

Happy coding!

Connect ASP.NET MVC 4.6.2 project to IdentityServer4

I have a website running on ASP.NET MVC 4.5.2. I have an IdentityServer4 server running but when I try and authenticate against it I get an:

invalid_request

I googled a bit but I can’t find a solution. Finally, I found the way.

First, in your IdentityServer4 you have to create a new client:

public static IEnumerable GetClients() {
    return new List<client> {
        new Client {
            ClientId = "yourid",
            AllowedScopes = new List<string> { "openid" },
            AllowedGrantTypes = GrantTypes.Hybrid,
            RedirectUris = new List { "http://yoururl/signin-oidc" },
        }
    }
}

When you added the new client, you can update your other MVC project. Under App_Start open Startup.Auth.cs and add this code:

using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;

namespace PSC
{
    public partial class Startup
    {
        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Cookies"
            });

            app.UseOpenIdConnectAuthentication(
              new OpenIdConnectAuthenticationOptions
              {
                Authority = "https://yourIdentityServerUrl",
                ClientId = "yourid",
                ResponseType = "id_token code",
                SignInAsAuthenticationType = "Cookies",
                RedirectUri = "http://yoururl/signin-oidc",
                Scope = "openid",
              });
        }
    }
}

You have to add a Nuget package called Microsoft.Owin.Security.OpenIdConnect.

Happy coding!

Create a well formed URI using UriBuilder class with C#

You can use System.UriBuilder class to build a new well-formed URI. You need to set some property of UriBuilder object's, like Scheme, Port, Host, Path etc...

You can get generated URI using AbsoluteUri Method.

    // Generate a new URI.
    UriBuilder objUri = new UriBuilder();
    objUri.Scheme = "http";
    objUri.Port = 80;
    objUri.Host = "www.microsoft.com";
    objUri.Path = "en-us/default.aspx";

    Response.Write("Genereted URI: " + objUri.Uri.AbsoluteUri);

Happy coding!

Advertsing

125X125_06

Planet Xamarin

Planet Xamarin




TagCloud

MonthList