Posted on Leave a comment

Extract images from URL in excel with PHP using PhpSpreadsheet

Last modified on August 7th, 2019 by Vincy.

There are various ways to extract images from a given URL. PHP contains built-in functions for extracting data including the images with a URL.

This article is for PHP code to extract images from URLs existing in an excel file.

I have used PhpSpreadsheet to read the URLs from an Excel file. Then, I created cURL script to extract images from the URL.

Extract Images from URL Read from Excel

PhpSpreadsheet library supports Excel read-write operations. It provides enormous features like formatting content, manipulating data and more. It has a rich set of built-in classes and thereby makes the development process easy.

Working with spreadsheets is a common need while handling excel data via programming. PhpSpreadsheet library reduces the developer’s effort on building applications with excel data handing.

We have already seen several examples of URL extract using PHP. Also, we have created code for getting video thumbnail from Youtube URL.

What is inside?

  1. Uses of extracting images from URL from Excel
  2. Advantages of PhpSpreadsheet Library
  3. Existing PHP libraries used to import-export
  4. File Structure
  5. About this example
  6. PHP code to load and extract image data
  7. Render extracted images in a gallery
  8. Database script
  9. Extract images from URL in excel using PhpSpreadsheet Output

Extracting of images from URL from an excel file will be helpful in many scenarios. Below list shows some scenarios.

  1. To import a large volume of images into your application’s media library.
  2. To migrate media files from one domain to another.
  3. To restore the Excel backup images into a database.
  4. To create a dynamic photo gallery without a database.

Advantages of PhpSpreadsheet Library

PhpSpreadsheet has many features and thereby has more advantages of using it.

  • It provides methods to prepare reports, charts, plans and more.
  • It has an option the read, write from a specified row, column and sheet of a spreadsheet document.
  • It is suitable for handling a large amount of data.
  • It helps to manage checklists, calendars, timesheets, schedules, proposal plans.
  • It provides security to protect spreadsheet data from editing.
  • It supports encryption to prevent the spreadsheet data from viewing. 

Existing PHP libraries used to import-export

There are many PHP libraries available in the market support spreadsheet data handling.

  • PortPHP supports import-export data between Excel, CSV and database storages. It has readers, writers and converters to process data exchange and manipulation.
  • The Spout is a PHP library used to read write spreadsheets in an efficient way. It supports three types of spreadsheets XLS, CSV, ODS.

File structure

Below screenshot shows the file structure of this example. The ExcelImportService class file is an integral part of this example. It loads PhpSpreadsheet library and covers all the operations related to the excel image extract.

The excel_template folder contains an input Excel file with image URLs. This example code loads this file to extract images from the URL.

Instead of using this fixed excel template, you can also allow users to choose an excel file. By adding a HTML form with a file input option user can choose their excel to explore extract.

Extract Images from URL from the Excel File Structure

About this example

This example loads an input Excel file in an Import service class. This sample excel file will contain image URLs.

In this example, I have used PhpSpreadsheet library to read the excel data. This library method helps to get the URLs and store into an array.

Then I iterate this URL array in a loop to extract the image data. I used PHP cURL script to extract images. In a previous tutorial, we have seen how to run PHP cURL script to extract content from a remote URL.

Finally, this code will store the extracted images into a directory and save the path to the database. In a previous article, we import excel data into a database without images. Also, we have seen examples to import data from CSV to a database.

PHP code to load and extract image data

This PHP code loads the ExcelImportService class to load and import image data from an excel.

This is the main PHP class created for this example. It handles all operations during the excel image extract.

<?php use \Phppot\ExcelImportService; require_once 'Class/ExcelImportService.php'; $excelImportService = new ExcelImportService(); $excelDataArray = $excelImportService->loadExcel(); if (! empty($excelDataArray)) { $isNewData = $excelImportService->importImages($excelDataArray); if ($isNewData) { $message = "Images extracted from excel successfully!"; } else { $message = "No new images found during the excel extract!"; } } $imageResult = $excelImportService->getAllImages(); ?> 

ExcelImportService.php

This class loads the PhpSpreadsheet library. It also has the DataSource instance in the class level.

The database access request from this class uses this instance. It is for saving the extracted image path to the database.

Note: Download PhpSpreadsheet library from Github without dependencies. Then run the get the dependencies via composer by using the following command.

composer require phpoffice/phpspreadsheet 

In this class, the loadExcel() function loads the input excel to read the URLs as an array. It returns this array to extract image blob via cURL request. 

The extractImage() function executes the cURL script. It gets the image resource data from the remote URL read from Excel. Then it writes the file into a target as specified in this example.

After putting the extracted images into a folder, then the code saves the to the image database table. The saveImagePath() method contains the insert query and parameters to invoke DataSource insert.

<?php namespace Phppot; use \Phppot\DataSource; require 'Vendor/PhpSpreadsheet/autoload.php'; class ExcelImportService { private $ds; function __construct() { require_once __DIR__ . './DataSource.php'; $this->ds = new DataSource(); } private function isUrlExist($url) { $query = 'SELECT * FROM tbl_images where remote_url = ?'; $paramType = 's'; $paramValue = array($url); $count = $this->ds->numRows($query, $paramType, $paramValue); return $count; } private function extractImage($url) { $path = pathinfo($url); $imageTargetPath = 'uploads/' . time() . $path['basename']; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, false); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // <-- important to specify curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // <-- important to specify $resultImage = curl_exec($ch); curl_close($ch); $fp = fopen($imageTargetPath, 'wb'); fwrite($fp, $resultImage); fclose($fp); $imageInfo["image_name"] = $path['basename']; $imageInfo["image_path"] = $imageTargetPath; return $imageInfo; } private function saveImagePath($imageInfo, $remoteUrl) { $query = "INSERT INTO tbl_images (image_name,image_path, remote_url) VALUES (?, ?, ?)"; $paramType = 'sss'; $paramValue = array($imageInfo["image_name"], $imageInfo["image_path"], $remoteUrl); $this->ds->insert($query, $paramType, $paramValue); } public function loadExcel() { //create directly an object instance of the IOFactory class, and load the xlsx file $xlsFile ='Excel_Template/imageURLs.xlsx'; $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($xlsFile); //read excel data and store it into an array $excelData = $spreadsheet->getActiveSheet()->toArray(null, true, true, true); $rowCount = count($excelData); $urlArray = array(); for($i=2;$i<$rowCount;$i++) { $url = $excelData[$i]['A']; if(!empty($url)) { $urlArray[] = $url; } } return $urlArray; } public function importImages($excelDataArray) { $isNewData = false; foreach($excelDataArray as $url) { $isUrlExist = $this->isUrlExist($url); if (empty($isUrlExist)) { $imageInfo = $this->extractImage($url); if(!empty($imageInfo)) { $this->saveImagePath($imageInfo, $url); } $isNewData = true; } } return $isNewData; } public function getAllImages() { $query = 'SELECT * FROM tbl_images'; $result = $this->ds->select($query); return $result; } }

DataSource.php

This is a common PHP class that we have used in many examples. It contains functions to execute the database operations planned for the example code. It establishes the database connection at its constructor.

Model classes used in our PHP examples load this class and instantiate it to access the database.

<?php namespace Phppot; /** * Generic datasource class for handling DB operations. * Uses MySqli and PreparedStatements. * * @version 2.3 */ class DataSource { // PHP 7.1.0 visibility modifiers are allowed for class constants. // when using above 7.1.0, declare the below constants as private const HOST = 'localhost'; const USERNAME = 'root'; const PASSWORD = ''; const DATABASENAME = 'phpsamples'; private $conn; /** * PHP implicitly takes care of cleanup for default connection types. * So no need to worry about closing the connection. * * Singletons not required in PHP as there is no * concept of shared memory. * Every object lives only for a request. * * Keeping things simple and that works! */ function __construct() { $this->conn = $this->getConnection(); } /** * If connection object is needed use this method and get access to it. * Otherwise, use the below methods for insert / update / etc. * * @return \mysqli */ public function getConnection() { $conn = new \mysqli(self::HOST, self::USERNAME, self::PASSWORD, self::DATABASENAME); if (mysqli_connect_errno()) { trigger_error("Problem with connecting to database."); } $conn->set_charset("utf8"); return $conn; } /** * To get database results * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function select($query, $paramType="", $paramArray=array()) { $stmt = $this->conn->prepare($query); if(!empty($paramType) && !empty($paramArray)) { $this->bindQueryParams($sql, $paramType, $paramArray); } $stmt->execute(); $result = $stmt->get_result(); if ($result->num_rows > 0) { while ($row = $result->fetch_assoc()) { $resultset[] = $row; } } if (! empty($resultset)) { return $resultset; } } /** * To insert * @param string $query * @param string $paramType * @param array $paramArray * @return int */ public function insert($query, $paramType, $paramArray) { $stmt = $this->conn->prepare($query); $this->bindQueryParams($stmt, $paramType, $paramArray); $stmt->execute(); $insertId = $stmt->insert_id; return $insertId; } /** * To execute query * @param string $query * @param string $paramType * @param array $paramArray */ public function execute($query, $paramType="", $paramArray=array()) { $stmt = $this->conn->prepare($query); if(!empty($paramType) && !empty($paramArray)) { $this->bindQueryParams($stmt, $paramType="", $paramArray=array()); } $stmt->execute(); } /** * 1. Prepares parameter binding * 2. Bind prameters to the sql statement * @param string $stmt * @param string $paramType * @param array $paramArray */ public function bindQueryParams($stmt, $paramType, $paramArray=array()) { $paramValueReference[] = & $paramType; for ($i = 0; $i < count($paramArray); $i ++) { $paramValueReference[] = & $paramArray[$i]; } call_user_func_array(array( $stmt, 'bind_param' ), $paramValueReference); } /** * To get database results * @param string $query * @param string $paramType * @param array $paramArray * @return array */ public function numRows($query, $paramType="", $paramArray=array()) { $stmt = $this->conn->prepare($query); if(!empty($paramType) && !empty($paramArray)) { $this->bindQueryParams($stmt, $paramType, $paramArray); } $stmt->execute(); $stmt->store_result(); $recordCount = $stmt->num_rows; return $recordCount; } } 

This is the HTML code to display the extracted images in the UI. I embed PHP code with this HTML to display the image path from the database dynamically.

The getAllImages() method fetches image results from the database. It returns an array of images extracted from the Excel. This array data iteration helps to render images in a gallery view.

<!doctype html> <html> <head> <link rel="stylesheet" type="text/css" href="CSS/style.css"> <title>Extract Images from URL in Excel using PHPSpreadSheet with PHP</title> </head> <body> <div id="gallery"> <div id="image-container"> <h2>Extract Images from URL in Excel using PHPSpreadSheet with PHP</h2> <?php if (! empty($message)) { ?> <div id="txtresponse"><?php echo $message; ?></div> <?php } ?> <ul id="image-list"> <?php if (! empty($imageResult)) { foreach ($imageResult as $k => $v) { ?> <li><img src="<?php echo $imageResult[$k]['image_path']; ?>" class="image-thumb" alt="<?php echo $imageResult[$k]['image_name'];?>"></li> <?php } } ?> </ul> </div> </div> </body> </html> 

After a successful image extract, this UI will acknowledge the user. It shows an appropriate message based on the image extract result.

If you extract an older excel that was already done, then the notification will say “No new images found”.

The following styles are used to present the extracted images in a gallery.

body { font-family: Arial; color: #212121; text-align: center; } #gallery { width: 1057px; margin: 0 auto; } #image-list { list-style-type: none; margin: 0; padding: 0; } #image-list li { margin: 10px 20px 10px 0px; display: inline-block; } #image-list li img { width: 250px; height: 155px; } #image-container { margin-bottom: 14px; } #txtresponse { padding: 10px 40px; border-radius: 3px; margin: 10px 0px 30px 0px; border: #ecdeaa 1px solid; color: #848483; background: #ffefb6; display: inline-block; } .btn-submit { padding: 10px 30px; background: #333; border: #E0E0E0 1px solid; color: #FFF; font-size: 0.9em; width: 100px; border-radius: 0px; cursor: pointer; position: absolute; } .image-thumb { background-color: grey; padding: 10px; } 

Database script

This SQL script is for creating the required database table in your environment. It has the create a statement of the tbl_images database table. This table is the storage the point to store the image local path.

Run this script before executing this example. You can also get the SQL script from the downloadable source code added with this article.

CREATE TABLE IF NOT EXISTS `tbl_images` ( `id` int(11) NOT NULL AUTO_INCREMENT, `image_name` varchar(50) NOT NULL, `image_path` varchar(50) NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=263 ; 

The screenshot below shows the image gallery output. These images are from the uploads folder of this example. This is the local location to store the extracted images from the database.

This screen shows the user acknowledgment message above the gallery view. This acknowledgment varies based on the input excel file data.

Extract Images from URL from the Excel Output

If the input excel is too older and extracted already, then the below message will notify the user.

No New Images

Hope this article helps you to image extract from URLs present in excel. The example presented is the simplest way of demonstrating image extract from Excel.

Download

↑ Back to Top

Posted on Leave a comment

HttpRepl: A command-line tool for interacting with RESTful HTTP services

Angelos Petropoulos

Angelos

The ASP.NET team has built a command-line tool called HttpRepl. It lets you browse and invoke HTTP services in a similar way to working with files and folders. You give it a starting point (a base URL) and then you can execute commands like “dir” and “cd” to navigate your way around the API:

C:\> dotnet httprepl http://localhost:65369/
(Disconnected)~ set base http://localhost:65369
Using swagger metadata from http://localhost:65369/swagger/v1/swagger.json http://localhost:65369/~ dir
. []
Fruits [get|post]
People [get|post] http://localhost:65369/~ cd People
/People [get|post] http://localhost:65369/People~ dir
. [get|post]
.. []
{id} [get]

Once you have identified the API you are interested in, you can use all the typical HTTP verbs against it. Here is an example of calling GET on http://localhost:65369/People as a continuation from before:

http://localhost:65369/People~ get
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 24 Jul 2019 20:33:07 GMT
Server: Microsoft-IIS/10.0
Transfer-Encoding: chunked
X-Powered-By: ASP.NET [ { "id": 1, "name": "Scott Hunter" }, { "id": 0, "name": "Scott Hanselman" }, { "id": 2, "name": "Scott Guthrie" }
]

Right now HttpRepl is being shipped as a .NET Core Global Tool, which means all you have to do to get it is run the following command on a machine with the .NET Core SDK installed:

C:\> dotnet tool install -g Microsoft.dotnet-httprepl --version “3.0.0-*”

The ASP.NET team built HttpRepl for the purpose of exploring and testing APIs. The idea was to make the experience of exploring and testing APIs through a command-line more convenient. What do you think about HttpRepl and what other uses do you envision for it? We would love to hear your opinion, please leave us a comment below or visit the project on GitHub. And for those wondering, HttpRepl’s official ship date is expected to align with .NET Core 3.0 GA.

Configure Visual Studio Code to launch HttpRepl on debug

You can configure Visual Studio to launch HttpRepl when debugging (along with your web app) by creating a new launch configuration as follows:

"version": "0.2.0", "compounds": [ { "name": ".NET Core REPL", "configurations": [ ".NET Core Launch (web)", "httprepl" ] } ], "configurations": [ { "name": "httprepl", "type": "coreclr", "request": "launch", "program": "dotnet", "args": ["httprepl", "http://localhost:5000"], "cwd": "${workspaceFolder}", "stopAtEntry": false, "console": "integratedTerminal" }, { "name": ".NET Core Launch (web)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/api.dll", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser "serverReadyAction": { "action": "openExternally", "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" }, "env": { "ASPNETCORE_ENVIRONMENT": "Development" }, "sourceFileMap": { "/Views": "${workspaceFolder}/Views" } }

Configure Visual Studio for Windows to launch HttpRepl on F5

You can configure Visual Studio to automatically launch HttpRepl when you F5 a project with the following simple steps:

The .exe for HttpRepl on Windows can be found in the following location:

%USERPROFILE%\.dotnet\tools\dotnet-httprepl.exe

Don’t forget to select it from the menu after adding it:

Next time you F5 your project, Visual Studio will automatically launch HttpRepl with the appropriate base URL (same URL that would have been passed to a browser, controlled through launchsettings):

Note: We are currently working on integrating HttpRepl into Visual Studio, which will give you an out-of-the box and more refined experience.

Configure Visual Studio for Mac to launch HttpRepl as a Custom Tool

In Visual Studio for Mac, you can configure a Custom Tool to open a new Terminal window and start httprepl. To configure this, go to Tools>Edit Custom Tools…

This will bring you to the External Tools dialog where you can add a new tool. To get started click the Add button to add a new tool. Here you will configure a new tool to launch a new Terminal instance and start the httprepl tool. Fill in the dialog with the following values.

  • Title: dotnet httprepl
  • Command: osascript
  • Arguments: -e ‘tell application “Terminal” to activate’ -e ‘tell application “Terminal”
    to do script “dotnet-httprepl”‘
  • Working directory: ${ProjectDir}

See the image below showing this new tool:

After clicking OK a new tool will appear in the Tools menu. To test your application with httprepl, start your application with Run>Start Debugging (or Run>Start without Debugging) and then start the httprepl with the new tool in the Tools menu:

When you invoke the tool a new Terminal window should appear in the foreground. From here you can set the base url to that of the api that you would like to test with set base. For example, see the image below that shows set base was executed and a get request will be executed next:

Give us feedback

It’s not HTTPie, it’s not Curl, but it’s also not PostMan. It’s something that you run and stays running and its aware of its current context. We find this experience valuable, but ultimately what matters the most is what you think. Please let us know your opinion by leaving comments below or on GitHub.

Angelos Petropoulos

Posted on Leave a comment

SHADERed Free GLSL/HLSL Shader Editor

Released a few weeks back, SHADERed is a free and open source editing environment for developing shaders, both HLSL and GLSL.  SHADERed enables you to create shaders on the fly with a real-time view of the results.  Currently it is Windows only, but the code is currently being ported from D3D to OpenG+SDL so this could change in the future.

Features of SHADERed include:

  • instantly see changes
  • vertex, pixel and geometry shaders
  • render states
  • audio file support
  • load obj 3d model files
  • load your own textures into shaders
  • render results to render texture (or screen)
  • create and edit your own input variables
  • shader statistics
  • code editor with compilation and error reporting
  • custom themes and templates

SHADERed is available on Github here.  The code is available under the liberal MIT license.  Compiled binaries for Windows are available here.  Check the video below to see SHADERed in action.

[youtube https://www.youtube.com/watch?v=eBIjnGeIGPI&w=853&h=480]

Design Programming


Posted on Leave a comment

ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 7

Daniel Roth

Daniel

.NET Core 3.0 Preview 7 is now available and it includes a bunch of new updates to ASP.NET Core and Blazor.

Here’s the list of what’s new in this preview:

  • Latest Visual Studio preview includes .NET Core 3.0 as the default runtime
  • Top level ASP.NET Core templates in Visual Studio
  • Simplified web templates
  • Attribute splatting for components
  • Data binding support for TypeConverters and generics
  • Clarified which directive attributes expect HTML vs C#
  • EventCounters
  • HTTPS in gRPC templates
  • gRPC Client Improvements
  • gRPC Metapackage
  • CLI tool for managing gRPC code generation

Please see the release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET Core 3.0 Preview 7 install the .NET Core 3.0 Preview 7 SDK

If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2019.

Note: .NET Core 3.0 Preview 7 requires Visual Studio 2019 16.3 Preview 1, which is being released later this week.

To install the latest client-side Blazor templates also run the following command:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview7.19365.7

Installing the Blazor Visual Studio extension is no longer required and it can be uninstalled if you’ve installed a previous version. Installing the Blazor WebAssembly templates from the command-line is now all you need to do to get them to show up in Visual Studio.

Upgrade an existing project

To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 7, follow the migrations steps in the ASP.NET Core docs.

Please also see the full list of breaking changes in ASP.NET Core 3.0.

To upgrade an existing ASP.NET Core 3.0 Preview 6 project to Preview 7:

  • Update Microsoft.AspNetCore.* package references to 3.0.0-preview7.19365.7.

That’s it! You should be ready to go.

Latest Visual Studio preview includes .NET Core 3.0 as the default runtime

The latest preview update for Visual Studio (16.3) includes .NET Core 3.0 as the default .NET Core runtime version. This means that if you install the latest preview of Visual Studio then you already have .NET Core 3.0. New project by default will target .NET Core 3.0

Top level ASP.NET Core templates in Visual Studio

The ASP.NET Core templates now show up as top level templates in Visual Studio in the “Create a new project” dialog.

ASP.NET Core templates

This means you can now search for the various ASP.NET Core templates and filter by project type (web, service, library, etc.) to find the one you want to use.

Simplified web templates

We’ve taken some steps to further simplify the web app templates to reduce the amount of code that is frequently just removed.

Specifically:

  • The cookie consent UI is no longer included in the web app templates by default.
  • Scripts and related static assets are now referenced as local files instead of using CDNs based on the current environment.

We will provide samples and documentation for adding these features to new apps as needed.

Attribute splatting for components

Components can now capture and render additional attributes in addition to the component’s declared parameters. Additional attributes can be captured in a dictionary and then “splatted” onto an element as part of the component’s rendering using the new @attributes Razor directive. This feature is especially valuable when defining a component that produces a markup element that supports a variety of customizations. For instance if you were defining a component that produces an <input> element, it would be tedious to define all of the attributes <input> supports like maxlength or placeholder as component parameters.

Accepting arbitrary parameters

To define a component that accepts arbitrary attributes define a component parameter using the [Parameter] attribute with the CaptureUnmatchedAttributes property set to true. The type of the parameter must be assignable from Dictionary<string, object>. This means that IEnumerable<KeyValuePair<string, object>> or IReadOnlyDictionary<string, object> are also options.

@code { [Parameter(CaptureUnmatchedAttributes = true)] Dictionary<string, object> Attributes { get; set; }
}

The CaptureUnmatchedAttributes property on [Parameter] allows that parameter to match all attributes that do not match any other parameter. A component can only define a single parameter with CaptureUnmatchedAttributes.

Using @attributes to render arbitrary attributes

A component can pass arbitrary attributes to another component or markup element using the @attributes directive attribute. The @attributes directive allows you to specify a collection of attributes to pass to a markup element or component. This is valuable because the set of key-value-pairs specified as attributes can come from a .NET collection and do not need to be specified in the source code of the component.

<input class="form-field" @attributes="Attributes" type="text" /> @code { [Parameter(CaptureUnmatchedAttributes = true)] Dictionary<string, object> Attributes { get; set; }
}

Using the @attributes directive the contents of the Attribute property get “splatted” onto the input element. If this results in duplicate attributes, then evaluation of attributes occurs from left to right. In the above example if Attributes also contained a value for class it would supersede class="form-field". If Attributes contained a value for type then that would be superseded by type="text".

Data binding support for TypeConverters and generics

Blazor now supports data binding to types that have a string TypeConverter. Many built-in framework types, like Guid and TimeSpan have a string TypeConverter, or you can define custom types with a string TypeConverter yourself. These types now work seamlessly with data binding:

<input @bind="guid" /> <p>@guid</p> @code { Guid guid;
}

Data binding also now works great with generics. In generic components you can now bind to types specified using generic type parameters.

@typeparam T <input @bind="value" /> <p>@value</p> @code { T value;
}

Clarified which directive attributes expect HTML vs C

In Preview 6 we introduced directive attributes as a common syntax for Razor compiler related features like specifying event handlers (@onclick) and data binding (@bind). In this update we’ve cleaned up which of the built-in directive attributes expect C# and HTML. Specifically, event handlers now expect C# values so a leading @ character is no longer required when specifying the event handler value:

@* Before *@
<button @onclick="@OnClick">Click me</button> @* After *@
<button @onclick="OnClick">Click me</button>

EventCounters

In place of Windows perf counters, .NET Core introduced a new way of emitting metrics via EventCounters. In preview7, we now emit EventCounters ASP.NET Core. You can use the dotnet counters global tool to view the metrics we emit.

Install the latest preview of dotnet counters by running the following command:

dotnet tool install --global dotnet-counters --version 3.0.0-preview7.19365.2

Hosting

The Hosting EventSourceProvider (Microsoft.AspNetCore.Hosting) now emits the following request counters:

  • requests-per-second
  • total-requests
  • current-requests
  • failed-requests

SignalR

In addition to hosting, SignalR (Microsoft.AspNetCore.Http.Connections) also emits the following connection counters:

  • connections-started
  • connections-stopped
  • connections-timed-out
  • connections-duration

To view all the counters emitted by ASP.NET Core, you can start dotnet counters and specify the desired provider. The example below shows the output when subscribing to events emitted by the Microsoft.AspNetCore.Hosting and System.Runtime providers.

dotnet counters monitor -p <PID> Microsoft.AspNetCore.Hosting System.Runtime

D8GX-5oV4AASKwM

New Package ID for SignalR’s JavaScript Client in NPM

The Azure SignalR Service made it easier for non-.NET developers to make use of SignalR’s real-time capabilities. A frequent question we would get from potential customers who wanted to enable their applications with SignalR via the Azure SignalR Service was “does it only work with ASP.NET?” The former identity of the ASP.NET Core SignalR – which included the @aspnet organization on NPM, only further confused new SignalR users.

To mitigate this confusion, beginning with 3.0.0-preview7, the SignalR JavaScript client will change from being @aspnet/signalr to @microsoft/signalr. To react to this change, you will need to change your references in package.json files, require statements, and ECMAScript import statements. If you’re interested in providing feedback on this move or to learn the thought process the team went through to make the change, read and/or contribute to this GitHub issue where the team engaged in an open discussion with the community.

New Customizable SignalR Hub Method Authorization

With Preview 7, SignalR now provides a custom resource to authorization handlers when a hub method requires authorization. The resource is an instance of HubInvocationContext. The HubInvocationContext includes the HubCallerContext, the name of the hub method being invoked, and the arguments to the hub method.

Consider the example of a chat room allowing multiple organization sign-in via Azure Active Directory. Anyone with a Microsoft account can sign in to chat, but only members of the owning organization should be able to ban users or view users’ chat histories. Furthermore, we might want to restrict certain functionality from certain users. Using the updated features in Preview 7, this is entirely possible. Note how the DomainRestrictedRequirement serves as a custom IAuthorizationRequirement. Now that the HubInvocationContext resource parameter is being passed in, the internal logic can inspect the context in which the Hub is being called and make decisions on allowing the user to execute individual Hub methods.

public class DomainRestrictedRequirement : AuthorizationHandler<DomainRestrictedRequirement, HubInvocationContext>, IAuthorizationRequirement
{ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DomainRestrictedRequirement requirement, HubInvocationContext resource) { if (IsUserAllowedToDoThis(resource.HubMethodName, context.User.Identity.Name) && context.User != null && context.User.Identity != null && context.User.Identity.Name.EndsWith("@jabbr.net", StringComparison.OrdinalIgnoreCase)) { context.Succeed(requirement); } return Task.CompletedTask; } private bool IsUserAllowedToDoThis(string hubMethodName, string currentUsername) { return !(currentUsername.Equals("bob42@jabbr.net", StringComparison.OrdinalIgnoreCase) && hubMethodName.Equals("banUser", StringComparison.OrdinalIgnoreCase)); }
}

Now, individual Hub methods can be decorated with the name of the policy the code will need to check at run-time. As clients attempt to call individual Hub methods, the DomainRestrictedRequirement handler will run and control access to the methods. Based on the way the DomainRestrictedRequirement controls access, all logged-in users should be able to call the SendMessage method, only users who’ve logged in with a @jabbr.net email address will be able to view users’ histories, and – with the exception of bob42@jabbr.net – will be able to ban users from the chat room.

[Authorize]
public class ChatHub : Hub
{ public void SendMessage(string message) { } [Authorize("DomainRestricted")] public void BanUser(string username) { } [Authorize("DomainRestricted")] public void ViewUserHistory(string username) { }
}

Creating the DomainRestricted policy is as simple as wiring it up using the authorization middleware. In Startup.cs, add the new policy, providing the custom DomainRestrictedRequirement requirement as a parameter.

services .AddAuthorization(options => { options.AddPolicy("DomainRestricted", policy => { policy.Requirements.Add(new DomainRestrictedRequirement()); }); });

It must be noted that in this example, the DomainRestrictedRequirement class is not only a IAuthorizationRequirement but also it’s own AuthorizationHandler for that requirement. It is fine to split these into separate classes to separate concerns. Yet, in this way, there’s no need to inject the AuthorizationHandler during Startup, since the requirement and the handler are the same thing, there’s no need to inject the handler separately.

HTTPS in gRPC templates

The gRPC templates have been now been updated to use HTTPS by default. At development time, we continue the same certificate generated by the dotnet dev-certs tool and during production, you will still need to supply your own certificate.

gRPC Client Improvements

The managed gRPC client (Grpc.Net.Client) has been updated to target .NET Standard 2.1 and no longer depends on types present only in .NET Core 3.0. This potentially gives us the ability to run on other platforms in the future.

gRPC Metapackage

In 3.0.0-preview7, we’ve introduced a new package Grpc.AspNetCore that transitively references all other runtime and tooling dependencies required for building gRPC projects. Reasoning about a single package version for the metapackage should make it easier for developers to deal with as opposed multiple dependencies that version independently.

CLI tool for managing gRPC code generation

The new dotnet-grpc global tool makes it easier to manage protobuf files and their code generation settings. The global tool manages adding and removing protobuf files as well adding the required package references required to build and run gRPC applications.

Install the latest preview of dotnet-grpc by running the following command:

dotnet tool install --global dotnet-grpc --version 0.1.22-pre2

As an example, you can run following commands to generate a protobuf file and add it to your project for code generation. If you attempt this on a non-web project, we will default to generating a client and add the required package dependencies.

dotnet new proto -o .\Protos\mailbox.proto
dotnet grpc add-file .\Protos\mailbox.proto

Give feedback

We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core and Blazor!

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel   

Posted on Leave a comment

Configuring a Server-side Blazor app with Azure App Configuration

Avatar

With .NET Core 3.0 Preview 6, we added authentication & authorization support to server-side Blazor apps. It only takes a matter of seconds to wire up an app to Azure Active Directory with support for single or multiple organizations. Once the project is created, it contains all the configuration elements in its appsettings.json to function. This is great, but in a team environment – or in a distributed topology – configuration files lead to all sorts of problems. In this post, we’ll take a look at how we can extract those configuration values out of JSON files and into an Azure App Configuration instance, where they can be used by other teammates or apps.

Setting up Multi-org Authentication

In the .NET Core 3.0 Preview 6 blog post we explored how to use the Individual User Accounts option in the authentication dialog to set up a Blazor app with ASP.NET Identity, so we won’t go into too much detail. Essentially, you click the Change link during project creation.

Click Change Auth during project creation

In this example I’ll be using an Azure Active Directory application to allow anyone with a Microsoft account to log into the app, so I’ll select Work or School Accounts and then select Cloud – Multiple Organizations in the Change Authentication dialog.

The Visual Studio add authentication dialog.

Once the project is created, my AzureAD configuration node contains the 3 key pieces of information my app’s code will need to authenticate against Azure Active Directory; my tenant URL, the client ID for the AAD app Visual Studio created for me during the project’s creation, and the callback URI so users can get back to my app once they’ve authenticated.

The appsettings.json inclusive of the settings.

Whilst this is conveniently placed here in my appsettings.json file, it’d be more convenient if I didn’t need any local configuration files. Having a centralized configuration-management solution would be easier to manage, as well as give me the ability to keep my config out of source control, should there come a point when things like connection strings need to be shared amongst developers.

Azure App Configuration

Azure App Configuration is a cloud-based solution for managing all of your configuration values. Once I have an Azure App Configuration instance set up in my subscription, adding the configuration settings is simple. By default, they’re hidden from view, but I can click Show Values or select an individual setting for editing or viewing.

The config values in Azure App Configuration

Convenient .NET Core IConfiguration Integration

The Azure App Configuration team has shipped a NuGet package containing extensions to ASP.NET and .NET Core that enable developers the ability of using the service, but without needing to change all your code that already makes use of IConfiguration. To start with, install the Microsoft.Extensions.Configuration.AzureAppConfiguration NuGet package.

Adding the NuGet Package for Azure App Configuration

You’ll need to copy the connection string from the Azure Portal to enable connectivity between your app and Azure App Configuration.

Copying the Azure App Configuration connection string

Once that value has been copied, you can use it with either dotnet user-secrets to configure your app, or using a debug-time environment variable. Though it seems like we’ve created yet one more configuration value to track, think about it this way: this is the only value you’ll have to set using an environment variable; all your other configuration can be set via Azure App Configuration in the portal.

Setting up the Azure App Configuration connection string in an environment variable

Using the Azure App Configuration Provider for .NET Core

Once the NuGet package is installed, the code to instruct my .NET Core code to use Azure App Configuration whenever it reads any configuration values from IConfiguration is simple. In Program.cs I’ll call the ConfigureAppConfiguration middleware method, then use the AddAzureAppConfiguration extension method to get the connection string from my ASPNETCORE_AzureAppConfigConnectionString environment variable. If the environment variable isn’t set, the call will noop and the other configuration providers will do the work.

This is great, because I won’t even need to change existing – or in this case, template-generated code – I just tell my app to use Azure App Configuration and I’m off to the races. The full update to Program.cs is shown below.

// using Microsoft.Extensions.Configuration.AzureAppConfiguration; public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.AddAzureAppConfiguration(options => { var azureAppConfigConnectionString = hostingContext.Configuration["AzureAppConfigConnectionString"]; options.Connect(azureAppConfigConnectionString); }); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });

When I run the app, it first reaches out to Azure App Configuration to get all the settings it needs to run and then works as if it were configured locally using appsettings.json. As long as my teammates or other services needing these values have the connection string to the Azure App Configuration instance holding the settings for the app, they’re good.

Running the authenticated app

Now, I can remove the configuration values entirely from the appsettings.json file. If I want to control the logging behavior using Azure App Configuration, I could move these left-over settings out, too. Even though I’ll be using Azure App Configuration as, the other providers are still there.

The appsettings.json with the settings removed.

Dynamic Re-loading

Log levels are a good example of how the Azure App Configuration service can enable dynamic reloading of configuration settings you might need to tweak frequently. By moving my logging configuration into Azure App Configuration, I can change the log level right in the portal. In Program.cs, I can use the Watch method to specify which configuration settings I’ll want to reload when they change.

public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.AddAzureAppConfiguration(options => { var azureAppConfigConnectionString = hostingContext.Configuration["AzureAppConfigConnectionString"]; options.Connect(azureAppConfigConnectionString) .Watch("Logging:LogLevel:Default") .Watch("Logging:LogLevel:Microsoft") .Watch("Logging:LogLevel:Microsoft.Hosting.Lifetime"); }); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });

The default load-time is 30 seconds, but now, should I need to turn up the volume on my logs to get a better view of what’s happening in my site, I don’t need to re-deploy or even stop my site. Simply changing the values in the portal will be enough – 30 seconds later the values will be re-loaded from Azure App Configuration and my logging will be more verbose.

Changing configuration values in the portal

Configuration Source Ordering

The JsonConfigurationSource configuration sources – those which load settings from appsettings.json and appsettings.{Environment}.json – are loaded during the call to CreateDefaultBuilder. So, by the time I call AddAzureAppConfiguration to load in the AzureAppConfigurationSource, the JSON file providers are already in the configuration sources list.

The importance of ordering is evident here; should I want to override the configuration values coming from Azure App Configuration with my local appsettings.json or appsettings.Development.json files, I’d need to re-order the providers in the call to ConfigureAppConfiguration. Otherwise, the JSON file values will be loaded first, then the last source (the one that will “win”) will be the Azure App Configuration source.

Try it Out

Any multi-node or microservice-based application topology benefits from centralized configuration, and teams benefit from it by not having to keep track of so many configuration settings, environment variables, and so on. Take a look over the Azure App Configuration documentation. You’ll see that there are a multitude of other features, like Feature Flags and dark deployment support. Then, create an instance and try wiring your existing ASP.NET Code up to read configuration values from the cloud.

Avatar
Brady Gaster

Senior Program Manager, ASP.NET Core

Follow    

Posted on Leave a comment

Forwarded Headers Middleware Updates in .NET Core 3.0 preview 6

Avatar

With the ASP.NET Core 2.1 release, we included UseHsts and UseHttpRedirection by default. These methods put a site into an infinite loop if deployed to an Azure Linux App Service, Azure Linux virtual machine (VM), or behind any other reverse proxy besides IIS. TLS is terminated by the reverse proxy, and Kestrel isn’t made aware of the correct request scheme.

OAuth and OIDC also fail in this configuration because they generate incorrect redirects. Calls to UseIISIntegration add and configure forwarded headers middleware when running behind IIS, but there’s no matching automatic configuration for Linux (Apache or Nginx integration). The fix for this issue is discussed in more detail in the doc article Forward the scheme for Linux and non-IIS reverse proxies.

Configuration-only Wire-up in Preview 6

With the updates in .NET Core 3 preview 6, you no longer need to call the middleware explicitly, as the host logic has been pre-wired to enable the Forwarded Headers Middleware by default as long as the ASPNETCORE_FORWARDEDHEADERS_ENABLED environment variable has been set to true. Turning on the Forwarded Headers Middleware is as simple as setting the ASPNETCORE_FORWARDEDHEADERS_ENABLED setting in the Azure Portal’s configuration blade for any App Service running on Linux or in a container.

Enabling the Forwarded Headers Middleware via config

Once this setting is set to true, the middleware starts working, and features dependent on Request.IsHttps resulting to true begin to function as expected.

Resolving the issue with ASP.NET Core 2.x Apps Today

If you’re currently building an ASP.NET Core 2.x app and want to run it on App Service for Linux now, there’s a workaround that will be future-proof when the updates come out for 3.0.

To forward the scheme from the proxy in non-IIS scenarios, add and configure Forwarded Headers Middleware. In Startup.cs, use the following code:

// using Microsoft.AspNetCore.HttpOverrides; public void ConfigureServices(IServiceCollection services)
{ if (string.Equals( Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"), "true", StringComparison.OrdinalIgnoreCase)) { services.Configure<forwardedheadersoptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // Only loopback proxies are allowed by default. // Clear that restriction because forwarders are enabled by explicit // configuration. options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); }
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ app.UseForwardedHeaders();
}

If you enable your ASP.NET Core 2.x apps with this workaround today, when you’re ready to upgrade to 3.0, you’ll already have the right configuration setting in place.

Base Image Update

The base images used by the App Service team to streamline the creation of ASP.NET Core apps will soon be updated so that the ASPNETCORE_FORWARDEDHEADERS_ENABLED environment variable will be set to true. Once they’re updated, you won’t even need to explicitly set the environment variable; it’ll be enabled by default.

Try it Out

If you’re new to building ASP.NET Core apps using containers, the App Service options for Linux and Container-based hosting offer a great place to get started. The docs are loaded with guidance and examples, from how to Run a .NET Core app in App Service on Linux to accessing a SQL Server Database from an ASP.NET Core app running in App Service Linux.

Avatar
Brady Gaster

Senior Program Manager, ASP.NET Core

Follow    

Posted on Leave a comment

Humble Programming Book Bundle By Packt

Humble are currently running a new bundle absolutely loaded with programming books and videos by Packt Press.  The Humble Book Bundle: Programming by Packt.  The bundle includes books on C++, C#, Java, JavaScript and Go programming.

As always, Humble Bundles are split into different tiers.  The tiers of this bundle are:

1$

  • Understanding Software
  • C# 7 and .NET Core Cookbook
  • C++ Programming by Example
  • Go Cookbook
  • Learning JavaScript Data Structures and Algorithms

8$

  • Modern C++ Programming Cookbook
  • Advanced Go Programming in 7 Days (VIDEO)
  • Java 11 Cookbook
  • Modern JavaScript From the Beginning (VIDEO)
  • Python Programming Blueprints
  • Functional Python Programming
  • Python 3 Object-Oriented Programming

15$

  • Software Architect’s Handbook
  • Learning C# 8 and .NET Core 3.0 (VIDEO)
  • C++ High Performance
  • The Modern C++ Challenge
  • Mastering Go
  • Java 11 in 7 Days
  • Learning Java By Building Android Games
  • Hands-On Object Oriented Programming with Java 11 (VIDEO)
  • Learn Java 12 Programming
  • Python for Beginners (VIDEO)
  • Clean Code in Python
  • Expert Python Programming
  • C# 7.1 and .NET Core 2.0 – Modern Cross Platform Development

Purchasing using this link give you the opportunity to support this site, which if you do, thank you very much!

[youtube https://www.youtube.com/watch?v=HX8AIq5j5oI&w=853&h=480]

GameDev News Programming


Posted on Leave a comment

nCine 2D Open Source Game Engine

The nCine Engine is a C++ powered, open source MIT licensed 2D game engine that has been under development for over 7 years.  It is a lower level code based framework, although it does support Lua scripting out of the box.  The engine also integrates the ImGui framework making creating tools and UIs a breeze.  The nCine engine works on Windows, Linux, Mac and Android.

Highlighted features include:

  • ImGui debug overlay and profilers
  • Lua integration for scripting
  • OPenGL 3.3/OpenGL ES 3.0
  • Spritesheet based animated sprites
  • Scengraph based transformations
  • Particle simulation with affectors
  • Sound and music playback
  • Text rendering with kerning
  • Support for multiple texture formats
  • Profiler graphs and statistics
  • Works on multiple platforms
  • Template containers and algorithms
  • Fully C++11 compliant codebase
  • High precision monotonic timers
  • Atomic counters
  • Thread pool creation, synchronization and affinity assignment
  • Basic math lbrary for vectors, 4×4 matrices and quaternions
  • Logging system with multiple levels and console or file output
  • GLFW 3 or SDL 2 for window and input on PC
  • Joystick support with hot swap and gamepad mappings
  • Android assets support
  • Google Test based unit tests with coverage checked with Gcovr
  • Microbenchmarked with the Google Benchmark support library
  • Doxygen based documentation with Graphviz class diagrams
  • Periodically checked with Cppcheck and Valgrind
  • Periodically linted with clang-format (previously with Artistic Style and Uncrustify)
  • Instrumentation for the Tracy frame profiler

With so many game engines on the market, you may be wondering… why another one?  Well the author explains exactly that right here.  The cCine project is hosted on GitHub and provides a Pong demo to get you started, implemented in both C++ and Lua.

[youtube https://www.youtube.com/watch?v=RdwuMKq2BmU&w=853&h=480]

GameDev News Programming


Posted on Leave a comment

ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 6

Daniel Roth

Daniel

.NET Core 3.0 Preview 6 is now available and it includes a bunch of new updates to ASP.NET Core and Blazor.

Here’s the list of what’s new in this preview:

  • New Razor features: @attribute, @code, @key, @namespace, markup in @functions
  • Blazor directive attributes
  • Authentication & authorization support for Blazor apps
  • Static assets in Razor class libraries
  • Json.NET no longer referenced in project templates
  • Certificate and Kerberos Authentication
  • SignalR Auto-reconnect
  • Managed gRPC Client
  • gRPC Client Factory
  • gRPC Interceptors

Please see the release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET Core 3.0 Preview 6 install the .NET Core 3.0 Preview 6 SDK

If you’re on Windows using Visual Studio, you also need to install the latest preview of Visual Studio 2019.

For the latest client-side Blazor templates also install the latest Blazor extension from the Visual Studio Marketplace.

Upgrade an existing project

To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 6, follow the migrations steps in the ASP.NET Core docs.

Please also see the full list of breaking changes in ASP.NET Core 3.0.

To upgrade an existing ASP.NET Core 3.0 Preview 5 project to Preview 6:

  • Update Microsoft.AspNetCore.* package references to 3.0.0-preview6.19307.2
  • In Blazor apps:
    • Rename @functions to @code
    • Update Blazor specific attributes and event handlers to use the new directive attribute syntax (see below)
    • Remove any call to app.UseBlazor<TStartup>() and instead add a call to app.UseClientSideBlazorFiles<TStartup>() before the call to app.UseRouting(). Also add a call to endpoints.MapFallbackToClientSideBlazor<TStartup>("index.html") in the call to app.UseEndpoints().

Before

app.UseRouting(); app.UseEndpoints(endpoints =>
{ endpoints.MapDefaultControllerRoute();
}); app.UseBlazor<Client.Startup>();

After

app.UseClientSideBlazorFiles<Client.Startup>(); app.UseRouting(); app.UseEndpoints(endpoints =>
{ endpoints.MapDefaultControllerRoute(); endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});

New Razor features

We’ve added support for the following new Razor language features in this release.

@attribute

The new @attribute directive adds the specified attribute to the generated class.

@attribute [Authorize]

@code

The new @code directive is used in .razor files (not supported in .cshtml files) to specify a code block to add to the generated class as additional members. It’s equivalent to @functions, but now with a better name.

@code { int currentCount = 0; void IncrementCount() { currentCount++; }
}

@key

The new @key directive attribute is used in .razor files to specify a value (any object or unique identifier) that the Blazor diffing algorithm can use to preserve elements or components in a list.

@foreach (var flight in Flights) { }

To understand why this feature is needed, consider rendering a list of cards with flight details without this feature:

@foreach (var flight in Flights) { }

If you add a new flight into the middle of the Flights list the existing DetailsCard instances should remain unaffected and one new DetailsCard should be inserted into the rendered output.

To visualize this, if Flights previously contained [F0, F1, F2], then this is the before state:

  • DetailsCard0, with Flight=F0
  • DetailsCard1, with Flight=F1
  • DetailsCard2, with Flight=F2

… and this is the desired after state, given we insert a new item FNew at index 1:

  • DetailsCard0, with Flight=F0
  • DetailsCardNew, with Flight=FNew
  • DetailsCard1, with Flight=F1
  • DetailsCard2, with Flight=F2

However, the actual after state this:

  • DetailsCard0, with Flight=F0
  • DetailsCard1, with Flight=FNew
  • DetailsCard2, with Flight=F1
  • DetailsCardNew, with Flight=F2

The system has no way to know that DetailsCard2 or DetailsCard3 should preserve their associations with their older Flight instances, so it just re-associates them with whatever Flight matches their position in the list. As a result, DetailsCard1 and DetailsCard2 rebuild themselves completely using new data, which is wasteful and sometimes even leads to user-visible problems (e.g., input focus is unexpectedly lost).

By adding keys using @key the diffing algorithm can associate the old and new elements or components.

@namespace

Specifies the namespace for the generated class or the namespace prefix when used in an _Imports.razor file. The @namespace directive works today in pages and views (.cshtml) apps, but is now it is also supported with components (.razor).

@namespace MyNamespace

Markup in @functions and local functions

In views and pages (.cshtml files) you can now add markup inside of methods in the @functions block and in local functions.

@{ GreetPerson(person); } @functions { void GreetPerson(Person person) { <p>Hello, <em>@person.Name!</em></p> }
}

Blazor directive attributes

Blazor uses a variety of attributes for influencing how components get compiled (e.g. ref, bind, event handlers, etc.). These attributes have been added organically to Blazor over time and use different syntaxes. In this Blazor release we’ve standardized on a common syntax for directive attributes. This makes the Razor syntax used by Blazor more consistent and predictable. It also paves the way for future extensibility.

Directive attributes all follow the following syntax where the values in parenthesis are optional:

@directive(-suffix(:name))(="value")

Some valid examples:

<!-- directive -->
...
<!-- directive with key/value arg-->
...
<!-- directive with suffix -->
<!-- directive with suffix and key/value arg-->

All of the Blazor built-in directive attributes have been updated to use this new syntax as described below.

Event handlers

Specifying event handlers in Blazor now uses the new directive attribute syntax instead of the normal HTML syntax. The syntax is similar to the HTML syntax, but now with a leading @ character. This makes C# event handlers distinct from JS event handlers.

<button @onclick="@Clicked">Click me!</button>

When specifying a delegate for C# event handler the @ prefix is currently still required on the attribute value, but we expect to remove this requirement in a future update.

In the future we also expect to use the directive attribute syntax to support additional features for event handlers. For example, stopping event propagation will likely look something like this (not implemented yet, but it gives you an idea of scenarios now enabled by directive attributes):

<button @onclick="Clicked" @onclick:stopPropagation>Click me!</button>

Bind

<input @bind="myValue">...</input>
<input @bind="myValue" @bind:format="mm/dd">...</input>
<MyButton @bind-Value="myValue">...</MyButton>

Key

...

Ref

<button @ref="myButton">...</button>

Authentication & authorization support for Blazor apps

Blazor now has built-in support for handling authentication and authorization. The server-side Blazor template now supports options for enabling all of the standard authentication configurations using ASP.NET Core Identity, Azure AD, and Azure AD B2C. We haven’t updated the Blazor WebAssembly templates to support these options yet, but we plan to do so after .NET Core 3.0 has shipped.

To create a new Blazor app with authentication enabled:

  1. Create a new Blazor (server-side) project and select the link to change the authentication configuration. For example, select “Individual User Accounts” and “Store user accounts in-app” to use Blazor with ASP.NET Core Identity:

    Blazor authentication

  2. Run the app. The app includes links in the top row for registering as a new user and logging in.

    Blazor authentication running

  3. Select the Register link to register a new user.

    Blazor authentication register

  4. Select “Apply Migrations” to apply the ASP.NET Core Identity migrations to the database.

    Blazor authentication apply migrations

  5. You should now be logged in.

    Blazor authentication logged in

  6. Select your user name to edit your user profile.

    Blazor authentication edit profile

In the Blazor app, authentication and authorization are configured in the Startup class using the standard ASP.NET Core middleware.

app.UseRouting(); app.UseAuthentication();
app.UseAuthorization(); app.UseEndpoints(endpoints =>
{ endpoints.MapControllers(); endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host");
});

When using ASP.NET Core Identity all of the identity related UI concerns are handled by the framework provided default identity UI.

services.AddDefaultIdentity<IdentityUser>() .AddEntityFrameworkStores<ApplicationDbContext>();

The authentication related links in top row of the app are rendered using the new built-in AuthorizeView component, which displays different content depending on the authentication state.

LoginDisplay.razor

<AuthorizeView> <Authorized> <a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a> <a href="Identity/Account/LogOut">Log out</a> </Authorized> <NotAuthorized> <a href="Identity/Account/Register">Register</a> <a href="Identity/Account/Login">Log in</a> </NotAuthorized>
</AuthorizeView>

The AuthorizeView component will only display its child content when the user is authorized. Alternatively, the AuthorizeView takes parameters for specifying different templates when the user is Authorized, NotAuthorized, or Authorizing. The current authentication state is passed to these templates through the implicit context parameter. You can also specify specific roles or an authorization policy on the AuthorizeView that the user must satisfy to see the authorized view.

To authorize access to specific pages in a Blazor app, use the normal [Authorize] attribute. You can apply the [Authorize] attribute to a component using the new @attribute directive.

@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@page "/fetchdata"

To specify what content to display on a page that requires authorization when the user isn’t authorized or is still in the processing of authorizing, use the NotAuthorizedContent and AuthorizingContent parameters on the Router component. These Router parameters are only support in client-side Blazor for this release, but they will be enabled for server-side Blazor in a future update.

The new AuthenticationStateProvider service make the authentication state available to Blazor apps in a uniform way whether they run on the server or client-side in the browser. In server-side Blazor apps the AuthenticationStateProvider surfaces the user from the HttpContext that established the connection to the server. Client-side Blazor apps can configure a custom AuthenticationStateProvider as appropriate for that application. For example, it might retrieve the current user information by querying an endpoint on the server.

The authentication state is made available to the app as a cascading value (Task<AuthenticationState>) using the CascadingAuthenticationState component. This cascading value is then used by the AuthorizeView and Router components to authorize access to specific parts of the UI.

App.razor

<CascadingAuthenticationState> <Router AppAssembly="typeof(Startup).Assembly"> <NotFoundContent> <p>Sorry, there's nothing at this address.</p> </NotFoundContent> </Router>
</CascadingAuthenticationState>

Static assets in Razor class libraries

Razor class libraries can now include static assets like JavaScript, CSS, and images. These static assets can then be included in ASP.NET Core apps by referencing the Razor class library project or via a package reference.

To include static assets in a Razor class library add a wwwroot folder to the Razor class library and include any required files in that folder.

When a Razor class library with static assets is referenced either as a project reference or as a package, the static assets from the library are made available to the app under the path prefix _content/{LIBRARY NAME}/. The static assets stay in their original folders and any changes to the content of static assets in the Razor class libraries are reflected in the app without rebuilding.

When the app is published, the companion assets from all referenced Razor class libraries are copied into the wwwroot folder of the published app under the same prefix.

To try out using static assets from a Razor class library:

  1. Create a default ASP.NET Core Web App.

    dotnet new webapp -o WebApp1
    
  2. Create a Razor class library and reference it from the web app.

    dotnet new razorclasslib -o RazorLib1
    dotnet add WebApp1 reference RazorLib1
    
  3. Add a wwwroot folder to the Razor class library and include a JavaScript file that logs a simple message to the console.

    cd RazorLib1
    mkdir wwwroot
    

    hello.js

    console.log("Hello from RazorLib1!");
    
  4. Reference the script file from Index.cshtml in the web app.

    http://_content/RazorLib1/hello.js
    
  5. Run the app and look for the output in the browser console.

    Hello from RazorLib1!
    

Projects now use System.Text.Json by default

New ASP.NET Core projects will now use System.Text.Json for JSON handling by default. In this release we removed Json.NET (Newtonsoft.Json) from the project templates. To enable support for using Json.NET, add the Microsoft.AspNetCore.Mvc.NewtonsoftJson package to your project and add a call to AddNewtonsoftJson() following code in your Startup.ConfigureServices method. For example:

services.AddMvc() .AddNewtonsoftJson();

Certificate and Kerberos authentication

Preview 6 brings Certificate and Kerberos authentication to ASP.NET Core.

Certificate authentication requires you to configure your server to accept certificates, and then add the authentication middleware in Startup.Configure and the certificate authentication service in Startup.ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{ services.AddAuthentication( CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(); // All the other service configuration.
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAuthentication(); // All the other app configuration.
}

Options for certificate authentication include the ability to accept self-signed certificates, check for certificate revocation, and check that the proffered certificate has the right usage flags in it. A default user principal is constructed from the certificate properties, with an event that enables you to supplement or replace the principal. All the options, and instructions on how to configure common hosts for certificate authentication can be found in the documentation.

We’ve also extended “Windows Authentication” onto Linux and macOS. Previously this authentication type was limited to IIS and HttpSys, but now Kestrel has the ability to use Negotiate, Kerberos, and NTLM on Windows, Linux, and macOS for Windows domain joined hosts by using the Microsoft.AspNetCore.Authentication.Negotiate nuget package. As with the other authentication services you configure authentication app wide, then configure the service:

public void ConfigureServices(IServiceCollection services)
{ services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate();
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAuthentication(); // All the other app configuration.
}

Your host must be configured correctly. Windows hosts must have SPNs added to the user account hosting the application. Linux and macOS machines must be joined to the domain, then SPNs must be created for the web process, as well as keytab files generated and configured on the host machine. Full instructions are given in the documentation.

SignalR Auto-reconnect

This preview release, available now via npm install @aspnet/signalr@next and in the .NET Core SignalR Client, includes a new automatic reconnection feature. With this release we’ve added the withAutomaticReconnect() method to the HubConnectionBuilder. By default, the client will try to reconnect immediately and after 2, 10, and 30 seconds. Enlisting in automatic reconnect is opt-in, but simple via this new method.

const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect() .build();

By passing an array of millisecond-based durations to the method, you can be very granular about how your reconnection attempts occur over time.

.withAutomaticReconnect([0, 3000, 5000, 10000, 15000, 30000])
//.withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior

Or you can pass in an implementation of a custom reconnect policy that gives you full control.

If the reconnection fails after the 30-second point (or whatever you’ve set as your maximum), the client presumes the connection is offline and stops trying to reconnect. During these reconnection attempts you’ll want to update your application UI to provide cues to the user that the reconnection is being attempted.

Reconnection Event Handlers

To make this easier, we’ve expanded the SignalR client API to include onreconnecting and onreconnected event handlers. The first of these handlers, onreconnecting, gives developers a good opportunity to disable UI or to let users know the app is offline.

connection.onreconnecting((error) => { const status = `Connection lost due to error "${error}". Reconnecting.`; document.getElementById("messageInput").disabled = true; document.getElementById("sendButton").disabled = true; document.getElementById("connectionStatus").innerText = status;
});

Likewise, the onreconnected handler gives developers an opportunity to update the UI once the connection is reestablished.

connection.onreconnected((connectionId) => { const status = `Connection reestablished. Connected.`; document.getElementById("messageInput").disabled = false; document.getElementById("sendButton").disabled = false; document.getElementById("connectionStatus").innerText = status;
});

Learn more about customizing and handling reconnection

Automatic reconnect has been partially documented already in the preview release. Check out the deeper docs on the topic, with more examples and details on usage, at https://aka.ms/signalr/auto-reconnect.

Managed gRPC Client

In prior previews, we relied on the Grpc.Core library for client support. The addition of HTTP/2 support in HttpClient in this preview has allowed us to introduce a fully managed gRPC client.

To begin using the new client, add a package reference to Grpc.Net.Client and then you can create a new client.

var httpClient = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };
var client = GrpcClient.Create<GreeterClient>(httpClient);

gRPC Client Factory

Building on the opinionated pattern we introduced in HttpClientFactory, we’ve added a gRPC client factory for creating gRPC client instances in your project. There are two flavors of the factory that we’ve added: Grpc.Net.ClientFactory and Grpc.AspNetCore.Server.ClientFactory.

The Grpc.Net.ClientFactory is designed for use in non-ASP.NET app models (such as Worker Services) that still use the Microsoft.Extensions.* primitives without a dependency on ASP.NET Core.

In applications that perform service-to-service communication, we often observe that most servers are also clients that consume other services. In these scenarios, we recommend the use of Grpc.AspNetCore.Server.ClientFactory which features automatic propagation of gRPC deadlines and cancellation tokens.

To use the client factory, add the appropriate package reference to your project (Grpc.AspNetCore.Server.Factory or Grpc.Net.ClientFactory) before adding the following code to ConfigureServices().

services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new Uri("https://localhost:5001"); });

gRPC Interceptors

gRPC exposes a mechanism to intercept RPC invocations on both the client and the server. Interceptors can be used in conjunction with existing HTTP middleware. Unlike HTTP middleware, interceptors give you access to actual request/response objects before serialization (on the client) and after deserialization (on the server) and vice versa for the response. All middlewares run before interceptors on the request side and vice versa on the response side.

Client interceptors

When used in conjunction with the client factory, you can add a client interceptor as shown below.

services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new Uri("https://localhost:5001"); }) .AddInterceptor<CallbackInterceptor>();

Server interceptors

Server interceptors can be registered in ConfigureServices() as shown below.

services .AddGrpc(options => { // This registers a global interceptor options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30)); }) .AddServiceOptions<GreeterService>(options => { // This registers an interceptor for the Greeter service options.Interceptors.Add<UnaryCachingInterceptor>(); });

For examples on how to author an interceptors, take a look at these examples in the grpc-dotnet repo.

Give feedback

We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core and Blazor!

Daniel Roth
Daniel Roth

Principal Program Manager, ASP.NET

Follow Daniel   

Posted on Leave a comment

What’s new in Azure SignalR 1.1.0 Preview 1

Avatar

Ken

We just shipped 1.1.0 Preview 1 of Azure SignalR Service SDK to support some new features in ASP.NET Core 3.0, including endpoint routing and server-side Blazor. Let’s take a look how you can use them in your Azure SignalR application.

Here is the list of what’s new in this release:

  • Endpoint routing support for ASP.NET Core 3
  • Use SignalR service in server-side Blazor apps
  • Server stickiness

Endpoint routing support for ASP.NET Core 3

For those who are using Azure SignalR, you should be familiar with AddAzureSignalR() and UseAzureSignalR(). These two methods are required if you want to switch your app server from self-hosted SignalR to use Azure SignalR.

A typical Azure SignalR application usually looks like this in Startup.cs (note where AddAzureSignalR() and UseAzureSignalR() are used):

public void ConfigureServices(IServiceCollection services)
{ ... services.AddSignalR() .AddAzureSignalR(); ...
} public void Configure(IApplicationBuilder app)
{ ... app.UseAzureSignalR(routes => { routes.MapHub<Chat>("/chat"); }); ...
}

ASP.NET Core 3.0 introduced a new endpoint routing support which allows routable things like MVC and SignalR to be mixed together in a unified UseEndpoints() interface.

For example, you can call MapGet() and MapHub() in a single UseEndpoints() call, like this:

app.UseEndpoints(routes =>
{ routes.MapGet("/foo", async context => { await context.Response.WriteAsync("bar"); }); routes.MapHub<Chat>("/chat");
});

This new syntax is also supported in the latest Azure SignalR SDK so you don’t need to use a separate UseAzureSignalR() to map hubs.

Now your Azure SignalR application looks like this:

public void ConfigureServices(IServiceCollection services)
{ ... services.AddSignalR() .AddAzureSignalR(); ...
} public void Configure(IApplicationBuilder app)
{ ... app.UseRouting(); app.UseEndpoints(routes => { routes.MapHub<Chat>("/chat"); }); ...
}

The only change you need to make is to call AddAzureSignalR() after AddSignalR().

This will be very useful in the case that SignalR is deeply integrated in your code base or the library you’re using. For example, when you’re using server-side Blazor.

Use SignalR service in server-side Blazor apps

Server-side Blazor is a new way to build interactive client-side web UI in ASP.NET Core 3. In server-side Blazor, UI updates are rendered at server side, then sent to browser through a SignalR connection. Since it uses SignalR, there is a natural need to use Azure SignalR service to handle the SignalR traffic so your application can easily scale.

blazor

If you look at some server-side Blazor code samples, you’ll see they have a call to MapBlazorHub() to setup the communication channel between client and server.

app.UseEndpoints(endpoints =>
{ ... endpoints.MapBlazorHub(); ...
});

The implementation of this method calls MapHub() to create a SignalR hub at server side. Before this release there is no way to change the implementation of MapBlazorHub() to use SignalR service. Now if you call AddAzureSignalR(), MapBlazorHub() will also use SignalR service to host the hub instead of hosting it on the server.

Please follow these steps to change your server-side Blazor app to use SignalR service:

  1. Open your Startup.cs, add services.AddSignalR().AddAzureSignalR() in ConfigureServices().
  2. Create a new SignalR service instance.
  3. Get connection string and set it to environment variable Azure:SignalR:ConnectionString.

Then run your app you’ll see the WebSocket connection is going through SignalR service.

Check out this repo for a complete code sample.

Server stickiness

The typical connection flow when using SignalR service is that client first negotiates with app server to get the url of SignalR service, then service routes client to app server.

When you have multiple app servers, there is no guarantee that two servers (the one who does negotiation and the one who gets the hub invocation) will be the same one.

We hear a lot of customers asking about whether it’s possible to make the two servers the same one so they can share some states between negotiation and hub invocation. In this release we have added a new “server sticky mode” to support this scenario.

To enable this, you just need to set ServerStickyMode to Required in AddAzureSignalR():

services.AddSignalR().AddAzureSignalR(options => { options.ServerStickyMode = ServerStickyMode.Required;
});

Now for any connection, SignalR service will guarantee negotiation and hub invocation go to the same app server (called “server sticky”).

This feature is very useful when you have client state information maintained locally on the app server. For example, when using server-side Blazor, UI state is maintained at server side so you want all client requests go to the same server including the SignalR connection. So you need to set server sticky mode to Required when using server-side Blazor together with SignalR service.

Please note in this mode, there may be additional cost for the service to route connection to the right app server. So there may be some negative impact in message latency. If you don’t want the performance penalty, there is another Preferred mode you can use. In this mode stickiness is not always guaranteed (only when there is no additional cost to do the routing). But you can still gain some performance benefits as message delivery is more efficient if sender and receiver are on the same app server. Also when sticky mode is enabled, service won’t balance connections between app servers (by default SignalR service balances the traffic by routing to a server with least connections). So we recommend to set sticky mode to Disabled (this is also the default value) and only enable it when there is a need.

You can refer to this doc for more details about server sticky mode.

Avatar
Ken Chen

Principal Software Engineering Manager

Follow Ken