📜 ⬆️ ⬇️

Getting .NET metadata on the client using ajax

Everyone who programs in ASP.NET MVC is well aware of how widely used metadata is in .NET in general and in MVC in particular. In MVC, attributes are applied both when generating markup and when validating data received from a client.
Using the classic website programming model works great. But what if you work with ajax and generate html markup dynamically on the client? Do you want to have model metadata (hereinafter MDM) on the client? I am yes!

The direct path is to form json, including both data and MDM there. We can always write something like in the controller:
public ActionResult GetData() { return Json(new { data = new {  }, meta = new {  } }); } 

It's simple, but not convenient when working with a model in the browser, and it looks somehow unattractive.
It occurred to me that the transfer of MDM should be the same as it does the server or browser, namely in the http headers. Let's try the following:
  1. Let's form an object meta.
  2. Serialize it into json string.
  3. We do encoding or converting the string to base64 (this is necessary, since the http header is transferred to ASCII).
  4. Create an http header with the name “meta-data”.

Next, I wrote a simple code that implements this idea.
First we define the model and put the attributes on the properties.
 public class Data { [ReadOnly(true)] [DisplayName("")] public int Id { get; set; } [DisplayName("")] public string Name { get; set; } } 


We write a simple action method that will return data and MDM to the client.
 public ActionResult GetData() { var data = new Data { Id = 1, Name = "Test" }; var meta = ModelMetadataProviders.Current.GetMetadataForType(() => data, typeof(Data)); var metaForJS = meta.Properties.ToDictionary( p => p.PropertyName, p => new { displayName = p.GetDisplayName(), readOnly = p.IsReadOnly }); var jsonMeta = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(metaForJS); // Uri.EscapeDataString -    .     base64. Response.Headers.Add("data-meta", Uri.EscapeDataString(jsonMeta)); return Json(data, JsonRequestBehavior.AllowGet); } 

We type in the address bar of the browser localhost : 67578 / Hab / GetData and look at the http header.
 HTTP/1.1 200 OK Cache-Control: private Content-Type: application/json; charset=utf-8 Server: Microsoft-IIS/8.0 X-AspNetMvc-Version: 4.0 data-meta:5B%7B%22displayName%22%3A%22%D0%9D%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD %D0%B8%D0%B5%22%2C%22readOnly%22%3Afalse%7D%2C%7B%22displayName%22%3A%22%D0%9D %D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%22%2C%22readOnly%22%3Atrue%7D%5D X-AspNet-Version: 4.0.30319 ... 

Let us make sure that everything works as it should, and proceed to the client part of the code.

 function createField(name, value, meta) { //      return $('<div/>').append( $("<label/>").attr('for', name).text(meta[name].displayName), $("<input type='text'/>").attr("name", name).attr("readonly", meta[name].readOnly).val(value) ); } $(function () { $.getJSON("/Hab/GetData") .done(function (data, s, xhr) { // ,    js  meta var meta = $.parseJSON(decodeURIComponent(xhr.getResponseHeader("data-meta"))); //       for (var p in data) $('body').append(createField(p, data[p], meta)); }) .error(function(d,s){alert(s);}); }); 

As a result, we get two input fields based on the model metadata on the server.
')
This approach is especially elegant when used on the backbone client. We can get MDM by overriding the parse method:
 var model= Backbone.Model.extend({ .... parse: function(data, xhr){ this.meta = $.parseJSON(decodeURIComponent(xhr.getResponseHeader("data-meta"))); return data; }, ...... }); 

Having obtained MDM in model.meta as an object js, we can use MDM in view to render the model
 var view = Backbone.View.extend({ render: functoin(){ var m = this.model.toJSON(); this.$el.html(this.template(_.extend(m, { meta: this.model}))); } }); 

using tempalte for something like:
 <script > ... <label><%-meta.name.displayName%></label> <input type='text' value="<%name%>" <%-meta.readOnly ?"readonly":"" %> />" ... </script> 

In conclusion, I will add that the described approach can be easily applied to validate the model on the client.

Source: https://habr.com/ru/post/152845/


All Articles