ASP.NET MVC vs Rails Routes

Leave a comment

One thing I have always wanted to compare are the default routes you get with Rails, with the default routes you get with ASP.NET MVC.

Rails

rails-routes

ASP.NET MVC4

GET /Permits Index
GET /Permits/Create Create
POST /Permits/Create Create
GET /Permits/Details/1 Details
GET /Permits/Edit/5 Edit
POST /Permits/Edit/5 Edit
GET /Permits/Delete/5 Delete
POST /Permits/Delete/5

Pretty close. Biggest differences are Rails does

/photos/:id/edit

for editing a resource while ASP.NET MVC does

/Permits/Edit/5

And some of the action names are different (new/create, show/details, update/edit, destroy/delete).

Of course you can always change your routing logic in ASP.NET MVC if you want something closer to what they do it in Rails:

http://stackoverflow.com/questions/9332026/asp-net-mvc-4-route-news-5

Anyways. Just something I have always wanted look at and compare.

Restful WCF service with Silverlight4

5 Comments

I’ve spent the last couple weeks wrestling with WCF4 and Silverlight4.

Here is some code that does a GET/POST/PUT/DELETE with a Silverlight4 client.

The Restful WCF Service

    [ServiceContract]
    public interface IInvoiceService
    {
        [WebGet(UriTemplate = "/invoices")]
        [OperationContract]
        List GetAllInvoices();

        [WebGet(UriTemplate = "/invoices/{invoiceId}")]
        [OperationContract]
        Invoice GetInvoice(string invoiceId);

        [WebInvoke(UriTemplate = "/invoices/{invoiceId}", Method = "PUT")]
        [OperationContract]
        Invoice UpdateInvoice(string invoiceId, Invoice newInvoice);

        [WebInvoke(UriTemplate = "/invoices", Method = "POST")]
        [OperationContract]
        Invoice AddNewInvoice(Invoice newInvoice);

        [WebInvoke(UriTemplate = "/invoices/{invoiceId}", Method = "DELETE")]
        [OperationContract]
        void DeleteInvoice(string invoiceId);
    }

Here is the implementation:

  public class InvoiceService : IInvoiceService
    {
        protected static List<Invoice> invoices = new List<Invoice>
        {
            new Invoice(1, "reciever1"),
            new Invoice(2, "reciever2"),
            new Invoice(3, "reciever3"),
        };

        public List<Invoice> GetAllInvoices()
        {
            return invoices;
        }

        public Invoice GetInvoice(string invoiceId)
        {
            Invoice invoice = invoices.Find(x => x.Id == int.Parse(invoiceId));

            if (invoice == null)
            {
                WebOperationContext ctx = WebOperationContext.Current;
                ctx.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
            }

            return invoice;
        }

        public Invoice UpdateInvoice(string invoiceId, Invoice newInvoice)
        {
            Invoice current = invoices.Find(x => x.Id == int.Parse(invoiceId));

            if (current == null)
            {
                WebOperationContext ctx = WebOperationContext.Current;
                ctx.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
                return null;
            }

            invoices.Remove(current);
            invoices.Add(newInvoice);

            return newInvoice;
        }

        public Invoice AddNewInvoice(Invoice newInvoice)
        {
            Debug.WriteLine("AddNewInvoice");

            Invoice existingInvoice = invoices.Find(x => x.Id == newInvoice.Id);
            WebOperationContext ctx = WebOperationContext.Current;

            if (existingInvoice != null)
            {
                ctx.OutgoingResponse.StatusCode = HttpStatusCode.Conflict;
                return null;
            }

            invoices.Add(newInvoice);

            ctx.OutgoingResponse.StatusCode = HttpStatusCode.Created;
            ctx.OutgoingResponse.Location = PluginFactory.INSTANCE.InvoiceServiceUri() + newInvoice.Id;

            return new Invoice(newInvoice.Id, newInvoice.Receiver);
        }

        public void DeleteInvoice(string invoiceId)
        {
            var id = Convert.ToInt32(invoiceId);
            invoices = invoices.FindAll(invoice => invoice.Id != id);
        }
    }

The Silverlight Client

You have two options when consuming services on the client. You can use WebClient (which is easy but weak). Or you can use the more rich HttpWebRequest which you need to consume Status Codes. This example users the later. Here is the Silverlight client.

GET

        private void GetClick(object sender, RoutedEventArgs e)
        {
            var request = (HttpWebRequest)WebRequest.Create(url + IdTextBox.Text);
            request.BeginGetResponse(GetCompleted, request);
        }

        private void GetCompleted(IAsyncResult ar)
        {
            // necessary for concurrency
            Dispatcher.BeginInvoke(delegate
            {
                try
                {
                    var request = (HttpWebRequest)ar.AsyncState;
                    var response = (HttpWebResponse)request.EndGetResponse(ar);

                    using (var streamReader = new StreamReader(response.GetResponseStream()))
                    {
                        ParseComplex(response, streamReader);
                    }
                }
                catch (WebException)
                {
                    outputTextBlock.Text = "This id does not exist.";
                }
            });
        }

POST

private void PostComplexClick(object sender, RoutedEventArgs e)
        {
            ClearTextBoxes();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);
            request.Method = "POST";
            request.ContentType = postContentType;
            request.BeginGetRequestStream(PostComplexRequestReady, request);
        }

        private void PostComplexRequestReady(IAsyncResult asyncResult)
        {
            HttpWebRequest request = asyncResult.AsyncState as HttpWebRequest;
            Stream postStream = request.EndGetRequestStream(asyncResult);

            Dispatcher.BeginInvoke(delegate
            {
                Invoice newInvoice = new Invoice(int.Parse(IdTextBox.Text), RandomReceiver());
                byte[] byteData = Encoding.UTF8.GetBytes(newInvoice.AsXml());
                postStream.Write(byteData, 0, byteData.Length);
                postStream.Flush();
                postStream.Close();

                request.BeginGetResponse(PostComplexResponseReady, request);
            });
        }

        private void PostComplexResponseReady(IAsyncResult asyncResult)
        {
            Dispatcher.BeginInvoke(delegate
            {
                try
                {
                    var request = asyncResult.AsyncState as HttpWebRequest;
                    var response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                    using (var streamReader = new StreamReader(response.GetResponseStream()))
                    {
                        ParseComplex(response, streamReader);
                    }
                }
                catch (WebException e)
                {
                    dynamic exceptionResponse = e.Response;
                    if (exceptionResponse.StatusCode == HttpStatusCode.Conflict)
                        outputTextBlock.Text = "This user already exists.";
                    else
                        outputTextBlock.Text = e.Message;
                }
            });

        }

PUT

  private void PutClick(object sender, RoutedEventArgs e)
        {
            ClearTextBoxes();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + IdTextBox.Text);
            request.Method = "PUT";
            request.ContentType = postContentType;
            request.BeginGetRequestStream(PostComplexRequestReady, request);

            // Note: PUT is very similar to POST - only difference is url
        }

DELETE

   private void DeleteClick(object sender, RoutedEventArgs e)
        {
            ClearTextBoxes();

            var request = (HttpWebRequest)WebRequest.Create(url + IdTextBox.Text);
            request.Method = "DELETE";
            request.BeginGetResponse(GetCompleted, request);

            // Note: DELETE is very similar to GET
        }

%d bloggers like this: