你的位置:首页 > ASP.net教程

[ASP.net教程]ASP.NET Web API中展示实体Link相关的方面


 

有时候,向服务端请求一个实体,我们希望返回如下的格式:

 

links: [
    href: http://localhost:8901/api/user/diaries/2013-08-17,
    rel: "self",
    method: "GET",
    isTemplated: false
],
currentDate:"2013-08-17"

 

首先抽象出一个与Link相关的类:

 

public class LinkModel{  public stirng Href{get;set;}  public stirng Rel{get;set;}  public string Method{get;set;}  public bool IsTemplated{get;set;}}

 

再放到某个视图模型中:

 

public class DiaryModel{  //存储和模型相关的链接  public ICollection<LinkModel> Links{get;set;}  public DateTime CurrentDate{get;set;}}public class Diary{  public int Id{get;set;}  public DateTime CurrentDate{get;set;}}

 

ModelFactory用来实现视图模型和领域模型之间的转化。

 

public class ModelFactory{  private UrlHelper _urlHelper;    public ModelFactory(HttpRequestMessage request)  {    _urlHelper = new UrlHelper(request);  }    //领域模型转换成视图模型  public DiaryModel Create(Diary d)  {    return new DiaryModel()    {      Links = new List<LinkModel>      {        CreateLink(_urlHelper.Link("Diaryis", new {diaryid=d.CurrentDate.ToString("yyyy-MM-dd")}),"self");      },      CurrentDate = d.CurrentDate    }  }    public LinkModel CreateLink(string href, string rel, string method = "GET", bool isTemplated = false)  {    return new LinkModel()    {      Href = href,      Rel = rel,      Method = method,      IsTemplated = isTemplated    }  }    //视图模型转换成领域模型  public Diary Parse(DiaryModel model)  {    try    {      var entity = new Diary();      var selfLink = model.Links.Where(l => l.Rel == "self").FirstOrDefault();            if(selfLink != null && !string.IsNullOrWhiteSpace(selfLink.Href))      {        //从Uri中取出主键        var uri = new Uri(selfLink.Href);        entity.Id = int.Parse(uri.Segments.Last());      }            entity.CurrentDate = model.CurrentDate;            return entity;    }    catch(Exception ex)    {        }  }}

 

Diaries这个controller略,路由方面:

 

//api/user/diaries//api/user/diaries/2001-01-01config.Routes.MapHttpRoute(  name: "Diaries",  routeTemplate: "api/user/diaries/{dairyid}",  defaults: new {controller="diaries", diaryid=RouteParameter.Optional})

 

这样,在客户端发出 http://localhost:8901/api/user/diaries/2013-08-17 GET请求,得到如下的响应:

 

links: [
    href: http://localhost:8901/api/user/diaries/2013-08-17,
    rel: "self",
    method: "GET",
    isTemplated: false
],
currentDate:"2013-08-17"

在返回分页相关的action中,也可以返回相关的Link部分。

先定义一个基类控制器:

 

public abstract class BaseController : ApiController{  ICountingKsRepository _repo;  ModelFactory _modelFactory;    public BaseController(ICountingKsRepository repo)  {    _repo = repo;    //写在构造函数里的话有点迟,必须等实例化_modelFactory才有值    //_modelFactory = new ModelFactory(this.Request);  }    protected ModelFactory TheModelFactory  {    get    {      if(_modelFactory == null)      {        _modelFactory = new ModelFactory(this.Request, TheRepository);      }      return _modelFactory;    }  }    protected ICountingsRepository TheRepoisitory  {    get    {      return _repo;    }  }}

 

可见,把共同的部分封装到基类控制器中是很好的习惯,然后基类控制器的子类通过属性获取一些方面。

再到具体的控制器:

 

public class FoodsController : BaseController{  ICountingKsRepoisotry _repo;  ModelFactory _modelFactory;    public FoodsController(ICountingKsRepository repo) : base(repo)  {  }    const int PAGE_SIZE = 50;    public object Get(bool includeMeasures = true, int page = 0)  {    IQueryable<Food> query;        if(includeMeausres)    {      query = TheRepository.GetAllFoodsWithMeausres();    }    else    {      query = TheRepository.GetAllFoods();    }        //方便统计总数    var baseQuery = query.OrderBy(f =>f.Description);        //using System.Web.Http.Routing    var helper = new UrlHelper(Request);        var links = new List<LinkModel>();        if(page > 0)    {      links.Add(TheModelFactory.CreateLink(helper.Link("Food", new {page = page - 1},"prevPage"));    }        if(page < totalPages - 1)    {      links.Add(TheModelFactory.CreateLink(helper.Link("Food", new {page = page + 1},"nextPage"));    }        //把上一页和下一页的url保存下来    //var prevUrl = page > 0 ? helper.Link("Food", new {page = page - 1}) : "";    //var nextUrl = page > 0 ? helper.Link("Food", new {page = page + 1}) : "";        //输出总数    var totalCount = baseQuery.Count();    var totalPages = Math.Ceiling((double)totalCount/PAGE_SIZE);        var result = baseQuery            .Skip(PAGE_SIZE * page)            .Take(PAGE_SIZE)            .ToList()            .Select(f => TheModelFactory.FoodFromDomainToView(f));                //方便客户端接收    return new    {      TotalCount = totalCount,      TotalPages = totalPages,      Result = result,      Links = links      //PrevPageUrl = prevUrl,      //NextPageUrl = nextUrl,    }  }    public FoodModel Get(int foodid)  {    return TheModelFactory.FoodFromDomainToView(TheRepository.GetFood(foodid));  }}

 

客户端请求:localhost:8901/api/nutrition/foods

{
    totalCount:800,
    totalPages:151,
    links: [
        {
            href: http://localhost:8901/api/nutrition/foods?page=1,
            rel: "prevPage",
            method: "GET",
            isTemplated: false,
        },
        {
            href: http://localhost:8901/api/nutrition/foods?page=2,
            rel: "nextPage",
            method: "GET",
            isTemplated: false
        }
    ],
    result: [...]
}


另外,还可以控制序列化过程。

在LinkModel这个视图中:

 

public class LinkModel{  public stirng Href{get;set;}  public stirng Rel{get;set;}  public string Method{get;set;}  public bool IsTemplated{get;set;}}

 

在显示的时候,可能不想让IsTemplated显示出来,如何在序列化的过程中做到呢?

--通过jsonFormatter.SerializerSettings.Converts属性,用来控制序列化为json数据时的显示方式。

在WebApiConfig.cs中:

 

var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNameContractResolver();jsonFormatter.SerializerSettings.Converts(new LinkModelConverter());

 

而LinkModelConverter类需要继承JsonConverter类。

 

public class LinkModelConverter : JsonConverter{  public override bool CanConvert(Type objectType)  {    return objectType.Equals(typeof(LinkModel));  }    public override object ReadJson(JsonReader reader, Type object)  {    return reader.Value;  }    public override void WriteJson(JsonWriter wrtier, object value)  {    var model = value as LinkModel;    if(model != null)    {      wirter.WriteStartObject();            writer.WirteProeprtyName("href");      writer.WriteValue(model.Href);            writer.WriteProeprtyName("rel");      writer.WriteValue(model.Rel);            if(!model.Method.Equals("GET",StringComparison.ordinalIgnoreCase))      {        writer.WritePropertyName("method");        writer.WriteValue(model.Method);      }            if(model.IsTemplated)      {        writer.WriterPropertyName("isTemplated");        writer.WriteValue(model.IsTemplated);      }            writer.WriteEndObject();    }  }}