Tahir Hassan's Blog

My Technical Notes

Thursday, 7 December 2017

Web API/Swagger File Upload

This article explains how to implement an action method which accepts a file upload, when using Web API and Swagger (using the normal .NET framework). Perhaps not all code of this code is necessary, or there might be simpler ways to do this when using Web API when in .NET Core.

First add an attribute which marks the method as accepting a `file` parameter:


[AttributeUsage(AttributeTargets.Method)]
public sealed class SwaggerParameterAttribute : Attribute
{
    public SwaggerParameterAttribute(string name, string description)
    {
        Name = name;
        Description = description;
    }

    public string Name { get; private set; }

    public string Description { get; private set; }

    public string Type { get; set; } = "text";

    public bool Required { get; set; } = false;
}

Thereafter, add a class which processes this attribute, which implements the `Swashbuckle.Swagger.IOperationFilter` attribute. If this attributes exists, then we add a parameter object to some `parameters` collection:


public class SwaggerParameterOperationFilter : Swashbuckle.Swagger.IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var requestAttributes = apiDescription.GetControllerAndActionAttributes<SwaggerParameterAttribute>();
        if (requestAttributes.Any())
        {
            operation.parameters = operation.parameters ?? new List<Parameter>();

            foreach (var attr in requestAttributes)
            {
                operation.parameters.Add(new Parameter
                {
                    name = attr.Name,
                    description = attr.Description,
                    @in = attr.Type == "file" ? "formData" : "body",
                    required = attr.Required,
                    type = attr.Type
                });
            }

            if (requestAttributes.Any(x => x.Type == "file"))
            {
                operation.consumes.Add("multipart/form-data");
            }
        }
    }
}

We have to ensure that the `IOperationFilter` is processed, by making Swagger aware of it. In your `Startup` class, you need to call `OperationFilter` method:


// config is a HttpConfiguration object
config.EnableSwagger(c =>
{
    c.OperationFilter<SwaggerParameterOperationFilter>();
    // ....
}

Finally, we annotate our action method with a `SwaggerParameterAttribute` making sure that the `Type` is `file`:


[HttpPost]
[ResponseType(typeof(string))]
[SwaggerParameter("myFile", "A file", Required = true, Type = "file")]
public async Task<IHttpActionResult> Upload()
{
    if (!Request.Content.IsMimeMultipartContent())
    {
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
    }

    var provider = await Request.Content.ReadAsMultipartAsync();
    var bytes = await provider.Contents.First().ReadAsByteArrayAsync();

    return Ok();
}

Tuesday, 5 December 2017

Emacs: Finding Function that is bound to key sequence

To get the function behind a keybinding, do:


C-h k key-sequence

Wednesday, 8 November 2017

Using SQL Merge Statement (Example)


IF OBJECT_ID('dbo.SystemSetting', 'U') IS NOT NULL 
  DROP TABLE dbo.SystemSetting; 

IF OBJECT_ID('dbo.WebConfig', 'U') IS NOT NULL 
  DROP TABLE dbo.WebConfig; 

CREATE TABLE dbo.SystemSetting([Name] varchar(50), [Value] varchar(250), 
     CONSTRAINT SystemSetting_PK PRIMARY KEY([Name]));

CREATE TABLE dbo.WebConfig([Name] varchar(50), [Value] varchar(250), 
     CONSTRAINT WebConfig_PK PRIMARY KEY([Name]));

GO

INSERT dbo.SystemSetting([Name], [Value]) VALUES('SystemType', 'Live');
INSERT dbo.SystemSetting([Name], [Value]) VALUES('RootUrl', 'http://tahirswebsite.com');

GO
INSERT dbo.WebConfig([Name], [Value]) Values('SystemType', 'Test');
INSERT dbo.WebConfig([Name], [Value]) Values('RootUrl', 'https://realwebsite.com');
GO

-- MERGE statement with the join conditions specified correctly.

BEGIN TRAN;
MERGE dbo.SystemSetting AS dbSetting
USING dbo.WebConfig AS webSetting
ON (dbSetting.[Name] = webSetting.[Name]) 
WHEN NOT MATCHED BY TARGET
    THEN INSERT([Name], [Value]) VALUES(webSetting.[Name], webSetting.[Value])
WHEN MATCHED AND dbSetting.[Value] <> webSetting.[Value]
    THEN UPDATE SET dbSetting.[Value] = webSetting.[Value]
OUTPUT $action, Inserted.*, Deleted.*;
COMMIT TRAN;
GO 

The above example plays out the scenario in which the web server wants to push its configuration data, stored in `WebConfig`, into the database's own `SystemSetting` table.

It will:

  • Creates tables `SystemSetting` and `WebConfig` that have the same two `varchar` columns, `Name` and `Value`.
  • It populates both tables with some sample values.
  • It merges `WebConfig` data into `SystemSetting`.

Monday, 30 October 2017

PowerShell: Command Argument Completion

A parameter's argument can be completed by using the `Register-ArgumentCompleter` function. See Completing Parameter Values With Other Parameter Values, which provides a fully working example, for more information.

For PowerShell V5 and later, there is no need to install TabCompletionPlusPlus module.

Vagrant: Forwarded port not exposed on network

In


Control Panel\System and Security\Windows Firewall\Allowed Programs
Allow `VBoxHeadless` to access the `Domain`, `Home/Work (Private)`, and `Public`.

Tuesday, 17 October 2017

PowerShell: Create Email Message with Attachments

A lot of the times, while in PowerShell, you want to email something as an attachment to a colleague without having to open it in Explorer and right-clicking it, and doing `Send to...` → `Mail recipient`. What is needed is a simple function `Email-File` that does this:


Function Email-File([string[]]$Files) {
    $outlook = New-Object -ComObject Outlook.Application;
    $mail = $outlook.CreateItem(0);
    $mail.Subject = 'Email Files';

    $Files | ForEach-Object {
        $mail.Attachments.Add($_);
    }

    $mail.Save();
    $mail.Display($false);
}

Saturday, 7 October 2017

Installing `adb` (Android Debug Bridge)

To install `adb` you must first download the "Android command line tools" from the Android Studio download page. It is a zip file, not a user friendly installer.

Crate a folder in the root of the `C:\` drive called `android-sdk`, and within it, copy the `tools` folder that is in the zip file.

After that, on the command line, `cd` to the `android-sdk\toosl\bin` folder. Invoke the `sdkmanager.bat` to install the so-called `platform-tools` that contains `adb`:


.\sdkmanager.bat "platform-tools"

After this finishes installing you will have a `platform-tools` folder in the `android-sdk` folder, that contains `adb.exe`. You can simply add this containing folder to the `PATH` environment variable if you wish invoke it from anywhere (or create an "Alias" in PowerShell).