2

I'm having a problem with resolving the root of a URL between my local machine and after deploying an application on IIS to our dev server.

The url when running locally is something like this. http://localhost:57173/

the url when running on the dev server is something like this. http://ServerName/AppName

I have JQuery Ajax calls to a web api and it won't find the location on the dev server if the trailing slash is left off.

Here's the call

$.ajax({
    type: "GET",
        url: "api/MyApi/Get",
        data: { period: selectedPeriod },
        cache: false,
        success: function (data) {

        }
    });

If I look in FireBug at the api call on the dev server, with the trailing slash left off, it will show

http://ServerName/api/MyApi/Get

If I do have the trailing slash, it will resolve correctly to this. (Notice the AppName now included.)

http://ServerName/AppName/api/MyApi/Get

What am I missing here? It looks like it's not finding the proper root of the application when I move it to this server. The trailing slash makes no difference locally. It will find the api either way. Any ideas how to resolve this?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
madvora
  • 1,717
  • 7
  • 34
  • 49

3 Answers3

2

A url without a leading slash is considered a relative url. Your url "api/MyApi/Get" will be resolved by the browser relative to the url of the page containing the script.

http://ServerName/AppName appears to the browser as if it is a page request, with AppName being the name of the page and the AppName is not considered when resolving the url.

http://ServerName/AppName/ (trailing slash) appears to the browser as a directory request and becomes the base of your relative url.

The best fix for this is probably to redirect requests for http://ServerName/AppName to http://ServerName/AppName/

To do the redirect from within your application, insert the following into your /Home/Index controller action (before any other code)

if (!Request.Url.AbsolutePath.EndsWith("/"))
{
    return Redirect("~/");
}

This will redirect any request for http://ServerName/AppName to http://ServerName/AppName/ As a side effect, it would also redirect any requests for http://ServerName/AppName/Index to http://ServerName/AppName/ but I don't think that would cause you any problems.

Grax32
  • 3,986
  • 1
  • 17
  • 32
  • Great explanation. Can you elaborate on how to do that redirect? I'm still new to this and I've been looking at how to add that trailing slash if it's missing. The default routing is set up to display home/index, so it will still go to that page either way. – madvora Nov 18 '15 at 23:18
2

I also had this issue, the way to solved it was declare a javascript variable that contains the part of the url that differs in both environments (dev and local) and prepend that to every ajax request:

I put that variable in the layout file (Views/Shared/_Layout.cshtml), so that it could be automatically filled by ASP.NET and then used globally (make sure to create the script before the @RenderSection call):

  ...
  <script>
    var baseUrl = @Html.Raw(Json.Encode(Url.Content("~")));
  </sctipt>
  @RenderSection("scripts", required: false) 
</body>
...

Then prepend that variable in the url of the ajax call (for example View/Home/Index.cshtml):

    @section scripts {
      $.ajax({
        type: "GET",
        url: baseUrl + "api/MyApi/Get",
        data: { period: selectedPeriod },
        cache: false,
        success: function (data) {} 
     });
   }
Javier Gonzalez
  • 915
  • 6
  • 14
  • I like this. This is kind of what I was thinking of doing, but couldn't figure out how. I fixed another problem with the href in an anchor tag on the same environment with the Url.Action method. I'll try to carry this over to JavaScript. Thanks – madvora Nov 19 '15 at 00:22
  • I'm not sure what's not working with this. If I go to `http://ServerName/AppName` or `http://ServerName/AppName/` or `http://ServerName/AppName/Home` the baseUrl variable will be `/AppName/` and everything works, but if I try another page like `http://ServerName/AppName/Other`, the baseUrl is showing as `/AppName/Other`. Why isn't this getting the same base URL? – madvora Nov 19 '15 at 14:33
  • I figured out the answer, I changed your script in the _Layout to this `var baseUrl = @Html.Raw(Json.Encode(@Url.Content("~/")));` which now returns the correct base URL. Can you explain the difference here? If this looks right to you, please update your response and I'll mark this as the answer. – madvora Nov 19 '15 at 14:41
  • Let me make some tests, and I'll adjust the answer – Javier Gonzalez Nov 19 '15 at 14:43
  • As @richard [http://stackoverflow.com/a/8186202/1416077](comments) , the `Url.Action` method is intented to resolve an action-controller url, when we say `Url.Action("")` we are resolving to the index action in the current controller: HomeController => `/AppName/`, OtherController => `/AppName/Other` in the case of HomeController we only get `/AppName/` beacuse is the default controller (configured in /App_Start/RouteConfig.cs). `Url.Content("~")` is more suitable for resolve any url based in the root web directory which is obtained with the "~" wildcard. – Javier Gonzalez Nov 19 '15 at 15:11
  • I have my code as "~/" not "~". Maybe those work the same, I just want to make sure the answer is showing correct for someone else reading this. Thanks. – madvora Nov 19 '15 at 21:51
1

Javascript typically doesn't run unless the user causes an event to occur. I prefer to use the following because it allows MVC to get the exact route properly everytime no matter where you deploy.

Html

<button data-url="@Url.Action("MyApi", "Get")"></button>

Javascript

$(document).ready(function()
{
  $('button').on('click', function()
  {
    var url = $(this).data('url');
    $.ajax({
      type: "GET",
      url: url,
      data: { period: selectedPeriod },
      cache: false,
      success: function (data) {
    });
  });
});

Hard coding a url in javascript I consider bad-practice.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • Well in my case, I'm running three Web api calls on page load to populate some tables. Also running those again on a change event from a select, but I don't see how to pass the url info to be ready on page load with no event kicking off the Javascript. – madvora Nov 19 '15 at 00:10
  • So, why don't the tables have the url as data attribute? Basically makes your code reusable, because it can interrogate the table and get the url that has the data that table needs. – Erik Philips Nov 19 '15 at 00:11