Thursday 26 January 2017

How to create a custom media type formatter for accept type in Web api call

In this post we are going to create a custom media type formatter for accept type in web api call, Normally we are using the application/xml, text/xml format in accept while giving request.

In this sample we are going to take accept type as csv format for response type.

Steps to create a custom media type formatter.
1. Create a class which derived from BufferedMediaTypeformatter.
2. Make a condition for CanWriteType
3. Make a logic to WriteToStream
4. Add a format type in constructor in supportedMediaTypes.


    public class BlogCsvFormatter : BufferedMediaTypeFormatter
    {

        public BlogCsvFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/blog"));
        }

        public override bool CanReadType(Type type)
        {
            return false;
        }

        public override bool CanWriteType(Type type)
        {
            if(type == typeof(BlogModel))
            {
                return true;
            }
            else
            {
                Type t = typeof(IEnumerable<BlogModel>);
                return t.IsAssignableFrom(type);
            }
        }

        public override void WriteToStream(Type type, object value, 
               Stream writeStream, HttpContent content)
        {
            using(StreamWriter writer = new StreamWriter(writeStream))
            {
                var list = value as IEnumerable<BlogModel>;
                if (list != null)
                {
                    foreach (var item in list)
                    {
                        writer.Write(string.Format(
         "{0},\"{1}\",\"{2}\",\"{3}\"\n",item.Id,item.Name,item.NoOfPosts,item.Createdby));
                    }
                }
                else
                {
                    var item = value as BlogModel;
                    if (item == null)
                        throw new InvalidOperationException("Cant able to serialize");

                    writer.Write(string.Format(
        "{0},\"{1}\",\"{2}\",\"{3}\"\n", item.Id, item.Name, item.NoOfPosts, item.Createdby));
                }
            }

        }

    }


Add the custom formatter in web api config


    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.Formatters.Add(new BlogCsvFormatter());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

    }



Create a controller logic with get where it return the all blog ids.

   public class BlogModel
    {
        public int Id { set; get; }

        public string Name { set; get; }

        public string Createdby { set; get; }

        public int NoOfPosts { set; get; }
    }

   public class BlogController : ApiController
    {

        public IEnumerable<BlogModel>  GetBlogs()
        {
            List<BlogModel> blogs = new List<BlogModel>();

            blogs.Add(new BlogModel() { 
                    Id = 1,Name="DotnetVisio",Createdby="Rajesh",NoOfPosts=400 });

            blogs.Add(new BlogModel() { 
                    Id = 2, Name = "Angularjs", Createdby = "Rajesh", NoOfPosts = 50 });

            blogs.Add(new BlogModel() { 
                    Id = 3, Name = "SqlServer", Createdby = "Suresh", NoOfPosts = 250 });

            return blogs;
        }


}


While giving request we have to give accept as following to get the custom format to execute when we give request with accept as application/blog our custom formatter will execute

url : http://localhost:57437/api/blog

send request as
accept:application/xml

output: you may see it is in xml format







send request as
accept:application/blog

output: you may see it is in csv format based on our custom formatter , due to our request accept type output changes, you may see that the same url request return a collection in previous one , but here it is csv format.







From this post you can learn how to create a custom media type formatter for accept type in web api call.

Create a File upload directive with progress bar and abort option using angular js

In this post we are going to see how to create a file upload control with progress bar and abort options using angular js. we are going to use the existing module angular-file-upload.js , angular-file-upload-shim.js create by daniel , This needs to be add as reference to our project. Where angularFileUpload module is added as dependency module in root module.



    <script src="~/Scripts/angular.js" type="text/javascript"></script>
    <script src="~/Scripts/angular-route.js" type="text/javascript"></script>
    <script src="~/Scripts/angular-file-upload.js" type="text/javascript"></script>
    <script src="~/Scripts/angular-file-upload-shim.js"  type="text/javascript"></script>


First we have to create a Directive which will acts as Fileupload control. where the directive have options like showing progress bar and abort options.

progressWidth directive change the progress bar width while uploading

var rgapp = angular.module('rgapp', ['angularFileUpload']);

rgapp.directive('progressWidth', [function () {
    return {
        restrict:'A',
        scope: {
            progressWidth:'='
        },
        link: function (scope, elem, attr, cntrl) {           

            var width = (elem.prop('offsetWidth') * scope.progressWidth) / 100
            angular.element(elem.find('div')).css('width', width + 'px');

            scope.$watch(function () { return scope.progressWidth; }, 
             function (newval, oldval) {
                if (newval != oldval) {
                    var width = (elem.prop('offsetWidth') * scope.progressWidth) / 100;
                    angular.element(elem.find('div')).css('width', width + 'px');
                }
            });
        }
    }
}]);



rgapp.directive('fileuploadForm', ['$upload'function ($upload) {
    return {
        scope: {
            asyncurl: '@',          
            successCallback: '&',
            fileselectCallback:'&',
            errorCallback: '&',
            inputData:'='
        },
        restrict: 'E',
        templateUrl:'../App/templates/upload.html',
        link: function (scope, element, attr, cntrl) {

            scope.progressFiles = [];
            var uploadingFiles = [];
            var uploadFileHandlers = [];

            scope.uploadedFilesString = '';

            scope.uploading = function(){
                for (var i = 0; i < uploadingFiles.length; i++) {
                    var _file = uploadingFiles[i];
                    scope.progressFiles[i].isabort = false;
                    scope.progressFiles[i].isfinished = false;
                    (function (indx) {

                        scope.progressFiles[i].isprogress = true;

                        uploadFileHandlers[indx] = $upload.upload({
                            url: attr.asyncurl,
                            method: 'POST',
                            file: _file,
                            data: scope.inputData
                        })
                            .progress(function (prg) {
                                console.log("index " + indx);
                                scope.progressFiles[indx].progress = 
                                              Math.round((prg.loaded * 100) / prg.total);

                                if (scope.progressFiles[indx].progress==100)
                                    scope.progressFiles[indx].isfinished = true;
                                })

                            .success(function (data, status, headers, config) {

                                scope.progressFiles[indx].isprogress = false;
                                scope.progressFiles[indx].isfinished = true;

                                if (scope.successCallback)
                                    scope.successCallback({ data: data, status: status, 
                                        headers: headers, config: config });

                            })
                            .error(function (data, status, headers, config) {

                                scope.progressFiles[i].isabort = true;

                                if (scope.errorCallback)
                                scope.errorCallback({ data: data, status: status, headers: 
                                       headers, config: config });
                            });

                   })(i);

                }
                uploadingFiles = [];
                scope.uploadedFilesString = '';
            }

            scope.abort = function (indx) {
                if (uploadFileHandlers.length > indx) {
                    uploadFileHandlers[indx].abort();
                    scope.progressFiles[indx].isabort = true;  
                    scope.progressFiles[indx].isprogress = false;                
                }
            }

            scope.close = function (indx) {                                 
                scope.progressFiles[indx].isshow = false;               
            }

            scope.uploadedFiles = function (files) {

                uploadingFiles = files;
                scope.progressFiles = [];
                scope.uploadedFilesString = '';
                for (var i = 0; i < uploadingFiles.length; i++) {

                    scope.uploadedFilesString += uploadingFiles[i].name + ";";
                    scope.progressFiles[i] = {  
                           isshow:true,isprogress:false, index: i, isabort: false,
                           isfinished: false, extn: '',filecolor:''
                           filename: uploadingFiles[i].name, size: uploadingFiles[i].size, 
                           progress: 0, type: uploadingFiles[i].type };

                    scope.progressFiles[i].extn = scope.progressFiles[i].filename.substr(
                           scope.progressFiles[i].filename.lastIndexOf('.') + 1);
                    
     scope.progressFiles[i].filecolor = scope.getExtensionFile(scope.progressFiles[i].extn);

                }

                if (scope.fileselectCallback)
                    scope.fileselectCallback({ files: files });
            }
           

            scope.browseFiles = function () {
                document.getElementById('rgfile').click();               
            }
           

            scope.getExtensionFile = function (extn) {
                switch (extn) {
                    case "jpg":
                    case "png":
                    case "bmp":
                        return "#7b8c25";
                        break;
                    case "xls":
                    case "xlsx":
                    case "csv":
                        return "#468847";
                        break;
                    case "doc":
                    case "docx":
                        return "#428bca";
                        break;
                    case "ini":
                    case "jar":
                    case "zip":
                        return "#773472";
                        break;
                    case "exe":
                        return "#bce8f1";
                        break;
                    default:
                        return "red";
                }
            }
           
        }
    }
}]);




Template:
**************************
<div>
    <div class="" style="width:500px">
        <div class="form-inline" style="padding:20px">
            <label>Files :</label>
            <input type="text" ng-model="uploadedFilesString" class="form-control" />
            <button ng-click="browseFiles()" class="btn btn-sm">Browse</button>
            <button ng-click="uploading()" class="btn btn-sm btn-success">Upload</button>
        </div>
        <div class="form-group">
            <input  type="file" style="display:none" ng-file-select="uploadedFiles($files)" 
                    class="form-control" id="rgfile"
                    ng-model="user.file" multiple />
        </div>        
        <div>
            <div class="panel">
                <div ng-show="upfile.isshow" class="row container" style="padding:10px;" 
                     ng-repeat="upfile in progressFiles">                   
                    <div class="col-lg-2 col-md-2 col-sm-2">
                        <span style="color: {{upfile.filecolor}};font-size: 40px;
                           position: absolute;" class="glyphicon glyphicon-file"></span>
                        <span style="font-size12px;font-weight600;colorwhite;
                              positionabsolute;margin-top:25px;
                              border-top-left-radius5px;width35px;
                              text-align:center">{{upfile.extn}}</span>
                    </div>
                    <div class="col-lg-5 col-md-5 col-sm-5">
                        <strong style="padding:5px;">File : {{upfile.filename}}</strong>
                        <div style="padding:5px;">Size : {{upfile.size}}</div>
                    </div>                   
                    <div class="col-lg-5 col-md-5 col-sm-5">
                        <div class="row container">
                            <div class="col-lg-10 col-md-9 col-sm-8">
                                <div style="border:1px solid green;border-radius:5px;
                                   margin-top:10px;background-color:lightgray" 
                                   progress-width="upfile.progress">
                                    <div ng-show="!upfile.isabort" 
                                         style="border:1px solid green;border-radius:5px;
                                         background-color:lawngreen;width:40px;"></div>
                                    <div ng-show="upfile.isabort" 
                                         style="border:1px solid red;border-radius:5px;
                                         background-color:lawngreen;width:40px;"></div>
                                </div> 
                                <div>
                                    <button ng-show="upfile.isprogress" 
                                        style="margin-top:5px;" 
                                        ng-click="abort(upfile.index)" 
                                        class="btn btn-danger btn-xs">Abort</button>
                                    
                                    <button ng-show="upfile.isfinished" 
                                        style="margin-top:5px;" 
                                        ng-click="close(upfile.index)" 
                                        class="btn btn-success btn-xs">Close</button>
                                    
                                   <button ng-show="upfile.isabort" 
                                        style="margin-top:5px;" 
                                        ng-click="close(upfile.index)" 
                                        class="btn btn-warning btn-xs">Close</button>
                                </div>
                                                          
                            </div>                           
                            <div class="col-lg-2 col-md-3 col-sm-4">
                              <span><strong>{{upfile.progress}}%</strong>
                              </span>                              
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>



Controller 

rgapp.controller('blogcontroller', ['$scope','$upload',function ($scope,$upload) {

    $scope.Files = [];
    $scope.FilesData = [];
    $scope.filedata = { trackingid:'', description:''};

    $scope.success = function (data,status,headers,config) {
        console.log('config');
        console.log(config);
        $scope.FilesData.push(data);
    }

    $scope.error = function (data,status,headers,config) {
        console.log('error');
        console.log(config);
    }

    $scope.uploadfiles = function (files) {
        console.log(files);
    }

    $scope.uploadFile = function () {

        for (var i = 0; i < $scope.Files.length; i++) {

            var file = $scope.Files[i];

            $upload.upload({

            }).progress(function (evt) {

                }).success(function (evt) {

                })

        }

    }

    $scope.uploadedFiles = function (files) {
        $scope.Files = files;
    }

}]);



Html:
**************** 
<div ng-controller="blogcontroller">
    <div>
        <div style="margin-left:25px;">
            <fileupload-form asyncurl="~/api/Blog"
                             method="post"
                             input-data="filedata"
                             fileselect-callback="uploadfiles(files)"
                             success-callback="success(data,status,headers,config)"
                             error-callback="error(data,status,headers,config)">
            </fileupload-form>
        </div>
    </div>  
</div>



MVC Controller:
********************

        [CheckMimeMultiPart]
        public void Post()
        {
            try
            {
                var path = HttpContext.Current.Server.MapPath("~/files");
                var sprovide = new MultipartFormStreamProvider(path);
                await Request.Content.ReadAsMultipartAsync(sprovide);             
                 
            }catch(Exception ex)
            {
               
            }

        }


Output:
*********************






Once you click on the upload button, the files starts showing the progress bar with abort options like below, if you click abort then it will change the color of progress bar and shows the close button













From this post you can learn how to create a file upload control with progress bar and abort options using angular js