1

I want user's latitude and longitude to be updated on every page visited. In order not to duplicate things, I created a base controller where I also implemented the onActionExecuting which allows the method to run every time on every action from the derived controllers, but I can't figure out how to pass the latitude and longitude parameters since they are only passed from the browser through an Ajax call.

public class BaseController : Controller
{
    protected readonly UserManager<ApplicationUser> _userManager;

    public BaseController(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        base.OnActionExecuting(context);
        UpdateLocation(latitude, longitude); //How do I pass these parameters from Ajax?
    }

    public void UpdateLocation(double latitude, double longitude)
    {
        var user = _userManager.GetUserAsync(User);
        if (user != null)
        {
            user.Location = new Point(latitude, longitude) { SRID = 4326 };
            await _userManager.UpdateAsync(user);
        }
    }
}

I placed this script in the layout. It retrieves the location data and sends it to the action method.

<script>
$(document).ready(function () {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(showPosition);
    }
    else {
        console.log($`Geolocation + {} not supported by browser.`);
    }
});

function showPosition(position) {
    var postData = { 'latitude': position.coords.latitude, 'longitude': position.coords.longitude };
    $.ajax({
        url: 'Base/UpdateLocation',
        contentType: 'application/json; charset=utf-8',
        type: 'GET',
        data: postData,
        dataType: 'json',
        success: function (data) {
            console.log(data);
        },
        error: function (request, data) {
            console.log(data);
        }
    });
};
</script>
Qudus
  • 1,440
  • 2
  • 13
  • 22
  • Why do you want to send the coordinates to the onActionExecuting method? According to the documentation the onActionExecuting method is called before the action method is invoked: https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.controller.onactionexecuting?view=aspnet-mvc-5.2 – IceCode Aug 09 '20 at 19:20
  • @Örvar I want the coordinates updated before the action is hit but it doesn't really matter, I can also have it updated after it is hit. Even if I remove the onActionExecuting, and place the method in the constructor, I'll still need to pass the coordinates to the method. So, it's still the same problem. – Qudus Aug 09 '20 at 19:26
  • I would suggest that you saved the coordinates in a database or perhaps in a session on every page load. That way you could get the coordinates from the database every time the onActionExecuting method fires. – IceCode Aug 09 '20 at 19:37
  • @Örvar I'm saving in the database. You can see under the method `user.Location = new Point(latitude, longitude)` but I also have to make it constantly update it since users won't be stationary. – Qudus Aug 09 '20 at 19:53
  • The code seems to be doing what you want it todo. When every page has been loaded it gets the coordinates, sends them to the controller and saves them in the database. What else do you want it todo? – IceCode Aug 09 '20 at 20:08
  • @Örvar It is working now. I assumed the action methods in a base controller wouldn't be called from a derived controller (catching up to oop) and I also didn't know ajax would trigger the action method it's pointing to in the URL automatically. – Qudus Aug 09 '20 at 23:59

2 Answers2

1

I hope I understood your question well. Here it goes:

From my experiences and brief Googling, I am under the impression that you can not retrieve coordinates from a simple HTTP request, which means, you can not receive coordinates with every page request. You could for example search for senders location by his requests IP address but that is certainly not reliable. You have to use JavaScript on the client's side (in his browser). The way you do it is, in my opinion, a good way to do it.

From what you describe, the best approach would be to simply let your code how it is and run the script that sends coordinates on every page. This would result in the behavior that you seek. On every page load, the script runs and the user gets his coordinates updated. There will not be any duplicates if you put the script in your page layout and I think there is no need to pass the coordinates into the OnActionExecuting method.

Note: If you describe your needs further I believe we could come up with more suitable solution

Martin Drozdík
  • 351
  • 3
  • 7
  • It is in the layout but know that even when the script run on every page request. It will only send the parameters to the action method which is never called. It is under this action method I want to update the coordinates in the database. So, the question now is, how to run an action method automatically on all pages. – Qudus Aug 09 '20 at 19:48
  • If your script runs on every page (once), that means it will trigger the UpdateLocation action on every page, will it not? Could you please rephrase your comment and refer to methods by their name? I got quite confused as to what you need to execute and when. Maybe share more of your code? I don't understand the context of what you want to accomplish. – Martin Drozdík Aug 09 '20 at 19:58
  • If the script runs on every page, it won't trigger the UpdateLocation unless I call UpdateLocation itself, the script will only send the longitude and latitude parameters (UpdateLocation won't do anything with it). The context of what I want to achieve is to trigger the UpdateLocation on every page requests – Qudus Aug 09 '20 at 20:03
  • So function UpdateLocation in your questions code is not the method invoked by the Ajax call? – Martin Drozdík Aug 09 '20 at 20:18
  • It is. You were right. The UpdateLocation method is also called by ajax, not only taking parameters. I got it working now. – Qudus Aug 10 '20 at 00:02
  • 1
    Well, I am glad I could help! Any upvotes are highly appreciated ;) – Martin Drozdík Aug 10 '20 at 14:05
0

I didn't need to implement the onActionExecuting method. I changed the UpdateLocation to an action method. Though there is no need to create a base controller, I could simply put the action method in one controller and it will be called from any controller as long as the views of that controller inherit from the layout view where the location script is placed.

public class BaseController : Controller
{
    protected readonly UserManager<ApplicationUser> _userManager;

    public BaseController(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task<JsonResult> UpdateLocation(double latitude, double longitude)
    {
        var user = await _userManager.GetUserAsync(User);
        if (user != null)
        {
            user.Location = new Point(latitude, longitude) { SRID = 4326 };
            await _userManager.UpdateAsync(user);
        }
        return Json($"Latitude: {latitude}, Longitude: {longitude}");
    }
}

Then updated the location script in the layout.

<script>
    $(document).ready(function () {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(showPosition);
        }
        else {
            console.log($`Geolocation + {} not supported by browser.`);
        }
    });

    function showPosition(position) {
        var postData = { 'latitude': position.coords.latitude, 'longitude': position.coords.longitude };
        $.ajax({
            url: "@Url.Action("UpdateLocation", "[DerivedControllerName]")",
            contentType: 'application/json; charset=utf-8',
            type: 'GET',
            data: postData,
            dataType: 'json',
            success: function (data) {
                console.log(data);
            },
            error: function (request, data) {
                console.log(data);
            }
        });
    };
</script>
Qudus
  • 1,440
  • 2
  • 13
  • 22