Parfois on ne veut pas inclure certaines librairies plutôt lourde pour un besoin très minime. Dans mon cas, c’était une simple liste d’objets avec la possibilité d’en ajouter des occurences côté front-end. Je voulais éviter d’utiliser handlebars ou même pire, un framework complèt pour en venir à mes fins.

var _incrementIndex = function ($element) {
    var attributes = ["id", "name", "data-valmsg-for"];
    $element.find("input, span, select, textarea").each(function (index, elem) {
        
        var $elem = $(elem);

        for (index = 0; index < attributes.length; ++index) {
            var attrValue = attributes[index];
            if ($elem.attr(attrValue)) {
                var id = $elem.attr(attrValue).replace(/[^\d]/g, '');
                var newId = $elem.attr(attrValue).replace(id, parseInt(id, 10) + 1);
                $elem.attr(attrValue, newId);
            }
        }
    });
}

En passant une div à cette fonction, elle s’occupera de mettre à jour l’ensemble des index en gardant la schémantique de ASP .NET.

Trop souvent, une base de donnée contient des données avec des caractères blancs complètement inutiles. Par exemple un nom, au lieu de “Gabriel” on se retrouve avec “Gabriel “ ou “ Gabriel”. C’est très facile pallier à ce problème en effectuant un .Trim() tout bête juste avant la persistance des données. Cependant, ça devient vite facile de l’oublier.

C’est pourquoi un custom model binder peut pallier à cette problématique très rapidemment, et ce pour l’ensemble de l’application.

public class TrimModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType.GetTypeInfo().IsValueType ||
            (context.Metadata.ModelType == typeof(string)))
        {
            return new TrimModelBinder();
        }

        return null;
    }
}
public class TrimModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        //Get the value
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == ValueProviderResult.None)
        {
            // no entry
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

        //Set the value, this has to match the property type.
        var typeConverter = TypeDescriptor.GetConverter(bindingContext.ModelType);
        var propValue = typeConverter.ConvertFromString(valueProviderResult.FirstValue.Trim());
        bindingContext.Result = ModelBindingResult.Success(propValue);
        return Task.CompletedTask;
    }
}

Finalement, il suffit d’aller ajouter le code suivant dans votre Startup.cs pour ajouter votre model binder à la liste des ModelBinderProviders du framework. Je le place à la première position, car je veux qu’il soit éxécuté le plus rapidemment possible.

services.Configure(options =>
{
    options.ModelBinderProviders.Insert(0, new TrimModelBinderProvider());
});

Dans le cadre d’un projet d’amélioration de la sécurité d’une application, j’ai du notamment passer l’ensemble des champs VARCHAR d’une base de donnée vers NVARCHAR. La raison est bien simple: protéger le contenu des injections XSS.

Il faut en outre que:

  • Le caractère  “>” devienne “&gt;”
  • Le caractère “<” devienne “&lt;”
  • Le caractère  ” devienne &quot;

Pour ce faire, il suffit de générer une série d’instructions “ALTER TABLE” à partir de cette requête SQL:

SELECT 'ALTER TABLE ' + isnull(schema_name(syo.id), 'dbo') + '.[' +  syo.name +'] ' 
    + ' ALTER COLUMN [' + syc.name + '] NVARCHAR(' + case syc.length when -1 then 'MAX' 
        ELSE convert(nvarchar(10),syc.length) end + ') '+ 
        case  syc.isnullable when 1 then ' NULL' ELSE ' NOT NULL' END +';' 
   FROM sysobjects syo 
   JOIN syscolumns syc ON 
     syc.id = syo.id 
   JOIN systypes syt ON 
     syt.xtype = syc.xtype 
   WHERE 
     syt.name = 'varchar' 
    and syo.xtype='U'

Finalement, ce n’est qu’une partie des éléments à mettre en place pour sécuriser une application convenablement d’une possible faille XSS.

Les applications .NET Core agissent différemment des applications traditionnelles ASP .NET sur la stack Windows / IIS. En effet, l’application lorsque démarée, procède à des locks sur l’ensemble des fichiers utilisés.

J’entends par là:

  • les .DLL
  • Les .EXE
  • Les fichiers de logs

Cela rend donc impossible le déploiement. Si vous essayez, vous aurez sans doute cette problématique:

Deployment job started
Found 1 deployable artifacts.
Deploying Web application TestProject.Web
Downloading artifact package TestProject.Web.zip (20,523,009 bytes)
Execute before-deploy.ps1 script for TestProject.Web application
All file locks have been released!
Website 'testproject.com' already exists with 'testproject.com' app pool and root directory at 'F:\websites\custom\TestProject'
Updating website bindings to:
 - http *:80:testproject.com
Application path with expanded environment variables: F:\websites\custom\TestProject
Updating web site 'testproject.com' contents from Zip archive C:\Users\appveyor\AppData\Local\Temp\1\2hylwzwk.w4o\TestProject.Web.zip
Info: Updating file (testproject.com\before-deploy.ps1).
Info: Updating file (testproject.com\refs\TestProject.Web.exe).
Info: Updating file (testproject.com\TestProject.Application.dll).
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 1 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 2 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 3 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 4 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 5 of 5.
Web Deploy cannot modify the file

Pour résoudre le problème, il faut créer un fichier “before-deploy.ps1” directement dans le root du projet Web et mettre le code suivant:

Restart-WebAppPool "testproject.com"
Write-Host "All file locks have been released!"

Pas de craintes, Restart-WebAppPool termine les requêtes courantes avant de redémarrer l’application pool.

Finalement, ne surtout pas oublier de l’inclure dans votre project.json:

"publishOptions": {
    "include": [
      "before-deploy.ps1"
    ]
  }