Archive

Archive for the ‘SPA’ Category

Firefox needs UTF8 for GeoXml3


I’m developing an WebAPI2 solution with Angular that receives, from Android devices, GPS points.
So Latitude , Longitude , Speed etc. are saved in a SQL Server 2014 table; from the web site the headquarter would follow the real time navigation.
The chosen approach was to write an KML file from the db data, and show this KML in a GoogleMaps window.
Having local files (not on another domain) the best solution is to use the GeoXml3 library, which is relatively easy to use (examples here and here).
For the javascript part, no problem: there is an simple setTimeout that calls via jQuery an WebAPI2 method that re-write the kml (the name is the record ID), and then adding an random number to the querystring the boss can follow the persons wandering somewhere, seeing the kml line that slowly progress on the map.
But there was an problem: On IE10 , 11, Chrome, Edge all ok; instead on Firefox (Windows or Ubuntu or Android tablet) no kml shown.
Sometimes the things are discovered in a very casual manner…i tried to save the KML with another name using Notepad++ , and change the code in order to not use the API call and instead try to show immediately this second file where i wanted to test some change, in order to understand what was wrong : bingo, KML immediately visible!
This was a real head scratching…the files (the generated from the system, and the one saved from the previous) in Notepad++ was absolutely the same, no visible difference.
So i tried to compare them with WinMerge and surprise, the generated file was full of strange characters before every normal character, immediately i realized “oops, the encoding?” and only after i noticed in Notepad++ that in the Encoding menu the file saved from Notepad++ itself was UTF8 encoded, and the one produced from the C# code was without encoding.
In the WebAPI2 method effectively i used Unicode: so now, all the browsers shows the KML writing the file as:

private async Task WriteTextAsync(string filePath, string text)
{
    //byte[] encodedText = Encoding.Unicode.GetBytes(text);
    byte[] encodedText = Encoding.UTF8.GetBytes(text);
    //
    using (FileStream sourceStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 1048576, useAsync: true))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    };
}

Angular modal problem after update

I was already prepared to the head banging.
I had a .NET 4.5 project with Angular 1.3.15, reopening for some update i tried an NuGet update and voilà, Angular 1.4.1 available: but no updates for Bootstrap, still 3.3.4 … troubles to the horizon…
The site is essentially an employees management, with add update delete done using modal windows.
After the update apparently all still working until i tried an update (of a record).
The original code was

modalInstance = $modal.open({
    templateUrl: 'app/partials/formemployees.html',
    controller: 'modalController',
    backdrop: "static",
    keyboard: true,
    modalFade: true,
    scope: $scope
})

With this code the modal window was appearing, inside the window no problem for fields editing, but after closing the window by the ESC on the keyboard or the Save button, on the browser canvas a grey background was covering everything: only after an F5 the site was working.
The problem seems related to the BootStrap version not aligned to Angular for the “modal” part; after some struggle i found that the problem is relative to the animation feature.
By adding the clause “animation: false, ” :

modalInstance = $modal.open({
    animation: false,
    templateUrl: 'app/partials/formemployees.html',
    controller: 'modalController',
    backdrop: "static",
    keyboard: true,
    modalFade: true,
    scope: $scope
})

Now the modal is working correctly.

Categories: .NET, Angular, SPA, Vs2013

WebAPI2 errors management in Android

In my last project is used an SQL Server 2014 Phones table, with an Android app that register the device itself in this table via an WebAPI2 call.

The table is simple:


We have an primarykey named “PK_Phones” on PhoneId.

The PhoneID is the IMEI code, if the device has phone capabilities and is installed an working SIM, or the Android ID.

There are other common fields for multiuser management as the timestamp which is very useful with the Entity Framework 6 used in this solution.

The Web counterpart (created with Visual Studio 2013) is a common MVC5 project using WebAPI2 with Breeze, Angular , Bootstrap, Modernizr, Moment, Ninject, Toastr…an huge blob installed from NuGet using the HotTowel package (Ninject , Jasmine for testing and other package needs to be installed by hand).

The first move was to create an ADO.NET Entity Data Model (here i don’t speak in detail of the project organization) and using “Code first from Database” i created the class representing my table:

namespace net.studioalessi.walkad.dl
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public partial class Phones
    {
        [Key]
        [StringLength(20)]
        public string PhoneId { get; set; }

        [StringLength(30)]
        public string Manufacturer { get; set; }

        [StringLength(20)]
        public string Model { get; set; }

        [StringLength(150)]
        public string Notes { get; set; }

        [StringLength(20)]
        public string PhoneNumber { get; set; }

        public bool Banned { get; set; }

        [StringLength(50)]
        public string UtenteModifica { get; set; }

        public DateTime? OraModifica { get; set; }

        [StringLength(50)]
        public string UtenteInserimento { get; set; }

        public DateTime? OraInserimento { get; set; }

        [Column(TypeName = "timestamp")]
        [MaxLength(8)]
        [Timestamp]
        public byte[] SSMATimeStamp { get; set; }
    }
}

Then the necessary Controller (right click on Controllers folder->add new scaffolded item->


Web API 2 Controller with actions, using Entity Framework as in figure.

Using the previous Phones class we generate the Controller and the Context, in our case the context is the class PhonesContext:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace net.studioalessi.walkad.dl
{
    public class PhonesContext : DbContext
    {
        public PhonesContext()
            : base("name=WalkadConn")
        {
        }

        // no virtual ? in the previous EF version was generated as virtual...
        public DbSet<Phones> Phones { get; set; }

        // Also the code below was generated automatically in the previous version.
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteModifica)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteInserimento)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.SSMATimeStamp)
                .IsFixedLength();
        }
    }
}

And the Controller is changed from the generated version to an BreezeController

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Walkad.Models;
using net.studioalessi.walkad.dl;
using Breeze.WebApi2;

namespace Walkad.Controllers
{
    [BreezeController]
    public class PhonesController : ApiController
    {
        private IPhoneRepository _repo;

        public PhonesController(IPhoneRepository repo)
        {
            _repo = repo;
        }

        [HttpGet]
        public string Metadata()
        {
            return _repo.MetaData;
        }

        [HttpPost]
        [AllowAnonymous]
        public async Task<IHttpActionResult> RegisterImei(PhoneModel model)
        {
            PhonesContext objT = new PhonesContext();
            try
            {
                Phones objTrace = new Phones
                {
                    UtenteInserimento = "RemoteUser",
                    OraInserimento = DateTime.Now,
                    Model = model.Model,
                    Manufacturer = model.Manufacturer,
                    PhoneId = model.Imei
                };
                //
                objT.Phones.Add(objTrace);
                await objT.SaveChangesAsync();
                return Ok();
            }
            catch (Exception ex)
            {
                return BadRequest(ex.ToString());
            }
            finally
            {
                //
            }
        }
    }
}

The PhoneModel was created by hand and is the model representing the JSON string sent from the client (Web site or Android app, as in this case)

public class PhoneModel
{
    [Required]
    [StringLength(15)]
    public string Imei { get; set; }

    [StringLength(30)]
    public string Manufacturer { get; set; }

    [StringLength(20)]
    public string Model { get; set; }
}

At this point we can try the service from Fiddler


By clicking “Execute” we can see that our call was successful


And in SQL Management Studio we can see the new record:


Ok , we can create the Android app with Eclipse Luna (with the Android Developer Tools).

I don’t show the details, i created an simple Android app with an menu item that calls the routine named registerThisDevice:

private void registerThisDevice() {
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    String strBasePath = sharedPrefs.getString((String)getText(R.string.pref_httpbase), "http://192.168.0.77/Walkad");
    new WebApiRegisterImei().execute(strBasePath + "/api/Phones/RegisterImei");
}

Note the i’m using the IP , the tablet used for the test is in the same WiFi network but is not Windows, is not able to resolve the NETBIOS name.

Note also that we web address is registered in a Preference that defaults to my WiFi address.

In order to follow the best practices , the routine WebApiRegisterImei is an AsyncTask so is called with .execute() passing the server address as String argument (in every case , if we don’t use an AsyncTask we get an NetworkOnMainThread exception):

private class WebApiRegisterImei extends AsyncTask<String, Void, JSONObject> {
    ProgressDialog myProgressBar;
    public String ExportResult = "";
    public String LastException = "";
    public Exception LastExceptionObject;       

    protected JSONObject doInBackground(String... params) {
        BufferedInputStream  in = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(params[0]);
            urlConnection = (HttpURLConnection) url.openConnection();
            // the Android docs says that HttpURLConnection uses the GET method by default. It will use
            // POST if setDoOutput(true) has been called. Other HTTP methods
            // (OPTIONS, HEAD, PUT, DELETE and TRACE) can be used with
            // setRequestMethod(String). Anyway forcing POST as request method is useful.
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            //
            JSONStringer vm;
            vm = new JSONStringer()
                    .object().key("Imei").value(MainActivity.ImeiId)
                             .key("Manufacturer").value(android.os.Build.MANUFACTURER)
                             .key("Model").value(android.os.Build.MODEL)
                    .endObject();

            urlConnection.setFixedLengthStreamingMode(vm.toString().getBytes().length);
            urlConnection.setRequestProperty("Content-Type", "application/json");
            urlConnection.setRequestProperty("charset", "utf-8");
            DataOutputStream wr = new DataOutputStream (
                    urlConnection.getOutputStream ());
                    wr.writeBytes (vm.toString());
                    wr.flush ();
                    wr.close ();                           
            //
            int statusCode = urlConnection.getResponseCode();
            if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                // handle unauthorized (if service requires user login)
            } else if (statusCode != HttpURLConnection.HTTP_OK) {
                InputStream errorstream = urlConnection.getErrorStream();
                BufferedReader br = null;
                if (errorstream == null){
                    InputStream inputstream = urlConnection.getInputStream();
                    br = new BufferedReader(new InputStreamReader(inputstream));
                }else{
                    br = new BufferedReader(new InputStreamReader(errorstream));
                }
                String strResponse = "";
                String strTemp;
                while ((strTemp = br.readLine()) != null){
                    strResponse += strTemp;
                }   
                strResponse = strResponse.toLowerCase();
                if(strResponse.contains("pk_phones"))
                {
                    ExportResult = getErrorDuplicated();
                    return null;
                }
            }
            //
            ExportResult = getExportCompleted();
        } catch (Exception e) {
            Mint.logException(e);
            LastException = e.toString();
            LastExceptionObject = e;                
        } finally {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    Mint.logException(e);
                }
            }
            if(urlConnection != null){
                urlConnection.disconnect();  
            }
        }
        return null;
    }

    private String getErrorDuplicated() {
        return (String)getText(R.string.imeiduplicatederr);
    }

    private String getExportCompleted(){
        return (String)getText(R.string.registerimeidone);
    }           

    @Override
    protected void onPreExecute() {
        try {
            myProgressBar = new ProgressDialog(MainActivity.thisMainActivity, ProgressDialog.STYLE_SPINNER);    
            myProgressBar.setMessage((String)getText(R.string.registerimeiwait));
            myProgressBar.show();
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }

    @Override
    protected void onPostExecute(JSONObject result) {
        try {
            myProgressBar.dismiss();
            if(LastException.length() > 0){
                Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();   
                Mint.logException(LastExceptionObject);
            }else{
                Toast.makeText(MainActivity.thisMainActivity, ExportResult, Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }
}

Examining the code , we can note that onPreExecute and onPostExecute are the only places where we can interact with the main thread and relative controls (the progress bar) so in the background part we register the exceptions details in one string and one object, examining them on the onPostExecute.

Note the use of the JSONStringer: debugging the code you can see that is created the JSON string as used in Fiddler.

If all is ok, we can see that in SQL Server is written a new row in the Phones table.

The test for statusCode != HttpURLConnection.HTTP_OK was the difficult part to discover how it works, the subject of this post.

If you reissue the command from the Android app on the same device you violate the primary key, so the WebAPI code goes in the Exception and is sent to the client “BadRequest” (HTTP error 400) instead of “Ok”(200), sending as other info ex.ToString().

The management of the returned error is done as you can read in the code: we need to examine the returned stream , by reading this stream we can create a string that contains the original ex.ToString, for example

{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"System.Data.Entity.Infrastructure.DbUpdateException: 
An error occurred while updating the entries. See the inner exception for details. ---> 
System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. 
See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_Phones'. 
Cannot insert duplicate key in object 'dbo.Phones'. The duplicate key value is…..

So in the Android code we search if the string contains the name of the primary key , giving to the user an scoped message, or we give to the user an generic message and the code uses Splunk Mint Express for registering all infos about the unmanaged exception (that could be an network failure, and so on).

The requested resource does not support http method ‘POST’

After a life of web sites developed with Web Forms, now i’m working with MVC.

Not a basic MVC, but SPA apps using Angular, Breeze, Twitter Bootstrap.. an huge jump.

In the Controller folder of my solution i was thinking to create an utilities controller, in this case for the logging of javascript errors, paging utilities, and so on.

So i initially created a class as:

public class ServicesController : ApiController
{
	[HttpPost]
	public void SubmitError(string jsRespoText, string jsstatus, string jssource)
	{
		ExceptionUtility.LogJsExceptionFile(jsRespoText, jsstatus, jssource);
	}

	[HttpGet]
	public JsonGenResponse CreatePagerStdTable(int startRowIndex, int totalCount, int pageSize, string jsLoadDataFunName, string Filter, string QueryType)
	{
		// this will be another post…
	}
}

For the HttpGet , no problem.

When i tried from Fiddler the HttpPost method i got the error written in the subject of this post.

A Controller decorated with [BreezeController] works as POST, and it was tested.. but it uses a JObject parameter, i noticed.

The POST problem pops out when you try to use primitive (string, int..) parameters in a Web API call instead of an object.

A solution could be to use [FromBody], with this attribute WebAPI search the attribute:value in the request body.

But there is a problem : is not possible to specify more than one “[FromBody]” , so is not possible to write:

public void SubmitError([FromBody]string jsRespoText, [FromBody]string jsstatus, [FromBody]string jssource)

The solution is to create a class, in my case

public class JsErrorData
{
    public string jsRespoText { get; set; }
    public string jsstatus { get; set; }
    public string jssource { get; set; }

}

And then write the method as

[HttpPost]
public void SubmitError(JsErrorData jsObjErr)
{
    ExceptionUtility.LogJsExceptionFile(jsObjErr.jsRespoText, jsObjErr.jsstatus, jsObjErr.jssource);
}

The javascript caller:

function logJsError(responseText, status, source, callback) {
    var ajaxImpl = breeze.config.getAdapterInstance("ajax");
    ajaxImpl.ajax({
        type: "POST",
        contentType: "application/json;charset=UTF-8",
        url: strBaseHttp + "api/Services/SubmitError",
        data: "{'jsRespoText': '" + responseText.replace(/'/g, '') + "','jsstatus':'" + status + "','jssource':'" + source + "'}",
        success: function (html) {
            callback(html);
        },
        async: true
    });
}

Now HttpPost is working.

Categories: .NET, JQuery, MVC, SPA

Upgrade of Durandal projects

I’m working with Durandal , and i need to upgrade some old (old..2013…) code.
But this code was written when there was Durandal 1.x , now with NuGet in a Visual Studio 2013 project the version is 2.01 , and are changed a lot of things.

Fortunately in this Durandal site page and in this Papa’s page there are useful guidelines.

Categories: .NET, Durandal, JQuery, MVC, SPA

RequireJS with Visual Studio 2013 & WebAPI

Now that the SPA is the mainstream technology of the moment, i was wondering how really are useful Knockout, Durandal, RequireJS and so on.

These new buzzwords are javascript frameworks, founded (RequireIS no) on the well knowed jQuery.

I have done many web applications with jQuery and SPA concepts , using web forms (.aspx) as html/json generators and then decoding/evaluating the resulted html or JSON by hand.

So i started a lot of tests, trying to convert some old web applications in “à la page” SPA apps.

This is not a simple task , because the technologies are rapidly changing and evolving (as the jQuery versions): so the sample probably working at the time of the publications three months after is not working because jQuery changes, new library versions and so on.

I tried to evaluate RequireJs, the first step was to create with Visual Studio 2013 a new ASP.NET Web Application, using the WebAPI template:


Note that i explicity chosen Web API.

Then the first thing that seems obvious is to add a WebAPI controller:


naming it “TestController”

The generated TestController.cs contains

namespace RequireJsTest.Controllers
{
    public class TestController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<controller>/5
        public string Get(int id)
        {
            return "hi this is the test " + id.ToString();
        }
........


In this case i changed the default “value” string with “hi this is the test ” + id.ToString().

By adding the WebAPI there is a new class WebApiConfig in App_Start, called in the global.asax.

Note the comment above // GET api/<controller>/5 : this will be be the path for the call, with the template defined in the WebApiConfig class.

If you try http://localhost:<yourport>/api/test/5 you get a file named 5.json , that contains “hi this is the test 5” (using the default Visual Studio web server).

Now, in the past in a complex web application with many .js sources it was requested a manual and complex management in order to provide the requested js modules in a certain situation, for example we are deleting a db record and it is necessary to provide the infrastructure for the ajax call vs the db and the messaging framework to the user.

With RequireJS the dependencies management in javascript is simplified, the pages http://requirejs.org/docs/start.html and http://requirejs.org/docs/api.html#data-main explains very well the concepts.

So we can install RequireJS from TOOLS->Library Package Manager->Manage NuGet Packages for Solution…

and search online:


Done this , to the HomeController.cs we can add a new method

public ActionResult Test()
{
    ViewBag.Title = "Test Page";
    return View();
}

Right clicking this method we can Add View for this Controller method,


That creates an Test.cshtml file under Views/Home :


By default there is only an <h2> , we change to

@{
    ViewBag.Title = "Test";
}

<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<div id="msgplaceholder"></div>
<script data-main="/app/main" src="@Url.Content("~/Scripts/require.js")"></script>

Require.js is the script added from RequireJS, and now we see what is the “data-main” attribute.

A SPA app typically uses a folder named “app” , immediately under the root, as the main placeholder of the SPA structure.

And another commonly used standard is to use a main.js file as the SPA core.

As the requireJS site write, “The data-main attribute is a special attribute that require.js will check to start script loading”.

So the last <script> loads a file named main.js using the require.js library.

At this point we need to create the main.js , and a structure for the separation of the concepts.

In this case:


In main.js we define some aliases for other js modules, specifying the path:

(function () {
    requirejs.config(
        {
            paths: {
                'testview': '/app/viewmodels/test',
                'datasvc': '/app/services/testsvc',
                'config': '/app/configs/config'
            }
        }
    );

    require(['testview'],
        function (testvw) {
            //debugger;
            var id = 10;
            testvw.showMessage(id);
        }
    );
})();

Here the function decorated with require([‘testview’] is automatically executed, if you place another function for example

require(
    ['testview'],
    function (testvw) {
        //debugger;
        var id = 10;
        testvw.showMessage(id);
    }
);

require(
    ['testview'],
    function (testvw) {
        var id = 20;
        testvw.showMessage(id);
    }
);

is automatically executed after the first has finished (the result depend from the callbacks timings).

Note that the function define as required the module that we aliased with ‘testview’, that points to /app/viewmodels/test.js

Here in test.is we can write

define('testview',
    ['jquery', 'datasvc'],
    function ($, datasvc) {
        var showMessage = function (id) {
            datasvc.getTest(id, function (message) {
                $("#msgplaceholder").html(message);
            });
        };

        return {
            showMessage: showMessage
        };
    }
);

That is we define that the current module will be publicy knowed as “testview” with “define”, and so in main.js we can write “require([testview]”

In test.js we note that this module is defined with the public name “testview”, and internally requires jQuery , which is already publicy knowed with the $ symbol, and our other module “datasvc”,

Here we can define some functions, the ones that we need public are defined public with the “return” keyword (a sample of Module Pattern).

We know now that datasvc is the alias defined in /app/services/testsvc.js where we can write something as

define('datasvc',
    ['jquery', 'config'],
    function ($, config) {
           var callWebApi = function (url, type, callbackfunc) {
                //debugger;
                $.ajax({
                    url: url,
                    type: type,
                    dataType: 'json',
                    success: function (data) {
                        callbackfunc(data);
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        callbackfunc(jqXHR.responseText);
                    }
                });
            },

            getTest = function (id, callback) {
                url = config.baseUrl + id;
                callWebApi(url, 'GET', callback);
            };

        return {
            getTest: getTest
        };
    }
);

That requires “config” defined in /app/configs/config.js

define('config',
    [],
    function () {
        var baseUrl = '/api/test/';  //important ! Verify that the name must be the same of the controller

        return {
            baseUrl: baseUrl
        };
    }
);

Note that config.js is not requiring other libraries, so we can write define(‘config’, []

That is nothing in the square brackets.

if we try to run our app and point the browser to /home/test ( for example http://localhost:29404/home/test)


Conclusion: effectively some of these new frameworks are really useful and RequireIS leads to a less “spaghetti” javascript code.

Categories: .NET, JQuery, MVC, SPA, Vs2013