My Blog

Not a fan of writing but why not give it a try

Sending Files From One Asp Application to Another

In this post I’ll explain how you can send files from one ASP application to another. I’ll be using HttpClient class to make it simpler to send requests. This post is split into two sections of:

  • Sending a request with File
  • Receiving a request with File

This is for API calls so I won’t be using HttpPostedFileBase.

Send a POST request with file content


So first thing we need to do is read a file:

1
2
var filename = "file_name.jpg";
var bytes = File.ReadAllBytes("path\to\file\" + filename);

Now we’ll write the file bytes to a stream:

1
2
var stream = new MemoryStream();
stream.Write(bytes, 0, bytes.Length);

At this point we’re ready to create our request and send it. I’ll add the filename to the request headers. You can even add it to the url as a query string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// VERY IMPORTANT to rewind the stream to the beginning
stream.Seek(0, SeekOrigin.Begin)

// Create Stream objects
var client = new HttpClient();
var content = new StreamContent(stream);

// Add headers
content.Headers.Add("Content-Type", "image/jpeg");
content.Headers.Add("File-Name", filename; // Add filename to headers

// Post the request
// _request_uri should be the path your endpoint
var result = client.PostAsync(_requestUri, content).Result;

Receive the POST request with file content


Receiving the request is much easier. All we have to do is read the content of the POST request:

1
2
3
4
5
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
    var data = await Request.Content.ReadAsStreamAsync();
}

Now you can do anything you want with the file stream stored in data. You can get the File-Name as follows:

1
2
3
4
5
6
7
IEnumerable<string> values;
if (!Request.Headers.TryGetValues("File-Name", out values))
{
    // If filename does not exist return error message
    return new HttpResponseMessage(HttpStatusCode.ExpectationFailed);
}
var filename = values.First<string>();

Data cleanup


Remember to cleanup your disposable objects or nest them in using statements:

1
2
3
4
5
6
7
8
using(var stream = new MemoryStream()) {
    // operations
    using(var client = HttpClient()) {
        using(var content = new StreamContent(stream)) {
            // content operations and sending the request
        }
    }
}

Or explicitly dispose them:

1
2
3
client.Dispose();
content.Dispose();
stream.Dispose();

Orchard View Counter for a Content Item

In this post I’ll create a very simple view counter which can be attached to any content item and it’ll increment everytime someone visits the content item’s page. Here is what we need

  • ContentPart: a content part which is the model
  • ContentPartRecord: since we are saving data to the database
  • Handler: to save the count to the database
  • Driver: this is where the counter will be incremented
  • Migration: to create a database table
  • Shape: this is optional. You don’t have to view the count this way but you can

ContentPart and ContentPartRecord


Let’s dive into the code and add a file ViewCounterPart.cs to Models directory with following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ViewCounterPart : ContentPart<ViewCounterPartRecord>
{
    [Required]
    public int Views
    {
        get { return Record.Views; }
        set { Record.Views = value; }
    }
}

public class ViewCounterPartRecord : ContentPartRecord
{
    public virtual int Views { get; set; }
}

Handler


This part is saving data to the database so we must create a handler for it. Create a file named ViewCounterPartHandler.cs to Handlers directory with following content:

1
2
3
4
5
6
7
public class ViewCounterPartHandler : ContentHandler
{
    public ViewCounterPartHandler(IRepository<ViewCounterPartRecord> repository)
    {
        Filters.Add(StorageFilter.For(repository));
    }
}

Driver


This class is used everytime Orchard tries to render a content item with ViewCounterPart attached. Therefore I’ll place the incrementation logic here. NOTE there is no ip filtering to make sure views are added for unique users only. Every page refresh will increment the view by 1 (unless page is cached). You can add ip filtering yourself if you wish

Add the following content to ViewCounterPartDriver.cs in Drivers directory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ViewCounterPartDriver : ContentPartDriver<ViewCounterPart>
{
    protected override string Prefix
    {
        get { return "ViewCounter"; }
    }

    private readonly IWorkContextAccessor _workContext;
    public ViewCounterPartDriver(IWorkContextAccessor workContext)
    {
        _workContext = workContext;
    }

    protected override DriverResult Display(ViewCounterPart part, string displayType, dynamic shapeHelper)
    {
        // Do not increment if in admin menu
        if (AdminFilter.IsApplied(_workContext.GetContext().HttpContext.Request.RequestContext)) {
            return null;
        }

        // Increment the part.Views, it'll be saved to the database automatically
        part.Views++;

        return null; // If you wish to display a shape you must replace this line
    }
}

if you decided to add a shape replace the last return null with the following

1
2
3
return ContentShape("Parts_ViewCounter", () => shapeHelper.Parts_ViewCounter(
    Views: part.Views
));

Migration


We’ll need to create a database table and a content part (you can also attack it to a content item in the migraion). Add following to Migrations.cs

1
2
3
4
5
6
7
8
9
10
SchemaBuilder.CreateTable(typeof(ViewCounterPartRecord).Name,
    table => table
    .ContentPartRecord()
    .Column<int>("Views", column => column.NotNull())
);

ContentDefinitionManager.AlterPartDefinition(typeof(ViewCounterPart).Name,
    part => part
        .Attachable()
);

Shape (optional)


If you decided to render a shape you can add the file Views/parts/ViewCounter.cshtml with following content:

1
@(Model.Views ? Model.Views : 0)

That snippet will only render a number (if exists). Feel free to add better markup, container and styles to it.

Generalise reCaptcha Addition to Forms by Just Adding a Class

I this post we’ll add reCaptcha filed to each form by just adding a class. In addition in my example I wanted to retrieve my public key from a server to place in the script. Let’s begin by adding a form markup

1
2
3
4
5
6
7
8
9
<div class="form-group row">
    <div class="col-sm-12">
        <!-- id field does not matter as long as it is unique but assign an id -->
        <div id="g-recaptcha" class="g-recaptcha"></div>
    </div>
    <div class="col-sm-12 text-left">
        <a href="" class="captcha-reset">Reset captcha</a>
    </div>
</div>

Here is the precidure to add reCaptcha to all .g-recaptcha div tags

  • Add an onLoad function to Google’s reCaptcha api
  • Render a reCaptcha for every .g-recaptcha tag
  • Attach a listener to .captcha-reset to reset the captcha

Add onLoad function to Google’s reCaptcha api

First thing is to load the recaptcha api library from google with keywords async defer

1
<script src="https://www.google.com/recaptcha/api.js?onload=onCaptchaLoad&render=explicit" async defer></script>

Notice onload=onCaptchaLoad and render=explicit. These attributes pretty much stop recaptcha from rendering automatically. You can indeed render them with just html but in my case I would like to retrieve my public key from elsewhere after the page has loaded. So let’s implement the onload function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 /*******************************************************************************
 * Captcha rendered javascript, it will render a captcha for every
 * div with class name g-recaptch
 * Dependencies: jQuery, googleCaptchaApi
 *******************************************************************************/

 // Global variables
 var onCaptchaLoad;
 var resetCaptcha;

(function renderCaptchas(onCaptchaLoad, resetCaptcha, document) {
    onCaptchaLoad = function () {
        $.get('url/to/your/recaptcha/public/key/', function success(data) {
            $('.g-recaptcha').each(function addCaptcha() {
                var captcha = this;
                var id = grecaptcha.render($(captcha).attr('id'), {
                    'sitekey': data,
                });

                // Set the grecaptcha widget id in the form
                $(this).parents('form').data('grecaptcha', id);
            });
        });
    };

    resetCaptchaButton = function () {
        // Must be called with an item inside the form as context
        var form = $(this).parents('form');
        grecaptcha.reset(form.data('grecaptcha'));
    };

    $(document).on('ready', function onReady() {
        $('.captcha-reset').on('click', function resetCaptcha(e) {
            resetCaptchaButton.call(this);
            e.preventDefault();
        });
    });
})(window, document);

That single snippet pretty much achieves all three points required. Only note that the above script should be included before google’s script.