目录
介绍
背景
安装
通用服务和OData
使用MsSql.RestApi实现OData服务
单项结果
添加更多OData服务
高级OData服务
$ extend参数
$apply
消费OData服务
自定义SQL服务
JQuery DataTable API
构建微服务
兴趣点
在本文中,我将向您展示如何使用MsSql.RestApi包创建功能强大的REST API,而这仅使用几行C#代码。如果您采用这种方法,可能会为您节省大量时间。
介绍
有时,令人惊讶的是我们花了多少时间来创建各种REST API,尽管这是我们多年来一直在解决的问题。如果您是经典的后端开发人员,则可能已为不同的前端应用程序和组件创建了大量REST API。尽管在大多数情况下REST API应该是一个简单的函数,但您可能必须创建或修改多个层,创建新的Model或ViewModel类,甚至为最简单的REST API创建新的LINQ / SQL查询。奇怪的是,我们仍然没有一种技术可以让我们更轻松地创建REST API。
引用:
MsSql.RestApi是一个库,可以帮助您使用最少量的代码加速构建功能强大的REST API的过程。
在本文中,您将看到一些我认为是创建REST API的最佳和最简单的方法。MsSql.RestApi是一个免费的开源ASP.NET库,使您能够以最小的努力创建所需的几乎所有REST API。该库的主要功能包括:
- 创建强大的OData服务仅需要两行代码
- 与在服务器端模式下工作的JQuery DataTables轻松集成
- 创建执行任何T-SQL查询的API仅需的单行命令
- 使用Azure Functions构建微服务所需的最少代码量
唯一的问题是SQL Server的版本 - 您需要在SQL Server 2016(或更高版本)或Azure SQL数据库(最新版本的SQL Server)上运行数据库。MsSql.RestApi库只适用于这些版本的MsSql数据库引擎。
如果您有兴趣,请阅读以下部分,因为此方法可能会改变您创建REST API的方式。
背景想象一下经典的Web开发架构,它将前端和后端代码分开,前端代码用JavaScript编写(vanilla JavaScript,Angular,React,JQuery等),调用一些后端REST API端点。
如果您正在处理相当大的系统,则可能需要实现许多Web服务,这些Web服务使用各种标准返回有关实体/对象的信息。例如,您可能最终得到以下一组服务:
- GetCustomerByID,GetCustomerByName,GetCustomersByRegion,GetCustomersByStatus
- GetOrderByID,GetOrdersByCustomer,GetOrdersByStatus
- GetProductByID,GetProductsByType,GetProductsInPriceRange
一旦你创建了它们,当有人要求你通过添加skip=和take=参数来实现对某些端点的分页时,你很可能需要进一步扩展它,然后可能需要组合像GetCustomersByRegion,GetCustomersByStatus这样的服务按区域和/或状态来获得客户,所以您需要扩展第一个或第二个服务或创建新端点。
另一种方法是为每个概念(例如,数据库表)创建一个端点并添加你应该返回的指定参数:
- /GetCustomers?CustomerID=…&Region=…..&Status=…&OrderBy=
- /GetOrders?OrderID=…&CustomerID=…&Status=…
- /GetProducts?ProductID=…&Type=….&MinPrice=…&MaxPrice=….
在这种方法中,您可以只添加一个参数,而不是为每个新请求创建新服务。似乎很好,但仍然不完美。您最终会获得大量参数,而不是很多服务,您必须知道每个参数如何相互协作。你需要确保每一个参数组合正常工作并且没有出现意想不到的结果(例如,当客户端使用参数CustomerID=17&Status=4调用GetCustomers会发生什么,如有ID=17的customer没有状态为4?是返回空的结果还是错误?)。在这种情况下,您需要定义一些语义,因此客户端可以知道在组合参数时会发生什么(并且要注意,如果他们有机会,他们可以组合任何内容)。
在这两种情况下,您都会遇到一些定制服务的问题,这些定制服务在逻辑上不属于任何条目,如按区域划分的平均销售额或客户,其中“销售额”是从几个不同的实体派生的复杂计算。在这种情况下,您需要创建一个新的端点。
无论您选择何种方式,您最终都会得到许多端点或许多参数。一段时间后,这可能会成为一个维护地狱——你可能不知道使用了什么端点,是否有一些重复,等等。
作为后端开发人员,您可能不愿意添加新服务或参数,因为现有代码变得无法维护。作为前端开发人员,您可能会遇到这样的情况,即后端开发人员要么不创建您需要的参数,要么就需要花费大量时间来完成它。
现在,您可以考虑更改方法并尝试通用的和标准化的API。
MsSql.RestApi是一个免费的开源ASP.NET库,可以帮助您更灵活,更快速地开发可以满足各种前端请求的REST API服务。这个开源库的主要好处是:
- 您需要编写几行代码来创建REST API,以返回具有明确定义语义的任何数据。
- 内置的OData服务实现具有最重要的参数。
- 您可以轻松创建自定义服务。
- 内置对JQuery DataTables插件的支持,使您可以创建富客户端表。
如果您认为这对您的项目有用,那么让我们来看看细节。
安装为了将MsSql.RestApi库添加到项目中,您需要使用以下命令行从NuGet获取此库:
PM> Install-Package MsSql.RestApi
在使用.NET Core项目时,您需要在Startup类(ConfigureServices方法)中初始化数据访问组件,如以下示例所示:
using MsSql.RestApi;
namespace MyApp {
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSqlClient(Configuration["ConnectionStrings:MyConnection"]);
}
}
}
此示例中的假设是您的连接字符串存储在key为MyConnection的appsettings.config文件中。您可以修改字符串内容并从任何其他位置获取连接字符串。
从技术上讲,就是这样。您不需要创建一些模型和类。此代码将配置所有内容。
现在让我们看看如何使用这个包创建一些REST API。
通用服务和OData您需要创建的大多数REST API服务都具有通用功能。您需要提供有关某个实体或表的数据,并允许客户端过滤结果集,指定应显示的属性,排序结果,实现一些分页,并对数据库中的大多数表重复此操作。如果您不想定义自己的协议或规范,可以使用一些现有的协议,如OAsis OData或Facebook GraphQL。
OData和GraphQL都允许您通过HTTP指定某种“查询”,您可以告诉后端服务应该返回什么。OData和GraphQL之间的差异如下例所示:
OData - 按参数查询
GraphQL - 通过示例查询
http://..../human?
$select=name,height
$filter=id eq 1000
$orderby=
$take=10
$skip=30
{
human(id: "1000") {
name
height
}
}
在OData中,您可以通过URL参数以类似SQL的方式指定要检索的记录。还有$select,您可以指定希望获取什么样的属性参数,$filter参数,你可以指定一个返回的记录必须满足的条件,$orderby它定义了结果的排序方式,和$skip/ $take可用于分页。在GraphQL中,您可以定义要获取的文档的结构,并将一些条件注入到文档正文中(请参阅id:1000的human 对象),后端服务将返回匹配的数据。
虽然OData是一个较旧的协议,但我仍然更喜欢这种方法胜过GraphQL。我喜欢前端代码能够使用富查询/表达式规范精确地指定所需的内容,如下面的示例所示:
http://......./StockItems
$select=UnitPrice,TaxRate,ColorName,Size&
$filter=UnitPrice gt 10 and UnitPrice lt 20&
$orderBy=UnitPrice asc&
$skip=20&
$take=10
此API调用将获取关于单价在10到20之间的股票项目的信息,按单价订购,并返回列的单价、税率、颜色名称和大小。此外,它将对结果进行分页并返回第三个10项页。
理论上,您可以使用GraphQL执行相同的操作,https://www.howtographql.com/graphql-js/8-filtering-pagination-and-sorting/,但如果您想在REST上创建类似SQL语言的内容,那么OData方法会稍微容易一些。我想声明什么应该返回结果看起来像SQL语言的参数。现在,当我们知道OData是什么时,让我们看看如何实现该服务。
使用MsSql.RestApi实现OData服务使用MsSql.RestApi库您可以轻松实现OData服务以公开数据库表数据。想象一下,我们有一个经典的数据库驱动开发模型,其中表已经在数据库中(理想的数据库优先开发模型——但如果你使用一些代码优先生成器生成表,它也会工作),我们需要为它们实现REST API。
让我们假设我们有一个包含列“ name,surname,address,town” 的People表,我们想要创建REST API,通过此表上的URL启用完整查询功能。我们需要像以下代码示例中的那样创建ASP.NET控制器:
using Belgrade.SqlClient;
using System.Threading.Tasks;
namespace WideWorldImporters.Controllers
{
public class ODataController : Controller
{
ICommand db = null;
public ODataController(ICommand db)
{
this.db = db;
}
public async Task People()
{
TableSpec spec =
new TableSpec(schema: "dbo", table: "People",
columns: "name,surname,address,town");
await this
.OData(spec)
.Process(this.db);
}
}
}
安装MsSql.RestApi将会注入服务ICommand,IQueryPipe以及IQueryMaper,这些服务可以用于查询数据库。此库使用Belgrade SqlClient库访问SQL Server或Azure SQL数据库中的数据。在上面的例子中,我使用的命令服务和标准依赖注入初始化控制器中的服务,然后我创建People方法,其处理OData的请求(即,读取$select,$filter,$orderby参数),基于输入参数生成SQL查询,对数据库执行该SQL查询,并将结果返回给客户端。我唯一需要的是表结构的规范,包括数据库模式,表名和列集合。OData和Process 方法将解析来自HTTP请求的传入参数,将它们转换为SQL查询并执行它。
当您创建此操作时,您将能够获取有关使用OData查询的人员的信息:
http://..../OData/People?$select=name, surname&$orderby=name&$skip=30&$take=10
这个单独的REST端点在action方法中包含两个语句,这将使您的前端代码能够使用各种条件获取数据,而无需为其他功能创建新的端点、新SQL或LINQ查询。使用两个C#语句,您将获得访问数据库表的全功能服务。
单项结果您需要实现的最常见情况之一是通过id(主键)从表返回单个结果。OData使您能够通过在实体名称后指定键值来执行此操作:
http://..../OData/People(17)
为了通过key实现获取单个结果,我们首先需要在MVC框架中注册一个路由,其将这种请求URL模式映射到OData控制器 - 类似于:
app.UseMvc(routes =>
{
routes.MapRoute(
"odata-single",
"OData/{action}({id})",
new { controller = "OData" }
);
});
放置在大括号( )中的任何URL标记都将作为参数提供给操作方法。现在我们需要向服务OData请求的控制器操作方法添加一个可选的id参数,并将此id提供给OData方法:
public async Task People(int? id)
{
TableSpec spec =
new TableSpec(schema: "dbo", table: "People",
columns: "name,surname,address,town",
primaryKey: "PersonID");
await this
.OData(spec, id: id)
.Process(this.db);
}
请注意表规范中的一个额外更改。由于OData方法将按主键过滤记录,因此我们需要指定表中的哪一列是主键。MsSql.RestApi将使用此列来过滤结果。
添加更多OData服务如果您需要创建其他表的REST端点,你可以用相同的模式创造新的控制器方法,这些方法提供数据给StockGroups,StockItems,Orders,Invoices,等。
public async Task StockGroups()
{
var spec = new TableSpec("WebApi","StockGroups", "StockGroupID,StockGroupName");
await this.OData(spec).Process(this.db);
}
public async Task StockItems()
{
var spec = new TableSpec("WebApi", "StockItems",
"StockItemID,StockItemName,SupplierName, SupplierReference,ColorName,OuterPackage,UnitPackage,Brand,Size,LeadTimeDays,QuantityPerOuter, IsChillerStock,Barcode,TaxRate,UnitPrice,RecommendedRetailPrice,TypicalWeightPerUnit,
MarketingComments,InternalComments,CustomFields,QuantityOnHand,BinLocation, LastStocktakeQuantity,LastCostPrice,ReorderLevel,TargetStockLevel,SupplierID,ColorID,
UnitPackageID,OuterPackageID");
await this.OData(spec).Process(this.db);
}
public async Task Invoices()
{
var spec = new TableSpec("WebApi", "Invoices", "InvoiceID,InvoiceDate, CustomerPurchaseOrderNumber,IsCreditNote,TotalDryItems,TotalChillerItems,DeliveryRun, RunPosition,ReturnedDeliveryData,ConfirmedDeliveryTime,ConfirmedReceivedBy,CustomerName, SalesPersonName,ContactName,ContactPhone,ContactEmail,SalesPersonEmail,DeliveryMethodName, CustomerID,OrderID,DeliveryMethodID,ContactPersonID,AccountsPersonID,SalespersonPersonID,
PackedByPersonID");
await this.OData(spec).Process(this.db);
}
public async Task SalesOrders()
{
var spec = new TableSpec(schema: "WebApi", table: "SalesOrders",
columns: "OrderID,OrderDate,CustomerPurchaseOrderNumber,ExpectedDeliveryDate,
PickingCompletedWhen,CustomerID,CustomerName,PhoneNumber,FaxNumber,WebsiteURL,
DeliveryLocation,SalesPerson,SalesPersonPhone,SalesPersonEmail");
await this.OData(spec).Process(this.db);
}
如您所见,您需要使用两个语句重复一个小方法——一个用于定义表结构,另一个用于处理OData请求。没有数据访问,没有查询(SQL或LINQ),没有多余的测试。每个服务的两行代码将为您提供前端代码所需的大部分功能。
由于不同服务的代码非常相似,因此您可以为数据库中的所有表轻松地生成大量的OData服务。您只需定义应作为OData服务公开的模式,表名和列名集合。
我正在使用T4模板为更大的数据库生成代码:
public partial class ODataController: Controller
{
ICommand db = null;
public ODataController(ICommand sqlCommandService)
{
this.db = sqlCommandService;
}
[HttpGet]
public async Task ()
{
var spec = new TableSpec("","", "");
await this.OData(spec).Process(this.db);
}
}
在此模板中,我假设这config是一个对象数组,其属性Schema表示放置表的数据库模式,包含表名称的表,以及包含以逗号分隔的表列的列表集合。
您需要做的唯一事情是使用某些配置数组中的列定义表的名称和模式,此T4模板将自动生成所有必需的OData端点。这样,我们可以轻松地为非常复杂的数据库创建大量的OData服务。
高级OData服务标准OData参数可能使您能够实现前端代码所需的80%的请求。可以使用$select, $filter, $orderby,$skip/$take参数和丰富的表达式成功描述大多数请求。但是,在某些情况下,您需要更多,例如:
- 您可能需要在相关表中包含一些其他数据。
- 如果要创建报告或图表,则可能需要创建分组,聚合和计算结果的分析查询。
幸运的是,OData服务通过使用两个参数提供这些功能,$extend和$apply。
$ extend参数默认情况下,OData服务将单个表公开为平面数据,但在某些情况下,您需要获取一些其他信息。例如,如果您要获取客户数据,则可能需要获取所有客户发票和客户订单。从理论上讲,您可以通过ID获取客户,然后发送单独的请求,按客户ID过滤订单和发票; 但是,从同一个调用中获取所有内容会更好(类似于C#/ LINQ中的Include()方法)。OData使您可以使用$expand参数指定应包含在获取的数据中的相关数据。 $expand参数是应与主实体一起获取的相关实体的列表,如以下示例所示:
- http://localhost:53026/Customers?$filter=CustomerId eq 1&$expand=Orders,Invoices
此OData查询将使用ID 1得到customer并通过包含属于customer的所有Orders和Invoices扩展结果。
为了实现OData服务可以使用MsSql.RestApi库扩展相关信息,您需要使用AddRelatedTable方法指定关系,此方法指定了主实体(Customer)拥有关联的Orders和Invoices表:
[HttpGet("Customers")]
public async Task Customers()
{
var spec = new TableSpec(schema: "Application", table: "People", columns: "PersonID,
FullName,PhoneNumber,FaxNumber,EmailAddress,ValidTo")
.AddRelatedTable("Orders", "Sales", "Orders", "Application.People.PersonID =
Sales.Orders.CustomerID", "OrderID,OrderDate,ExpectedDeliveryDate,Comments")
.AddRelatedTable("Invoices", "Sales", "Invoices", "Application.People.PersonID =
Sales.Invoices.CustomerID", "InvoiceID,InvoiceDate,IsCreditNote,Comments");
await this
.OData(spec)
.Process(this.db);
}
您需要提供的其他信息是用于将主要实体与相关实体联接的查询条件。这是您需要指定的所有内容,以便能够应用$extend参数。
$extend参数还允许您针对包含的子实体创建高级查询。例如,通过为每个扩展实体指定$top参数,您可以在结果集中为每个customer设置前两个Orders和前三个Invoices:
http://localhost:53026/Customers(17)?$expand=Orders($top=2),Invoices($top=3)
您可以在$filter参数中添加丰富的表达式,以定义非常具体的条件来返回相关实体。
http://localhost:53026/Customers(17)?$expand=Orders($orderby=OrderDate asc,
$filter=OrderID lt 12000 or (month(OrderDate) gt 4),$top=2)
$extend 参数可能是一个非常有用的工具,使您可以使用丰富的语法来获取相关实体,以指定实体的属性。
$apply您可能需要的另一个功能是分组和聚合。如果你有像饼图这样的报告,你可能需要获得一些汇总和预先计算的数据。
OData服务有$apply参数,您可以在其中指定不同的聚合,例如:
- $apply=aggregate(PersonID with min as Minimum)
- $apply=groupby((FullName), aggregate(PersonID with sum as Total))
- $apply=groupby((PhoneNumber), aggregate(PersonID with sum as Total), aggregate(PersonID with min as Minimum))
- $apply=groupby((PhoneNumber, FaxNumber), aggregate(PersonID with sum as Total), aggregate(PersonID with min as Minimum))
第一个聚合从集合中返回最小PersonID值。 “作为 Minimum” 的部分指定应返回的属性的名称。第二个通过FullName分组返回PersonID的总和。等效的SQL查询将是:
SELECT sum(PersonID) as Total FROM People GROUP BY FullName
您可以通过执行经典SQL分组查询完成大部分的事情并获取各种报告的数据。
消费OData服务拥有良好的标准化OData服务是好的,但如何使用它们?由于OData服务是普通的Http服务,您只需在URL中添加参数即可获得所需的任何内容。在下面的示例中显示了一个JQuery ajax调用,它将请求发送到OData Customers端点并将结果传递给回调方法
$.ajax('/odata/Customers?$apply=groupby((PostalCity),aggregate(CustomerID with sum as Total))&$orderby=CustomerID with sum desc&$top=5',
{ dataType: 'json' })
.done(result => {
});
由于OData是标准化协议,您可能会发现许多客户端API库,如 o.js 或Apache Olingo,使您能够使用其接口调用OData服务。使用o.js库(我用来访问OData服务的库)调用OData服务的代码示例如下所示:
o('http://services.odata.org/V4/OData/OData.svc/Products').take(5).skip(2).get( data => {
console.log(data); //An array of 5 products skipped by 2
}, status => {
console.error(status); // error with status
});
另一种选择是创建OData服务的swagger / OpenAPI规范,并让swagger生成各种客户端。建立swagger规范应该是一件容易的事,因为每个端点有6个固定参数。如果你有大量的端点,你总是可以使用类似于我用来为不同实体大量生成OData方法的T4模板。
swagger: "2.0"
info:
description: "OData service exposing information from WideWorldImporters database."
version: "1.0.0"
title: "Wide World Importers"
termsOfService: "http://swagger.io/terms/"
contact:
email: "jovan@acme.com"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "localhost:64958"
basePath: "/OData"
tags:
- name: ""
description: "Information about "
schemes:
- "https"
- "http"
paths:
/:
get:
tags:
- ""
summary: "Find information about "
description: "Multiple status values can be provided with comma separated strings"
operationId: ""
produces:
- "application/json"
parameters:
- name: "$select"
in: "query"
description: "Selecting the properties that should be returned by service"
required: false
type: "array"
items:
type: "string"
enum:
- ""
collectionFormat: "multi"
- name: "$orderby"
in: "query"
description: "Ordering results by properties"
required: false
type: "array"
items:
type: "string"
enum:
- ""
- " asc"
- " desc"
collectionFormat: "multi"
- name: "$top"
in: "query"
description: "Selecting the properties that should be returned by service"
required: false
type: "integer"
- name: "$skip"
in: "query"
description: "Selecting the properties that should be returned by service"
required: false
type: "integer"
- name: "$apply"
in: "query"
description: "aggregation function that should be applied on the results"
required: false
type: "string"
- name: "$filter"
in: "query"
description: "Condition that returned items must satisfy"
required: false
type: "string"
responses:
200:
description: "Provided information about ."
400:
description: "The OData request is not valid"
500:
description: "Cannot process request"
与之前生成操作的模板代码类似,假设config是一个包含每个端点定义的表、anc列的数组。一旦你的模板为每个端点生成规范,你就可以生成客户端库,其可以通过swagger工具,像https://editor.swagger.io/,访问你的服务。
自定义SQL服务如果你真的需要一些自定义的API,不能使用标准的$filter,$orderby,$select参数实现,甚至是自定义的$apply和$extend参数也无法实现,你总是有一个选项来创建自己的定制服务。
如果您有这样的特定查询或报告,你可能需要创建从数据库中获取所需数据的SQL查询。MsSql.RestApi使您能够轻松提供应执行的SQL查询。查询的结果可以很容易地返回给调用者(例如,浏览器中的JavaScript代码)。
以下示例说明如何实现执行复杂T-SQL查询的REST API,并将查询结果直接流式传输到response正文中:
public async Task Report1()
{
await this.db.Sql(
@"select Color as [key], AVG( Price ) as value
from Product
group by Color
having AVG( Price ) > 20
FOR JSON PATH").Stream(Response.Body);
}
请注意FOR JSON子句,它指示SQL数据库引擎将信息作为JSON文档而不是表格内容返回。这对于开发REST API很有用,因为您将获取的内容应该发送到浏览器。它使您能够直接将SQL Server的JSON响应放到Web客户端,而无需任何模型或数据传输对象(DTO),这些对象只是临时存储数据,之后Newtonsoft JSON.Net将DTO对象序列化为JSON响应,并将返回给浏览器。Command对象使您能够提供将要执行的任何T-SQL查询,并将结果流式传输到响应正文中。
请注意我将SQL别名作为[key]和value的部分。如果没有这些别名,查询将返回一个数组,其属性名称为列,如Color。但是,我正在使用一些前端组件(如D3图表)来显示数据,我需要处理这些组件需要某些特定格式的事实——在这种情况下,D#图表需要一个包含属性键和值的数组。使用SQL别名,我可以轻松生成我的前端组件所需的输出格式。
下图显示了使用D3库创建的饼图,该库使用自定义SQL端点的响应填充,该响应返回键和值属性。
如果您是SQL专家,或者您的团队中有人能够编写复杂的查询,那么您可以只接受任何查询、放置FOR JSON子句并将结果刷新到客户端。我的偏好是创建T-SQL存储过程,只是将过程名称放在查询中而不混合使用SQL和C#代码——但您可以使用这两种方法。
以下示例显示如何通过调用返回JSON的存储过程来创建使用参数中定义的条件返回某些库存项的API:
public async Task Search(string name, string tag, double? minPrice, double? maxPrice,
int? stockItemGroup, int top)
{
await this.db
.Sql("EXEC WebApi.SearchForStockItems @Name, @Tag, @MinPrice,
@MaxPrice, @StockGroupID, @MaximumRowsToReturn")
.Param("Name", DbType.String, name)
.Param("Tag", DbType.String, tag)
.Param("MinPrice", DbType.Decimal, minPrice)
.Param("MaxPrice", DbType.Decimal, maxPrice)
.Param("StockGroupID", DbType.Int32, stockItemGroup)
.Param("MaximumRowsToReturn", DbType.Int32, 20)
.Stream(Response.Body);
}
JQuery DataTable API
几乎每个应用程序都需要提供的另一个重要功能是在表中显示数据。这包括过滤表格中的数据,分页,按列排序结果,更改每页显示的项目数。
我知道在表格上实现丰富的客户端功能的最好的(免费和开源)组件之一是JQuery DataTables插件。此插件可以应用于普通的HTML表格,以便为表格中的分页,排序和过滤行添加所有必要的功能。如果你使用DataTables插件,普通的HTML表就像这样:
DataTables有两种工作模式:
- 客户端模式,其中所有行都预先加载到表中(例如,在中),DataTables在客户端JavaScript代码中进行所有处理。对于没有大量数据(最多几百行)的表,这是一个不错的选择。
- 服务器端模式,其中只返回当前应在表中显示的行。每当用户更改表中的某个状态时(转到下一页,更改应对结果排序的列)DataTables将向某个API发送新请求并获取应显示的新结果而不是当前结果进行展示。当您有大量可能显示的记录时使用此模式,并且在浏览器中加载所有内容会很慢且占用大量内存。
实现服务器端模式可能很棘手,因为您需要具有通过某些AJAX请求理解DataTables发送的参数的API ,并以DataTables期望的格式返回结果。
好消息是MsSql.RestApi了解DataTables协议。如果你想创建一个API处理一个从Application.People表获取数据的 DataTables请求,并显示出列FullName,EmailAddress,PhoneNumber,和FaxNumber,你需要在一些控制器添加了以下操作:
public async Task Table()
{
var spec = new TableSpec(schema: "Application", table: "People",
columns: "FullName,EmailAddress,PhoneNumber,FaxNumber");
await this
.Table(spec)
.Process(this.db);
}
定义表规范后(仅显示要显示的列的规范——您不需要包括所有表数据)。Table()API将使用此元数据来处理DataTables请求。
现在,您只需添加一个普通的空表,该表将成为显示页面中人员的模板:
Name
Email
Phone
Fax
为了将端点/ People / Table中的数据加载到空HTML表中,您应该添加以下代码,用id“ people” 初始化HTML表,设置将提供数据的服务器端API,并定义将显示哪些列:
$(() => {
$("table#people")
.DataTable({
"ajax": "/People/Table",
"serverSide": true,
"columns": [
{ data: "FullName" },
{ data: "EmailAddress", defaultContent: "" },
{ data: "PhoneNumber", defaultContent: "" },
{ data: "FaxNumber", defaultContent: "" }
]
});
});
结果,您将获得一个功能齐全的表,其只加载需要显示的记录而无需任何其他服务器端代码。
每当用户在表中执行某些操作时,DataTables将使用描述新状态的参数向URL / People / Table发送新的AJAX请求,并且MsSql.RestApi仅返回应显示的数据。
构建微服务目前的趋势是将您的应用程序分解为可以独立扩展的较小的可管理单元。与其他功能完全分离的独立功能单元称为微服务。
Azure Functions是用于放置将在无服务器模式下调用的微服务代码的地方,而不是配置指定的服务器。
在撰写本文时,Azure Functions没有与关系数据库的内置集成——它们通常以无SQL服务为目标。如果您想利用无服务器计算的优势并将关系引擎用作数据存储,这可能会变成复杂的项目。您需要将您的实体框架或其他数据访问,将您的域模型和所有模型类放在函数中,并为简单函数添加大量代码。
MsSql.RestApi 在这种情况下可以帮助您,并使您能够创建无服务器Azure Functions,使用最少量的代码访问您的数据库代码。
如果您喜欢使用OData规范的方法,这可能非常适合Azure Functions。有了MsSql.RestApi,您可以使用上面描述的两行代码来创建您的服务:
[FunctionName("SalesOrders")]
public static async Task SalesOrders(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, ILogger log)
{
TableSpec spec = new TableSpec(schema: "WebApi", table: "SalesOrders",
columns: "OrderID,OrderDate,CustomerPurchaseOrderNumber,ExpectedDeliveryDate,
PickingCompletedWhen,CustomerID,CustomerName,PhoneNumber,FaxNumber,WebsiteURL,
DeliveryLocation,SalesPerson,SalesPersonPhone,SalesPersonEmail");
return await req.OData(spec).GetResult(Environment.GetEnvironmentVariable("SqlDb"));
}
这可能是您的微服务的完美选择。您可以实现强大的OData微服务,其为您提供所有必要的功能,可以对SalesOrder表中的行进行过滤,排序,分组,另一方面,您只需编写两行代码。没有大型模型,复杂的数据访问框架——只需两行代码,您需要你所想的一切。此代码仅处理数据访问逻辑。
MsSql.RestApi只处理数据访问逻辑。安全性,CORS和可伸缩性由Azure Function框架处理(请参阅函数定义中的授权级别属性)。通过这种方式,您可以从两个方面获得最佳效果—— Azure Function无服务器可伸缩性和可管理性,以及关系引擎提供的仅几行代码的强大语言。
兴趣点正如您在本文中所看到的,如果您使用MsSql.RestApi库,使用SQL Server关系数据库可能很容易。
MsSql.RestApi是一个免费的开源库,极大地简化了从ASP.NET Server数据库中读取数据的ASP.NET REST API的实现。我的观点是,这是为现有数据库快速开发REST API的完美选择。
它需要的代码真的很少,你应该尝试一下。如果您发现它不适合您的需要,那么您总是可以使用一些其他技术删除这个最小量的代码重新实现。
如果您知道使用更少的代码为SQL Server数据库生成更强大的REST API的更好方法,请告诉我?。否则,请尝试使用此库并检查它是否对您有所帮助。
该库用于开发多个SQL Server gitHub示例应用程序,包括Wide World Importers示例Web应用程序和用于为SQL Server 2016构建的官方示例数据库的Wide World Importers Azure Function Microservice。如果您想查看更多代码,可以使用从SQL Server GitHub存储库下载示例项目并检查代码。
原文地址:https://www.codeproject.com/Articles/1265734/MsSql-RestApi-The-Easiest-Way-to-Build-the-ASP-NET