NEW!
CSHTML5 has now become OpenSilver!

We are happy to announce that CSHTML5 has been significantly improved and rebranded to 'OpenSilver', which stands for 'Open-source reimplementation of Silverlight'. It is fully backward compatible and it can be downloaded from OpenSilver.net. Upgrading from CSHTML5 to OpenSilver is very easy.
Read the FAQ

WCF and WebClient Limitations and Tutorials

Table of Contents

- Introduction

- Sample application

- Limitations of the "Add Service Reference" support (SOAP)

- Adding support for cross-domain calls (CORS)

- WCF authentication / cookies / passing credentials

- Tips for debugging WCF services

- Common issues and solutions

- Tutorial to easily create a SOAP-based client/server app in CSHTML5 (WCF)

- Tutorial to easily create a REST-based client/server app in CSHTML5 (Web API)

- See Also

- Support

Introduction

C#/XAML for HTML5 provides support for web services and HTTP calls in multiple ways, including:

Sample application

You can download a sample SOAP-based client/server application from the following URL:

http://cshtml5.com/downloads/TestCshtml5WCF.zip

IMPORTANT: the sample requires version 1.2 or newer.

The sample shows a basic To-Do management application.

Limitations of the "Add Service Reference" support (SOAP)

In the current version, C#/XAML for HTML5 has the following limitations regarding the "Add Service Reference" (SOAP) feature:

Furthermore, please note that the following features are NOT yet supported:

We are working to add support for all of the above features as soon as possible. Please make sure to vote for your most wanted features on http://cshtml5.uservoice.com

Adding support for cross-domain calls (CORS)

Due to the JavaScript restrictions in the browser, cross-domain calls require the server to implement CORS. In other words, if your client application is not hosted on the same domain as your WCF web service, you need to add CORS to the web service (or you can use JSONP as an alternative to CORS, but it is not recommended for modern services).

If you see the error "No 'Access-Control-Allow-Origin' header is present on the requested resource" in the browser Console window (F12), it is likely that CORS has not been properly configured.

 

Here are your options:

 

To add CORS to your web service (recommended), simply follow these steps:

1) If you are using a SOAP web service:

i) Make sure you have the file "Global.asax.cs" in your server-side SOAP web service project. If you don't have that file, right-click the project name in the Solution Explorer, and click "Add" -> "Global Application Class".

ii) Add the following code to the said file:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");

    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        //These headers are handling the "pre-flight" OPTIONS call sent by the browser
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, SOAPAction");
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
        HttpContext.Current.Response.End();
    }
}

To see this code in action, follow the SOAP tutorial below.

2) If you are using a REST / Web API web service:

i) Modify Web.Config to add the following code (note: you need to add the code to the right section of Web.Config:

<configuration> 
  <system.webServer>

    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, Content-Length" />
        <add name="Access-Control-Allow-Methods" value="POST,PUT,GET,DELETE,OPTIONS" />
      </customHeaders>
    </httpProtocol>

  </system.webServer>
</configuration>

ii) Add the following code to each of your Web API controller classes:

// OPTIONS
public HttpResponseMessage Options()
{
    return Request.CreateResponse(HttpStatusCode.OK);
}

To see this code in action, follow the REST tutorial below.

WCF authentication / cookies / passing credentials

Tips for debugging WCF services

Common issues and solutions

Tutorial to easily create a SOAP-based client/server app in CSHTML5 (WCF)

In this tutorial we are going to create a simple client/server application for managing To-Do items. If you have any feedback regarding this tutorial, please contact us.

Note: a sample project source code is available here.

1) Create a new project of type "WCF -> WCF Service Application". Let's call it "WcfService1"

2) Replace IService1.cs with the following code:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace WcfService1
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        List<ToDoItem> GetToDos();

        [OperationContract]
        void AddOrUpdateToDo(ToDoItem toDoItem);

        [OperationContract]
        void DeleteToDo(ToDoItem toDoItem);
    }

    [DataContract]
    public class ToDoItem
    {
        [DataMember]
        public Guid Id { get; set; }

        [DataMember]
        public string Description { get; set; }
    }
}

3) Replace Service1.svc with the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;

namespace WcfService1
{
    public class Service1 : IService1
    {
        private static Dictionary<Guid, ToDoItem> _todos = new Dictionary<Guid, ToDoItem>();

        public List<ToDoItem> GetToDos()
        {
            return _todos.Values.ToList();
        }

        public void AddOrUpdateToDo(ToDoItem toDoItem)
        {
            _todos[toDoItem.Id] = toDoItem;
        }

        public void DeleteToDo(ToDoItem toDoItem)
        {
            if (_todos.ContainsKey(toDoItem.Id))
                _todos.Remove(toDoItem.Id);
            else
                throw new FaultException("ID not found: " + toDoItem.Id);
        }
    }
}

4) Create the file "Global.asax" by right-clicking the project in the Solution Explorer, and clicking Add -> Global Application Class -> OK. Add the following code to it:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");

    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        //These headers are handling the "pre-flight" OPTIONS call sent by the browser
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, SOAPAction");
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
        HttpContext.Current.Response.End();
    }
}

5) Click "Start Debugging" (F5) and take note of the URL of the service. It should be something like: http://localhost:4598/Service1.svc
Keep the project running.

6) In a new instance of Visual Studio, create a new project of type C#/XAML for HTML5 -> Empty Application.

7) Click Project -> Add Service Reference, and paste the URL of the service that you created above.  It should be something like: http://localhost:4598/Service1.svc where you must replace 4958 with your port number. Click GO and then OK (leave the default name "ServiceReference1").

8) Manually remove the following references from the project references: System, System.Runtime.Serialization, System.ServiceModel, System.Xml (and any other DLL that starts with "System.").

9) Modify the page MainPage.XAML by removing the default TextBlock and replacing it with the following code:

<StackPanel>
    <TextBlock Text="CREATE A NEW TO-DO:" Margin="0,20,0,0" Foreground="Black" HorizontalAlignment="Left"/>
    <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
        <TextBox x:Name="SoapToDoTextBox" Width="200" Text="Enter your To-Do here" Foreground="Black" Background="#FFEEEEEE"/>
        <Button Content="Create" Click="ButtonAddSoapToDo_Click" Foreground="White" Background="#FFE44D26" Margin="5,0,0,0"/>
    </StackPanel>
    <TextBlock Text="LIST OF TODO's:" Margin="0,20,0,0" Foreground="Black" HorizontalAlignment="Left"/>
    <ItemsControl x:Name="SoapToDosItemsControl" HorizontalAlignment="Left">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                    <TextBlock Text="{Binding Description}" Foreground="Black"/>
                    <Button Content="Delete" Click="ButtonDeleteSoapToDo_Click" Foreground="White" Background="#FFE44D26" Margin="5,0,0,0"/>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <Button Content="Refresh the list" Foreground="White" Background="#FFE44D26" Click="ButtonRefreshSoapToDos_Click" HorizontalAlignment="Left" Margin="0,10,0,0"/>
</StackPanel>

10) Add the following code to MainPage.xaml.cs (IMPORTANT: be sure to replace the URL in red with the correct one - ie. use the same URL as above):

ServiceReference1.Service1Client _soapClient =
        new ServiceReference1.Service1Client(
            new System.ServiceModel.BasicHttpBinding(),
            new System.ServiceModel.EndpointAddress(
                new Uri("http://localhost:4598/Service1.svc")));

void ButtonRefreshSoapToDos_Click(object sender, RoutedEventArgs e)
{
    var todos = _soapClient.GetToDos();
    SoapToDosItemsControl.ItemsSource = todos;
}

void ButtonAddSoapToDo_Click(object sender, RoutedEventArgs e)
{
    var todo = new ServiceReference1.ToDoItem()
    {
        Description = SoapToDoTextBox.Text,
        Id = Guid.NewGuid()
    };
    _soapClient.AddOrUpdateToDo(todo);
    ButtonRefreshSoapToDos_Click(sender, e);
}

void ButtonDeleteSoapToDo_Click(object sender, RoutedEventArgs e)
{
    try
    {
        var todo = (ServiceReference1.ToDoItem)((Button)sender).DataContext;
        _soapClient.DeleteToDo(todo);
        ButtonRefreshSoapToDos_Click(sender, e);
    }
    catch (System.ServiceModel.FaultException ex)
    {
        // Fault exceptions allow the server to pass information such as "Item not found":
        System.Windows.MessageBox.Show(ex.Message);
    }
}

11) Click "Start Debugging" to test your client/server To-Do items application.

Tutorial to easily create a REST-based client/server app in CSHTML5 (Wep API)

In this tutorial we are going to create a simple client/server application for managing To-Do items. If you have any feedback regarding this tutorial, please contact us.

1) Create a new project of type "Web -> ASP.NET MVC 4 Web Application" (or newer). Let's call it "MvcApplication1".

When you click OK, a second dialog appears that lets you choose a Project Template. Be sure to select the "Web API" project template. Makes sure the option "Create a unit test project" is NOT checked, and click OK.

2) Modify Web.Config to add the following code (note: you need to add the code to the right section of Web.Config):

<configuration> 
  <system.webServer>

    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, Content-Length" />
        <add name="Access-Control-Allow-Methods" value="POST,PUT,GET,DELETE,OPTIONS" />
      </customHeaders>
    </httpProtocol>

  </system.webServer>
</configuration>

3) Create a new class called "ToDoItem.cs" inside the folder named "Models". Copy/paste the following code:

using System;

namespace MvcApplication1.Models
{
    public class ToDoItem
    {
        public Guid Id { get; set; }
        public Guid OwnerId { get; set; }
        public string Description { get; set; }
    }
}

4) Right-click the folder "Controllers" and click "Add -> Controller...". Enter the name "ToDoController" and choose "Empty MVC Controller" in the "Template" drop-down. Then click OK.
Note: the name of the controller is very important because it will have a direct impact on the URL of your REST web service.

using MvcApplication1.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MvcApplication1.Controllers
{
    public class ToDoController : ApiController
    {
        private static Dictionary<Guid, ToDoItem> _todos = new Dictionary<Guid, ToDoItem>();

        // GET api/ToDo
        public IEnumerable<ToDoItem> GetToDos()
        {
            return _todos.Values.ToList();
        }

        // GET api/ToDo/5
        public ToDoItem GetToDo(Guid id)
        {
            if (_todos.ContainsKey(id))
                return _todos[id];
            else
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
                    {
                        Content = new StringContent("ID not found: " + id),
                        ReasonPhrase = "ID not found"
                    });
        }

        // OPTIONS
        public HttpResponseMessage Options()
        {
            return Request.CreateResponse(HttpStatusCode.OK);
        }

        // PUT api/Todo/5
        public HttpResponseMessage PutToDoItem(Guid id, ToDoItem toDoItem)
        {
            if (id != toDoItem.Id)
                return new HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    Content = new StringContent("The ID must be the same as the ID of the todo."),
                    ReasonPhrase = "The ID must be the same as the ID of the todo"
                };

            _todos[toDoItem.Id] = toDoItem;
            return Request.CreateResponse(HttpStatusCode.OK);
        }

        // POST api/Todo
        public HttpResponseMessage PostToDoItem(ToDoItem toDoItem)
        {
            _todos[toDoItem.Id] = toDoItem;

            return Request.CreateResponse(HttpStatusCode.OK);
        }

        // DELETE api/Todo/5
        public HttpResponseMessage DeleteToDoItem(Guid id)
        {
            if (_todos.ContainsKey(id))
            {
                var toDoItem = _todos[id];
                _todos.Remove(id);
                return Request.CreateResponse(HttpStatusCode.OK, toDoItem);
            }
            else
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
                    {
                        Content = new StringContent("ID not found: " + id),
                        ReasonPhrase = "ID not found"
                    });
        }
    }
}

5) Click "Start Debugging" (F5) and take note of the URL of the service. It should be something like: http://localhost:4858
Keep the project running.

6) In a new instance of Visual Studio, create a new project of type C#/XAML for HTML5 -> Empty Application.

7) Modify the page MainPage.XAML by removing the default TextBlock and replacing it with the following code:

<StackPanel>
    <TextBlock Text="CREATE A NEW TO-DO:" Margin="0,20,0,0" Foreground="Black" HorizontalAlignment="Left"/>
    <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
        <TextBox x:Name="RestToDoTextBox" Width="200" Text="Enter your To-Do here" Foreground="Black" Background="#FFEEEEEE"/>
        <Button Content="Create" Click="ButtonAddRestToDo_Click" Foreground="White" Background="#FFE44D26" Margin="5,0,0,0"/>
    </StackPanel>
    <TextBlock Text="LIST OF TODO's:" Margin="0,20,0,0" Foreground="Black" HorizontalAlignment="Left"/>
    <ItemsControl x:Name="RestToDosItemsControl" HorizontalAlignment="Left">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                    <TextBlock Text="{Binding Description}" Foreground="Black"/>
                    <Button Content="Delete" Click="ButtonDeleteRestToDo_Click" Foreground="White" Background="#FFE44D26" Margin="5,0,0,0"/>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <Button Content="Refresh the list" Foreground="White" Background="#FFE44D26" Click="ButtonRefreshRestToDos_Click" HorizontalAlignment="Left" Margin="0,10,0,0"/>
</StackPanel>

8) Add the following code to MainPage.xaml.cs (IMPORTANT: be sure to replace the URL in yellow with the correct one - ie. use the same URL as above):

const string BaseUrl = "http://localhost:4858";

void ButtonRefreshRestToDos_Click(object sender, RoutedEventArgs e)
{
    var webClient = new WebClient();
    webClient.Encoding = Encoding.UTF8;
    webClient.Headers[HttpRequestHeader.Accept] = "application/xml";
    string response = webClient.DownloadString(BaseUrl + "//api/Todo");
    var dataContractSerializer = new DataContractSerializer(typeof(List<ToDoItem>));
    List<ToDoItem> toDoItems = (List<ToDoItem>)dataContractSerializer.DeserializeFromString(response);
    RestToDosItemsControl.ItemsSource = toDoItems;
}

void ButtonAddRestToDo_Click(object sender, RoutedEventArgs e)
{
    string data = string.Format(@"{{""Id"": ""{0}"",""Description"": ""{1}""}}", Guid.NewGuid(), RestToDoTextBox.Text.Replace("\"", "'"));
    var webClient = new WebClient();
    webClient.Headers[HttpRequestHeader.ContentType] = "application/json";
    webClient.Encoding = Encoding.UTF8;
    string response = webClient.UploadString(BaseUrl + "/api/Todo/", "POST", data);
    ButtonRefreshRestToDos_Click(sender, e);
}

void ButtonDeleteRestToDo_Click(object sender, RoutedEventArgs e)
{
    ToDoItem todo = (ToDoItem)((Button)sender).DataContext;
    var webClient = new WebClient();
    string response = webClient.UploadString(BaseUrl + "/api/Todo/" + todo.Id.ToString(), "DELETE", "");
    ButtonRefreshRestToDos_Click(sender, e);
}

public class ToDoItem
{
    public Guid Id { get; set; }
    public Guid OwnerId { get; set; }
    public string Description { get; set; }
}

9) Click "Start Debugging" to test your client/server To-Do items application.

 

See Also

 

Support

For any question, please post a message on the forums or contact us.