简体   繁体   中英

URL Routing issue with Javascript (Jquery) call to MVC3 Controller/Action/ID, returning 404, should get JSon

Question: How do I code the URL passed to javascript so that in the built/deployed version the code will go to the controller/action with the id and return the JSon data, like it works in the Visual Studio environment?

Goal: to return dates to the datepicker, because they will be disabled dates. I expect a return of JSon array of strings that are dates that are then fed into the datepicker.

Relevance to others: Returning JSon data from a call to Controller/Action/ID seems like it would be a common thing to do. I see other questions regarding routing issues, as well as JSon, and jQuery seems to be a popular way to enhance web development.

Background: I have a jQuery-UI datepicker that calls a Controller/Action/ID and returns json data to the datepicker. It works perfectly when running within Visual Studio, but fails when built and deployed to the Dev environment. The code passes the URL that is generated and passed to the jQuery is: /Secure/Validation/getDisabledDates/999

We are using Umbraco, with a directory called /Secure within the Umbraco area to hold our MVC code. The URL that the code tries then generates on the Dev environment is: /Secure/Validation/getDisabledDates/999 If I watch the call and the errors in Firebug, I see that an error for the following: POST http://dev.ourcompanyname.com/Secure/Validation/getDisabledDates/999 302 Found (if I investigate further, I find it was really a 404, that got sent to the error page, and that's what was found.)

I'm using @Url.Action in the view to get the correct (hopefully) URL to the action, but when the javascript request is made in the Dev environment, the return is a 404. The code gets into the javascript code and displays the URL in an alert, which looks right to my eyes. The code does not seem to get as far as the MVC controller (I tried putting a messagebox there).

This seems to be because the controller isn't actually a distinct, real page within a directory, because the MVC code is compiled into a DLL. I've heard this and it makes sense, but other people in the world seem to be calling controllers, so I'm not sure what my problem is here.

Originally there was only one set of dates, so some code calls only say GetDates, but then it was broken up by Widget, so calls then reflect ByWidget, so an ID is passed. All of the calls trace correctly, and the code works in Dev.

SetDates that gets called on success in the javascript function is another javascript function, but I have put alerts in SetDates, in the Dev environment the code does not get that far, presumably because it will not find the code in the ValidationController.

Any helpful suggestions would be very greatly appreciated!

Specs: MVC3, Javascript 1.6.4, Jquery jquery-ui-1.8.17, IIS 7.5, Umbraco 4.7.1, Visual Studio 2010

Javascript code in separate .js file:

function getDisabledDatesByWidget(urlGetDisabledDatesByWidget) {
    alert('urlGetDisabledDatesByWidget = ' + urlGetDisabledDatesByWidget);
    $.ajax({
        //data: {id: id},
        type: "POST",
        async: false,
        url: urlGetDisabledDatesByWidget,
        dataType: 'json',
        success: setDates,
        constrainInput: true,
        defaultDate: new Date(defaultCutoff + " 00:00")
    });
}

MVC code in the view, in the document ready function:

    widgetId = '@ViewBag.WidgetId';
    var urlGetDisabledDatesByWidget = '@Url.Action("getDisabledDates", "Validation", new{id = @ViewBag.WidgetId})';
    getDisabledDatesByWidget(urlGetDisabledDatesByWidget);

MVC code in ValidationController:

[HttpPost]
public JsonResult getDisabledDates(int id)
{
    List<String> disabledDateStrings = new List<String>();
    List<DateTime> _DisabledDateDates = DisabledDateSetup.GetDisabledDateDatesByWidget(id).Value.ToList();

    ////Convert disabled dates to string array
    foreach (DateTime disabledDate in _DisabledDateDates)
    {
        disabledDateStrings.Add(disabledDate.ToString("M-d-yyyy"));
    }

    ChangeType _changeType = ChangeType.NoChangeType;
    ChangeDateRangeSetupResponse _dates = DisabledDateSetup.GetValidChangeDateRange(_changeType);

    return Json(new
    {
        disabledDates = disabledDateStrings,
        startDate = UnixTicks(_dates.Value.BeginDate),
        endDate = UnixTicks(_dates.Value.EndDate)
    });
}

MVC code in the view, in the div:

 @Html.TextBoxFor(model => model.SetupDate, new { id = "DisabledDateCalendar"})

Global asax routing:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Account", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );

}

What I've done to try to resolve this before posting:

While I agree with a lot of Eli's answer, I do not agree with his dismissal of Url.Action and the other URL generating MVC helpers. The point to them is that they're hooked into the routing engine of MVC, so if a route changes and you're using the URL helpers, you have to do zero updating of URLs in your views. But if you've hard-coded all the URLs instead of using Url.Action, you'll have to go back and manually change each URL.

Here's an excellent article from Phil Haack with an example of how Url.Action saved a LOT of headaches when working around a bug in MVC: http://haacked.com/archive/2011/02/20/routing-regression-with-two-consecutive-optional-url-parameters.aspx

I never let Razor inject data into my JavaScript, only semantically into HTML. Also, instead of using Url.Action and those weird helpers, how about just rendering the URL and markup manually? MVC seems to add a lot of needless methods, plus it's easier to read where things are actually going:

Instead of:

@Html.TextBoxFor(model => model.SetupDate, new { id = "DisabledDateCalendar"})

How about:

<input type="text" id="DisabledDateCalendar" value="@Model.SetupDate" />

Make your other code semantically accessible from JavaScript:

<input type="hidden" id="widgetId" value="@ViewBag.WidgetId" />
<input type="hidden" id="urlGetDisabledDatesByWidget" value="/secure/validation/getdisableddates/@ViewBag.WidgetId" />

Also, is there a reason your AJAX is synchronous? Let's make that async, and now you don't have Razor mixed in with your JS:

function getDisabledDatesByWidget(urlGetDisabledDatesByWidget) {
    var urlGetDisabledDatesByWidget = $('#urlGetDisabledDatesByWidget').val(),
        widgetId = $('#widgetId').val();

    alert('urlGetDisabledDatesByWidget = ' + urlGetDisabledDatesByWidget);

    $.ajax({
        //data: {id: id},
        type: "post",
        url: urlGetDisabledDatesByWidget,
        dataType: 'json',
        constrainInput: true,
        defaultDate: new Date(defaultCutoff + " 00:00")
    })
    .done(setDates);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM