JSON

在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

字号+ 作者:H5之家 来源:H5之家 2017-11-20 16:04 我要评论( )

在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务端接口的耦合度。很多当今流行的RESTful API开发框架,包括Spring REST,也都默认支持HAL规范,当RESTful API被调用后,服务端就会返回ContentType为application/hal+json的JSON内容,例如:

{

"_links": {

"self": {

"href": "http://example.com/api/book/hal-cookbook"

}

},

"_embedded": {

"author": {

"_links":

"self": {

"href": "http://author-example.com"

}

},

"id": "shahadat",

"name": "Shahadat Hossain Khan"

}

},

"id": "hal-cookbook",

"name": "HAL Cookbook"

}

相对于仅返回一个id和一个name的结果而言,这样的JSON Response包含了更为丰富的信息,比如:当前请求的API地址、HAL Cookbook这本书的作者信息,以及访问作者信息的超文本链接。那么客户端在获取到这个服务端的响应后,就能很方便地将这些信息绑定在界面上,而无需通过多个API的调用来查找关联信息。另一方面,这种JSON Response中包含的超文本链接也可以是动态的,比如分页导航链接,这样客户端实现分页功能将变得非常方便。本文着重介绍在ASP.NET Core Web API中,如何为自己设计的RESTful API增加对HAL的支持。

Apworks框架

在ASP.NET Core Web API中为RESTful服务增加对HAL的支持,是通过Apworks框架以及HAL框架来完成的。这两个框架都是我自己设计开发的开源框架,前者基于Apache 2.0开源,后者基于MIT开源,因此完全可以用于商业系统开发。HAL项目为超文本应用语言(Hypertext Application Language)提供了基本的数据模型和处理逻辑,它对JSON的支持基于大名鼎鼎的Newtonsoft.Json,因此性能方面是可以保证的。简便快捷的流畅接口(Fluent Interface API)编程方式,使得构建一个完整合理的HAL对象模型变得非常容易。HAL项目的设计使用了一些对象结构化模式,有兴趣的朋友可以到Github项目主页(https://github.com/daxnet/hal)了解一下。

至于Apworks框架,它的主要功能并不是仅仅为了向ASP.NET Core Web API提供HAL的支持,它更重要的是一套基于.NET Core的微服务快速开发框架,不仅提供了面向领域驱动(DDD)的基本构造元素(聚合、实体、仓储、工厂等),而且整合了消息队列、消息派发及订阅、消息处理、查询服务、事件存储等微服务架构功能模块,并基于MongoDB、Entity Framework、RabbitMQ、PostgreSQL、SQL Server等基础服务作出了实现。目前整个框架还在开发和完善阶段,读者有兴趣也可以上Apworks Examples案例项目查看Apworks框架的案例代码,案例代码也在同步更新之中。等所有的案例代码开发完成后,我会对Apworks框架发布一个相对稳定的版本。

值得一提的是,Apworks框架中的Apworks.Integration.AspNetCore Package提供了对ASP.NET Core Web API的开发扩展,对HAL的支持也是该Package的一个部分。OK,Apworks框架和HAL框架暂时介绍这些,它们不是本文重点。接下来,让我们看看,如何快速地在ASP.NET Core Web API中实现HAL的支持。

最简单的案例

事实上,在《在ASP.NET Core中使用Apworks开发数据服务:对HAL的支持》一文中,我已经介绍了如何在Apworks快速搭建数据服务的同时,提供HAL的JSON数据格式。当时的案例是需要满足数据服务开发模式的,比如需要注入仓储实例,并且Controller需要默认提供GET、POST、PUT、DELETE的操作。这对于仅需要实现某些特定功能的通用Web API Controller而言,又显得太重了。所以,我们还是从最简单的案例开始吧。

上面是最简单的案例,我们就只用了几行代码,花了10分钟不到,就把ASP.NET Core Web API的默认项目打造成了支持HAL JSON格式的RESTful API项目。大概介绍几个要点:

由此可见,基于Apworks在ASP.NET Core Web API中为RESTful服务增加对HAL的支持还是非常方便的。在上面分页的案例中,分页查询是通过在URL中增加名为page的Query String来实现了,page这个Query String就对应Get方法中的第一个page参数。那么,如果我希望使用其它的Query String来作为分页的页码参数,又该如何做呢?

自定义分页参数

自定义分页参数方法非常简单,只需要在参数定义前加上PageNumberAttribute就行了。比如下面的Get方法:

[HttpGet]

[SupportsHal]

public IActionResult Get([PageNumber] int p = 1, int size = 5)

{

var values = new[] { "a", "b", "c", "d", "e",

"f", "g", "h", "i", "j",

"k", "l", "m", "n", "o",

"p","q", "r","s","t",

"u","v","w","x","y",

"z" };

var skip = (p - 1) * size;

var take = size;

var records = values.Length;

var pages = (records + size - 1) / size;

return Ok(new PagedResult(values.Skip(skip).Take(take), p, size, records, pages));

}

我们通过PageNumberAttribute来指定参数p为分页参数,那么,在访问特定页的数据时,就可以使用:52566/api/values?p=2这样的方式:

客户端范例

既然我们已经有了一个服务端分页的RESTful API,我们不妨快速搭建一个客户端App,来试用一下这个支持HAL JSON格式的服务端分页API是否好用。为此,我建立了一个客户端项目,开发采用Angular 4和TypeScript,主要代码如下:

// LettersResponse.ts

export class LettersResponse {

public first: string;

public prev: string;

public next: string;

public last: string;

public values: string[];

}

// my-letters-service.service.ts

@Injectable()

export class MyLettersServiceService {

constructor(private http: Http) { }

getLetters(url: string): Promise<LettersResponse> {

return this.http.get(url)

.toPromise()

.then(response => {

const json = response.json();

return {

first: json._links.first ? json._links.first.href : null,

last: json._links.last ? json._links.last.href : null,

prev: json._links.prev ? json._links.prev.href : null,

next: json._links.next ? json._links.next.href : null,

values: json._embedded.values

};

});

}

}

// app.component.ts

@Component({

selector: 'app-root',

templateUrl: './app.component.html',

styleUrls: ['./app.component.css'],

providers: [MyLettersServiceService]

})

export class AppComponent implements OnInit {

response: LettersResponse;

constructor(private service: MyLettersServiceService) {

}

ngOnInit(): void {

this.service.getLetters(environment.serviceUrl)

.then(res => this.response = res);

}

onLinkClicked(url: string): void {

console.log(url);

this.service.getLetters(url)

.then(res => this.response = res);

}

}

// app.component.html

<div *ngIf="response">

<ul>

<li *ngFor="let c of response.values">

<h2>{{c}}</h2>

</li>

</ul>

<button (click)="onLinkClicked(response.first)" [disabled]="!response.first">First Page</button>

<button (click)="onLinkClicked(response.prev)" [disabled]="!response.prev">Previous Page</button>

<button (click)="onLinkClicked(response.next)" [disabled]="!response.next">Next Page</button>

<button (click)="onLinkClicked(response.last)" [disabled]="!response.last">Last Page</button>

</div>

在命令行使用NgCLI启动客户端程序,然后访问:4200,得到的效果如下:

总结

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • netcore NetCore获取Json和Xml格式的配置信息

    netcore NetCore获取Json和Xml格式的配置信息

    2017-09-20 10:03

  • ASP.NET中使用JSON方便实现前台与后台的数据交换

    ASP.NET中使用JSON方便实现前台与后台的数据交换

    2017-09-09 14:08

  • JSON学习笔记(一)

    JSON学习笔记(一)

    2017-09-06 14:27

  • Core Json–JSON和Java

    Core Json–JSON和Java

    2017-09-03 16:00

网友点评
i