ASP Webforms AJAX
Webforms UpdatePanels
These are very easy way of updating webform sections with ajax. Under the covers it's fairly heavyweight, but for a traditional webform pages it's very effective.
Remember to add EnablePartialRendering
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
A ScriptManagerProxy can be used on content pages if the ScriptManager is in a MasterPage
By default, each panel update is fired by its children (ChildrenAsTriggers). Add triggers, either a PostBackTrigger to force a full page refresh, or a AsyncPostBackTrigger to fire a partial update from any control on the page.
Use asp:Timer either inside the UpdatePanel, or outside with a trigger, to refresh at Interval (ms).
Use asp:UpdateProgress (with displayAfter="1000") with a <ProgressTemplate> to show a loading image or text.
- UpdateMode=Always means it refreshes for any postback anywhere on page (INCLUDING partial updates in other updatePanels).
- Use UpdateMode=Conditional with nested updatePanels so only child updatePanels update.
For back/forward support (MSDN):
<asp:ScriptManager ID="ScriptManager1" runat="server"
EnableHistory="true" EnableSecureHistoryState="false"
onNavigate="ScriptManager1_Navigate">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
<asp:Label ID="Label1" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
Record the history transitions in code behind with AddHistoryPoint and handle the ScriptManager.OnNavigate event.
protected void Button1_Click(object sender, EventArgs e)
{
ShowData(DateTime.Now.ToLongTimeString());
}
private void ShowData(string s)
{
Label1.Text = s;
if (ScriptManager1.IsInAsyncPostBack && !ScriptManager1.IsNavigating)
{
ScriptManager1.AddHistoryPoint("History", s, s);
}
}
protected void ScriptManager1_Navigate(object sender, HistoryEventArgs e)
{
ShowData(e.State["History"]);
}
Asmx ScriptService
Just add the [ScriptService] to the class and [WebMethod].
Client side it's much more lightweight with jQuery (see jQuery notes)
var notify = $("#notification");
$.ajax({
url: '<%= ResolveClientUrl("~/NotificationService.svc") %>/Load',
success: function (data) {
data = data.d ? data.d : data;
notify.text(data.message);
notify.show("slow");
},
error: function (jqXHR, msg) { notify.show("slow").text("Error: " + msg); }
});
With a POST
$.ajax({
type: "POST",
contentType: "application/json",
url: "WebService.asmx/WebMethodName",
data: "{}",
success: function (x) {
data = data.d ? data.d : data;
alert(x.Message); }
});
Important: the jQuery data parameter should be quoted (a string - eg use JSON.stringify()), otherwise jQuery URL encodes your object (and asmx MUST be json).
$.getJSON(url) doesn't send the contentType of application/json (apparently) so doesn't work.
You may need to add a jQuery dataFilter to deal with ASP.Net dates (json doesn't have native date support).
Caution: unlike ASP MVC's DefaultDataBinder, the WCF is very fussy so [DataContract] inputs must match the posted json exactly. Here's the pattern:
var data = { category: {
id: $('#<%= SelectId.ClientID %>').val(),
name: $('#<%= CategoryName.ClientID %>').val()
}
};
var s = JSON.stringify(data);
var throbber = $('#<%= Throbber.ClientID %>');
throbber.toggleClass("hidden");
$.ajax({
type: "POST",
contentType: "application/json",
url: "Service1.svc/SaveCategory",
data: s,
success: function (data) {
data = data.d ? data.d : data;
show(data); },
error: function (msg) { show(msg); throbber.toggleClass("hidden"); }
});
The contract must match in case.
[DataContract(Name = "category")]
public class Category
{
[DataMember(Name = "name", IsRequired = false)]
public string CategoryName { get; set; }
[DataMember(Name = "id", IsRequired = false)]
public string CategoryId { get; set; }
ASP.Net 3.5+ returns a Json object wrapped with a "d" to prevent XSS. Either
data = data.d ? data.d : data;
or (slightly safer if you return a boolean)
data = data.hasOwnProperty("d") ? data.d : data;
AjaxMethods
Full details here, but basically you need:
- asp:ScriptManager EnablePageMethods="true"
- [WebMethod] on a static method of the code behind Page
- In client javascript, you get a PageMethods.xxx(arg1, arg2, onSuccess, onFail) function.
Exposing WCF to Ajax
See also WCF; MSDN RESTful WCF
Use Add New Item = AJAX-enabled WCF Service template.
On service class add
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
- On the [OperationContract] you can add [WebInvoke]/ [WebGet]
- HTTP verbs: GET= [WebGet]. Or: [WebInvoke(Method = "GET")] (default = POST)
- Default request/response is Json
On the js client, use "Content-Type", "application/json" mime type (see above) - For xml - use [WebInvoke(ResponseFormat = WebMessageFormat.Xml)] and add the line
WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml"; - The request format can be dynamic (Json/Xml/querystring depending on what is sent)- automaticFormatSelectionEnabled=true
<behaviors>
<endpointBehaviors>
<behavior>
<webHttp automaticFormatSelectionEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors> - WebOperationContext.Current has properties IncomingRequest and OutgoingResponse, methods CreateJsonResponse<T> (and Xml, Text and Atom).
- There's URI routing/mapping for simple scalar arguments:
[WebInvoke(UriTemplate = "Stuff?id={id}")]
public string GetStuff(int id)
You can delete all the WCF configuration if you change the .svc to use the WebScriptServiceHostFactory.
<%@ ServiceHost Language="C#" Debug="true" Service="MyWebApp.Service1" CodeBehind="Service1.svc.cs"
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>
In WCF, you can authenticate using ASP.Net authentication by creating an svc/endpoint with Service="System.Web.ApplicationServices.AuthenticationService". Enable the services in system.web.extensions/scripting/webServices/authenticationService enabled="true".
WCF vs Ajax ScriptService
Asmx | WCF |
---|---|
[ScriptService] | [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] |
[WebMethod] | [OperationContract] plus [WebInvoke]/ [WebGet] |
Ajax's JavascriptSerializer is better with dictionaries with string keys than WCF's DataContractJsonSerializer, and more flexible with dates |