Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accepting a byte array parameter in MVC API as Base64

I am currently building an ASP.NET MVC WebAPI service for which I'd like a small piece of binary data to be sent in the querystring. The method in this application is invoked through POST and the body contains the actual data. The querystring is used to describe some properties of the data in the body (e.g. how it is encoded).

I wonder if it is possible to accept a byte array parameter as base64 encoded string in the query string.

As an example I have this code:

using System.Text;
using System.Web.Http;

namespace WebApplication2.Controllers
{
    public class ByteArrayController : ApiController
    {
        public string Get([FromUri] byte[] myarray)
        {
            var strbResult = new StringBuilder();
            foreach (byte byteValue in myarray)
            {
                strbResult.AppendLine(byteValue.ToString() + " ");
            }
            return strbResult.ToString();
        }
    }
}

I then want to be able to send the parameter myarray in the following way:

http://.../api/ByteArray/Get?myarray=MTIzNA%3D%3D

Please don't mind the naming, it's just a simple example.

Now I know that arrays can be sent by using the same parameter multiple times in the querystring (e.g. myarray=1&myarray=2&myarray=3) but I'd like to accept it as a base64 encoded string.

I have been looking for an attribute to specify how to decode the array but wasn't able to find such attribute.

Of course I can modify the code to accept a string and then convert it to a byte array but my preference is to do this transparantly, if possible.

like image 902
Ron Deijkers Avatar asked Mar 16 '23 23:03

Ron Deijkers


1 Answers

Eventually I found an interesting article on model binders that enabled me to implement the above:

http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

For my solution I have created a model binder for the 'base 64 byte arrays':

using System;
using System.Web.Http.ModelBinding;
using System.Web.Http.ValueProviders;

namespace WebApplication2.Models
{
    /// <summary>
    /// Model binder for byte-arrays that are sent as base 64 strings. 
    /// </summary>
    public class ModelBinderWithBase64Arrays : IModelBinder
    {
        public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType == typeof(byte[]))
            {
                ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                string valueAsString = val.RawValue as string;
                try
                {
                    bindingContext.Model = Convert.FromBase64String(valueAsString);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
    }
}

I then registered it as the default model binder in WebApiConfig so that I don't have to explicitly state its type on the parameter in the controller. I added the following lines of code at the beginning of the Register method in WebApiConfig:

// Register model binder for base 64 encoded byte arrays
var provider = new SimpleModelBinderProvider(typeof(byte[]), new ModelBinderWithBase64Arrays());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);

Finally I modify the action method to make it use this default model binder for the byte[] parameter by adding the [ModelBinder] attribute (with which you can also set a specific model binder):

public string Get([ModelBinder] byte[] myarray)

Now I can just use a Base64 encoded value and it is received as a byte array.

Although this solution does require the [ModelBinder] attribute to be added, it does allow freedom of where the model binder is used and still provide as much transparency as possible.

This same solution could be used for other data as well of course, like custom date values or so.

like image 192
Ron Deijkers Avatar answered Mar 20 '23 13:03

Ron Deijkers