天天看点

AutoWrapper:通过有意义的响应来美化您的ASP.NET Core API介绍 默认的ASP.NET Core API响应AutoWrapper.Core的救援TL,DR 给我看代码定义自己的自定义消息定义自己的Api异常选项支持日志支持Swagger总结参考文献

目录

介绍 

默认的ASP.NET Core API响应

AutoWrapper.Core的救援

主要特点:

TL,DR 给我看代码

定义自己的自定义消息

定义自己的Api异常

选项

版本1.0.0

1.1.0版添加

支持日志

支持Swagger

总结

GitHub Repo

参考文献

介绍 

在为“实际”应用程序项目构建API时,大多数开发人员忘记了向其消费者提供有意义的响应的重要性。发生这种情况有几个原因;可能是他们的开发时间有限,他们没有标准的HTTP 响应格式,或者只是只要API 将所需的数据返回给消费者,他们就不在乎响应。嗯,API不仅要通过 HTTP传递JSON ,还应如何向使用它的开发人员提出有意义的响应。

就像有人曾经说过的…

“一个好的API设计是使用它的开发人员的UX。”

作为重视消费者的API 开发者,我们希望对他们做出有意义且一致的API响应。

ASP.NET Core 使我们能够快速创建REST API。但是,对于开箱即用的成功请求和错误,它们不会提供一致的响应。如果你对API采用RESTful方式,那么你将被使用HTTP动词,如GET、POST、PUT和DELETE。根据您的方法/操作的设计方式,每个操作都可能返回不同的类型。你的POST、PUT和DELETE终端可能会返回一个数据,或者根本没有。您的GET终端可能返回一个 string、 List<T>、某种类型的IEnumerable ,甚至是一个object。另一方面,如果您的API 抛出错误,它将返回一个object 或更糟糕的指出错误原因的 HTML 字符串。所有这些响应之间的差异使得使用API​​变得很困难,因为使用者需要了解每种情况下返回的数据的类型和结构。客户代码和服务代码都变得难以管理。

去年,我创建了两个用于管理异常和响应一致性的Nuget 包,使用一个自定义的用于restful API 的object包装器。

令我惊讶的是,这两个软件包现在都有数百次下载,并且被Blazor Boilerplate等其他开源项目使用。

虽然我认为这两个软件包都成功,但它们仍然存在一些故障,因此我决定创建一个新软件包来重构代码库,应用错误修复并为其添加新功能。

在本文中,我们将研究如何使用AutoWrapper来美化我们的ASP.NET Core  API响应。

默认的ASP.NET Core API响应

当您创建新的ASP.NET Core API模板时,Visual Studio将构建所有必需的文件和依赖项,以帮助您开始构建RESTful API。生成的模板包含一个“WeatherForecastController”,用于使用静态数据模拟简单的GET请求,如以下代码所示:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}
           

 运行应用程序时,将以JSON格式显示以下输出:

[
    {
        "date": "2019-09-16T13:08:39.5994786-05:00",
        "temperatureC": -8,
        "temperatureF": 18,
        "summary": "Bracing"
    },
    {
        "date": "2019-09-17T13:08:39.5995153-05:00",
        "temperatureC": 54,
        "temperatureF": 129,
        "summary": "Cool"
    },
    {
        "date": "2019-09-18T13:08:39.5995162-05:00",
        "temperatureC": 33,
        "temperatureF": 91,
        "summary": "Bracing"
    },
    {
        "date": "2019-09-19T13:08:39.5995166-05:00",
        "temperatureC": 38,
        "temperatureF": 100,
        "summary": "Balmy"
    },
    {
        "date": "2019-09-20T13:08:39.599517-05:00",
        "temperatureC": -3,
        "temperatureF": 27,
        "summary": "Sweltering"
    }
]
           

太好了,其API的工作与我们期望的一样,只是它没有给我们带来有意义的回应。我们知道数据是响应中非常重要的部分,但是,随便吐出数据是因为JSON响应实际上并没有太大帮助,尤其是当每个请求之间发生意外行为时。

例如,以下代码模拟了您的代码中可能发生的意外错误:

//This is just an idiot example to trigger an error
int num = Convert.ToInt32("a");
           

上面的代码尝试将包含非数字值的string转换为integer类型,这将在运行时导致错误。响应输出看起来像这样:

AutoWrapper:通过有意义的响应来美化您的ASP.NET Core API介绍 默认的ASP.NET Core API响应AutoWrapper.Core的救援TL,DR 给我看代码定义自己的自定义消息定义自己的Api异常选项支持日志支持Swagger总结参考文献

图1:未处理的异常

你能想象那些使用你的API从响应中看到这种格式的开发人员的失望吗?至少堆栈跟踪信息很有用,因为它可以使您了解错误的原因,但是应该进行处理,并且永远不要让您的API使用者看到该信息有安全隐患。堆栈跟踪信息在开发阶段和调试过程中绝对有帮助。在生产中,应处理此类详细错误,将其记录在某处进行分析,然后将有意义的响应返回给消费者。

另一种情况是,当您尝试访问 不存在的API终端时,除了著名的404 (Not Found) Http状态代码以外,没有任何回应。

使用REST API时,重要的是处理异常并为API处理的所有请求返回一致的响应,无论成功或失败。这使得使用API​​变得容易得多,而无需在客户端上使用复杂的代码。

AutoWrapper.Core的救援

AutoWrapper 会处理传入的  HTTP 请求,并通过为成功和错误结果提供一致的响应格式来自动为您包装响应。目的是让您专注于特定于业务的需求,并由包装程序处理HTTP 响应。想象一下在执行HTTP 响应标准时,您在开发API上节省的时间。

AutoWrapper 是基于VMD.RESTApiResponseWrapper.Core的项目分支  ,旨在支持.NET Core 3.x及更高版本。对该程序包的实现进行了重构,以提供更方便的方式来使用中间件,从而增加了灵活性。

主要特点:

  • 异常处理
  • ModelState验证错误处理(同时支持数据注释和FluentValidation)
  • 可配置的API异常
  • 结果和错误的一致响应格式
  • 详细的结果回复
  • 详细的错误响应
  • 可配置的HTTP状态码和消息
  • 添加对Swagger的支持
  • 添加对请求、响应和异常的日志支持
  • 在中间件中添加选项以设置ApiVersion和IsDebug属性

TL,DR 给我看代码

仅需几步,您就可以将API Controller转换为返回一些有意义的响应,而无需付出很多开发工作。您要做的就是:

1. 从NuGet或通过CLI 下载并安装最新版本的AutoWrapper.Core:

PM> Install-Package AutoWrapper.Core -Version 1.0.1-rc
           

引用:

注意:目前,这是一个预发行版本,一旦.NET Core 3发布,它将正式发行。

2.在Startup.cs中声明以下名称空间 

using AutoWrapper;
           

3.在UseRouting()中间件“之前”的Startup.cs的Configure()方法中注册下面的中间件:

app.UseApiResponseAndExceptionWrapper();
           

默认的API版本格式设置为“ 1.0.0.0”。如果希望为API指定其他版本格式,则可以执行以下操作:

app.UseApiResponseAndExceptionWrapper(new ApiResponseOptions { ApiVersion = "2.0" });
           

很简单!现在,尝试再次构建并运行ASP.NET Core API默认应用程序。根据我们的示例,以下是“WeatherForecastController” API 响应的样子:

{
    "message": "Request successful.",
    "isError": false,
    "result": [
        {
            "date": "2019-09-16T23:37:51.5544349-05:00",
            "temperatureC": 21,
            "temperatureF": 69,
            "summary": "Mild"
        },
        {
            "date": "2019-09-17T23:37:51.554466-05:00",
            "temperatureC": 28,
            "temperatureF": 82,
            "summary": "Cool"
        },
        {
            "date": "2019-09-18T23:37:51.554467-05:00",
            "temperatureC": 21,
            "temperatureF": 69,
            "summary": "Sweltering"
        },
        {
            "date": "2019-09-19T23:37:51.5544676-05:00",
            "temperatureC": 53,
            "temperatureF": 127,
            "summary": "Chilly"
        },
        {
            "date": "2019-09-20T23:37:51.5544681-05:00",
            "temperatureC": 22,
            "temperatureF": 71,
            "summary": "Bracing"
        }
    ]
}
           

如果您注意到了,那么输出现在在响应中包含一些属性,例如message,isError以及在result属性中包含的实际数据。

关于AutoWrapper的另一个好处是,日志记录已经预先配置。默认情况下,.NET Core应用程序具有内置的日志记录机制,并且包装程序已拦截的所有请求和响应都将被自动记录。对于此示例,它将在Visual Studio控制台窗口中显示如下内容:

AutoWrapper:通过有意义的响应来美化您的ASP.NET Core API介绍 默认的ASP.NET Core API响应AutoWrapper.Core的救援TL,DR 给我看代码定义自己的自定义消息定义自己的Api异常选项支持日志支持Swagger总结参考文献

图2:Visual Studio控制台日志

.NET Core支持与各种内置和第三方日志记录提供程序一起使用的日志记录API。根据您使用哪种受支持的.NET Core日志记录提供程序以及如何配置记录数据的位置(例如,文本文件,Cloud等),AutoWrapper会自动为您写入日志。 

这是当您尝试指向不存在的URL时的另一个输出示例:

{
    "isError": true,
    "responseException": {
        "exceptionMessage": "Request not found. The specified uri does not exist.",
        "details": null,
        "referenceErrorCode": null,
        "referenceDocumentLink": null,
        "validationErrors": null
    }
}
           

现在注意到响应对象是如何更改的。statusCode自动设置为404。当已经发生任何意外错误或异常时,result属性将自动省略,而显示responseException属性以显示错误消息和额外信息。

请记住,任何错误或异常也将被记录。例如,如果我们再次运行以下代码:

//This is just an idiot example to trigger an error
int num = Convert.ToInt32("a");
           

现在它将给您以下响应:

{
    "isError": true,
    "responseException": {
        "exceptionMessage": "Unhandled Exception occured. Unable to process the request.",
        "details": null,
        "referenceErrorCode": null,
        "referenceDocumentLink": null,
        "validationErrors": null
    }
}
           

控制台窗口将显示以下内容:

AutoWrapper:通过有意义的响应来美化您的ASP.NET Core API介绍 默认的ASP.NET Core API响应AutoWrapper.Core的救援TL,DR 给我看代码定义自己的自定义消息定义自己的Api异常选项支持日志支持Swagger总结参考文献

图3:Visual Studio控制台日志

默认情况下,AutoWrapper禁止显示堆栈跟踪信息。如果要在开发阶段从响应中查看错误的实际详细信息,只需将AutoWrapperOptions IsDebug设置为true:

app.UseApiResponseAndExceptionWrapper( new AutoWrapperOptions { IsDebug = true });
           

现在,当您再次运行该应用程序以触发异常时,它将显示如下内容:

{
    "isError": true,
    "responseException": {
        "exceptionMessage": " Input string was not in a correct format.",
        "details": "   at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type)\r\n   at System.Number.ParseInt32(ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)\r\n   at System.Convert.ToInt32(String value)\r\n   at AutoWrapperDemo.Controllers.WeatherForecastController.Get() in . . . . . . .,
        "referenceErrorCode": null,
        "referenceDocumentLink": null,
        "validationErrors": null
    }
}
           

注意,现在显示了真正的异常消息及其详细信息。

定义自己的自定义消息

要在响应中显示自定义消息,请使用AutoWrapper.Wrappers命名空间中的ApiResponse对象。例如,如果要在成功POST完成后显示一条消息,则可以执行以下操作:

[HttpPost]
public async Task<ApiResponse> Post([FromBody]CreateBandDTO band)
{
    //Call a method to add a new record to the database
    try
    {
        var result = await SampleData.AddNew(band);
        return new ApiResponse("New record has been created to the database", result, 201);
    }
    catch (Exception ex)
    {
        //TO DO: Log ex
        throw;
    }
}
           

成功运行代码将为您提供以下结果:

{
    "message": "New record has been created to the database",
    "isError": false,
    "result": 100
}
           

ApiResponse对象具有可以设置的以下参数:

ApiResponse(string message, object result = null, int statusCode = 200, string apiVersion = "1.0.0.0")
           

定义自己的Api异常

AutoWrapper还提供了可用于定义自己的异常的ApiException对象。例如,如果您想抛出自己的异常消息,则可以简单地执行以下操作:

用于捕获ModelState验证错误

throw new ApiException(ModelState.AllErrors());
           

用于抛出您自己的异常消息

throw new ApiException($"Record with id: {id} does not exist.", 400);
           

例如,让我们使用ModelState验证来修改POST方法:

[HttpPost]
public async Task<ApiResponse> Post([FromBody]CreateBandDTO band)
{
    if (ModelState.IsValid)
    {
        //Call a method to add a new record to the database
        try
        {
            var result = await SampleData.AddNew(band);
            return new ApiResponse("New record has been created to the database", result, 201);
        }
        catch (Exception ex)
        {
            //TO DO: Log ex
            throw;
        }
    }
    else
        throw new ApiException(ModelState.AllErrors());
}
           

验证失败时,运行代码将导致如下所示:

{
    "isError": true,
    "responseException": {
        "exceptionMessage": "Request responded with validation error(s). Please correct the specified validation errors and try again.",
        "details": null,
        "referenceErrorCode": null,
        "referenceDocumentLink": null,
        "validationErrors": [
            {
                "field": "Name",
                "message": "The Name field is required."
            }
        ]
    }
}
           

查看如何使用模型中的违规字段自动填充validationErrors属性。

ApiException对象包含以下三个重载constructors,可用于定义异常:

ApiException(string message, int statusCode = 500, string errorCode = "", string refLink = "")
ApiException(IEnumerable<ValidationError> errors, int statusCode = 400)
ApiException(System.Exception ex, int statusCode = 500)
           

选项

以下属性是可以设置的选项:

版本1.0.0

  • ApiVersion
  • ShowApiVersion
  • ShowStatusCode
  • IsDebug

1.1.0版添加

  • IsApiOnly
  • WrapWhenApiPathStartsWith

ShowApiVersion

如果要 在响应中显示API版本,则可以执行以下操作:

app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { ShowApiVersion = true });
           

默认API 版本格式设置为“ 1.0.0.0” 

ApiVersion

如果您希望指定其他版本格式,则可以执行以下操作:

app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { 
    ShowApiVersion = true, 
    ApiVersion = "2.0" 
});
           

ShowStatusCode

如果要在响应中显示StatusCode,则可以执行以下操作:

app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { ShowStatusCode = true });
           

IsDebug

默认情况下,AutoWrapper 禁止显示堆栈跟踪信息。如果要在开发阶段从响应中查看错误的实际详细信息,只需将设置AutoWrapperOptions IsDebug 为  true:

app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { IsDebug = true }); 
           

IsApiOnly

AutoWrapper 仅可用于ASP.NET Core API项目模板。如果要在前端项目(如Angular、MVC、React、Blazor和其他支持.NET Core的SPA框架)中组合API Controllers ,请使用此属性启用它。

app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { IsApiOnly = false} );
           

WrapWhenApiPathStartsWith

如果将IsApiOnly 选项设置为false,则还可以指定API 路径段以进行验证。默认情况下,它设置为"/api"。如果要将其设置为其他内容,则可以执行以下操作:

app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions { 
    IsApiOnly = false, 
    WrapWhenApiPathStartsWith = "/myapi" 
});
           

当请求路径包含WrapWhenApiPathStartsWith值时,这将激活AutoWrapper 来拦截HTTP响应。

引用:

请注意,我仍然建议您在一个单独的项目中实现API Controllers,以重视关注点的分离,并避免为您的SPAs和API混合路由配置。

支持日志

AutoWrapper 另一个好处是,日志记录已经预先配置。.NET Core应用程序默认情况下具有内置的日志记录机制,并且包装程序已拦截的所有请求和响应都将被自动记录(由于依赖倒置!)。.NET Core支持API与各种内置和第三方日志记录提供程序一起使用的日志记录。根据您使用哪种受支持的.NET Core日志记录提供程序以及如何配置记录数据的位置(例如,文本文件,Cloud等),AutoWrapper会自动为您写入日志。

支持Swagger

Swagger为您的API提供了高级文档,使开发人员可以参考您的API端点的详细信息并在必要时进行测试。这是非常有用的,特别是当您API是公开的并且希望许多开发人员使用它时。

AutoWrapper省略url中带有“/swagger”的任何请求,这样您仍然可以导航到Swagger UI以获得您的API文档。

总结

在本文中,我们学习了如何在您的ASP.NET Core应用程序中集成和使用AutoWrapper的核心功能。

我敢肯定,这个项目还有很多地方需要改进,请随时尝试一下,让我知道您的想法。

GitHub Repo

https://github.com/proudmonkey/AutoWrapper

参考文献

  • ASP.NET Core中间件
  • AutoWrapper.Core
  • ASP.NET Core 2.1:将VMD.RESTApiResponseWrapper.Core集成到REST API应用程序
  • ASP.NET Core和Web API:用于管理异常和一致响应的自定义包装器

继续阅读