DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. NancyFX and Content Negotiation

NancyFX and Content Negotiation

Phillip Haydon user avatar by
Phillip Haydon
·
Feb. 01, 13 · Interview
Like (0)
Save
Tweet
Share
4.80K Views

Join the DZone community and get the full member experience.

Join For Free

this has to be one of the most awesome features of nancy, content negotiation. recently added in 0.12, it gives you the ability to implement a single route that responds with different versions of the same document, without having to mess up your code with duplicate methods or conditional statements.

when doing this in asp.net mvc i would have to check the content type and decide how i want to respond to the request.

this ended up making duplicate methods, one which would be used by a normal get request, while the 2nd would be for an ajax request. or if it was similar, use conditional logic in the single method to decide how the action should respond…

nancy on the other hand supports content negotiation out of the box.

get["/negotiated"] = parameters => {
    return negotiate
        .withmodel(new ratpack {firstname = "nancy "})
        .withmediarangemodel("text/html", new ratpack {firstname = "nancy fancy pants"})
        .withview("negotiatedview")
        .withheader("x-custom", "somevalue");
};

note: sample taken from nancy github repo

what is content negotiation?

in short, it’s the ability to serve different versions of a document to the same uri.

to read more you can visit wikipedia or soa patterns … or… google

why should i care?

well lets assume we’re building a website and we have a shopping cart, we get to the checkout page and there’s a button to delete the item from your cart.

you want to give a really nice user experience by not posting back the entire page. you would rather just tell the server to delete the item from the cart, and do a quick update on the screen, and avoid the whole page reloading.

however what if the page is taking a long time to load, and as a result, the javascript hasn’t been executed and wired up all the events to the buttons to delete items from your cart, you still want to be able to post the entire page and maintain the usability of the page.

the same scenario occurs if the user has (unlikely) turned javascript off.

implementation

so using the example above, deleting an item from a cart and updating the page, using javascript verses a full postback of the page, using the exact same route.

let’s create a really basic module:

public class homemodule : nancymodule
{
    public class product
    {
        public int id { get; set; }
        public string name { get; set; }
        public decimal price { get; set; }
    }
    public static ilist<product> products = new list<product>()
    {
        new product {id = 1, name = "surface", price = 499},
        new product {id = 2, name = "ipad", price = 899},
        new product {id = 3, name = "nexus 10", price = 599},
        new product {id = 4, name = "think pad", price = 499},
        new product {id = 5, name = "yoga", price = 699},
    };

    public dynamic model = new expandoobject();

    public homemodule()
    {
        model.deleted = false;

        get["/"] = _ =>
        {
            model.products = products;

            return view["index", model];
        };
    }
}

i’ve nested a product class in there, and created a static list of products for demo.

we need a view to go with this to display the products:

<h2>products</h2>
<p>posted back using: <span class="status">@if ((bool)model.deleted) { @html.raw("full postback"); }</span></p>

<table>
  <thead>
    <tr>
      <th style="width: 50px;">id</th>
      <th style="width: 90px;">name</th>
      <th style="width: 50px;">price</th>
      <th style="width: 150px;"> </th>
      <th style="width: 160px;"> </th>
    </tr>
  </thead>

  @foreach (var product in model.products)
  {
    <tr>
      <td>@product.id</td>
      <td>@product.name</td>
      <td>@product.price.tostring("c")</td>
      <td><a href="/delete/@product.id">delete with javascript</a></td>
      <td><a href="/delete/@product.id">delete without javascript</a></td>
    </tr>  
  }
</table>

this rendered will display the following:

so when we press the ‘delete without javascript’ button we want it to remove the item, so we can add a new route:

get[@"/delete/{id}"] = _ =>
{
    var id      = (int) _.id;
    var item    = products.single(x => x.id == id);

    products.remove(item);
    model.products = products;
    model.deleted = true;

    return view["index", model];
};

now if we press the button:

we can see the url has updated, and the 3rd item was deleted from the list. it also updated the text to day it used a full postpack. we can see that a full postback has occurred since the url changed.

now we want to make the ‘delete with javascript’ button work.

so we can add some javascript:

<script src="/scripts/jquery-1.8.2.min.js"></script>
<script>
  (function ($) {
    $(document).on("click", 'a:contains(delete with javascript)', function (e) {

      e.preventdefault();

      var that = $(this),
          tr = that.closest('tr');

      $.ajax({
        url: this.href,
        type: 'get',
        datatype: 'json',
        contenttype: 'application/json; charset=utf-8'
      }).success(function (data) {
        if (data.deleted === true) {
          tr.remove();
          $('.status').text("using javascript");
        }
      });

    });

  })(jquery);
</script>

so this just looks for an anchor tag with the text ‘delete with javascript’ since we don’t want to stop the other buttons from working.

now we need to update the route to handle content negotiation.

get[@"/delete/{id}"] = _ =>
{
    var id      = (int) _.id;
    var item    = products.single(x => x.id == id);

    products.remove(item);
    model.products = products;
    model.deleted = true;

    return negotiate
        .withmodel((object) model)
        .withmediarangemodel("application/json", new
        {
            model.deleted
        })
        .withview("index");
};

the implementation is identical to before, the only difference is we replaced ‘view’ with ‘negotiate’.

note: the model being returned is dynamic, this allows me to just add the properties to it without having to define a static class. as a result i have to cast the model to an object when passing it into the method ‘withmodel’, if i was using a static class i wouldn’t need to do this.

if the type if different, in this case the request was for application/json, then we pass back only the data we need, rather than everything.

now if we run the same page again but we click on ‘delete with javascript’

this time when we delete item 4, the url hasn’t changed, but it’s removed the item and said it was done using the javascript.

now when we click either link, both scenarios are handled by the one route.

also…

this isn’t limited to just this use case, if you’re building an api and you want the route to respond with xml or json, or maybe even your own content type, this is an awesome feature that you can use to handle these scenarios so your consumers can get what they want, in the format they want, with no effort from you.

content negotiation – is awesome.

the demo for this project can be found here on github .

JavaScript Requests ASP.NET MVC User experience Awesome (window manager) GitHub Document Implementation IT

Published at DZone with permission of Phillip Haydon. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How to Create a Real-Time Scalable Streaming App Using Apache NiFi, Apache Pulsar, and Apache Flink SQL
  • Better Performance and Security by Monitoring Logs, Metrics, and More
  • Kubernetes vs Docker: Differences Explained
  • How To Check Docker Images for Vulnerabilities

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: