目录

介绍

为什么?

怎么做?

VMD.RESTApiResponseWrapper Nuget软件包

安装及使用

ASP.NET Core集成

ASP.NET Web API集成

样本响应输出

定义自定义异常

定义自己的响应对象

包源代码

实现

包装器类

ValidationError类

ApiError类

ApiException类

ApiResponse类

ResponseMessage枚举

ASP.NET Core实现

标准ASP.NET Web API实现

异常过滤器

委托处理程序

总结

参考文献


介绍

如今,构建RESTFul API非常流行,我们今天构建的大多数项目都严重依赖API /服务与数据进行通信。如您所知,创建Web API是一件容易的事,但是设计一个好的API并不像您想象的那么容易,尤其是当您正在处理许多暴露某些public API端点的项目或微服务时。

本文将讨论如何为ASP.NET Core和Web API应用程序实现自定义包装,以管理异常,从而为使用者提供有意义且一致的响应。

为什么?

在继续深入之前,让我们先谈一谈“为什么”。为什么我们需要实现一个自定义包装,为什么这是一件好事。

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

在为“实际”应用程序项目构建API时,许多开发人员并不关心使用者。他们最关心的是他们可以将数据返回给消费者,仅此而已。API不仅是通过HTTP来回传递JSON,还包括如何向使用API​​的开发人员呈现有意义的响应。

永远记住

引用:

对于使用API​​的开发人员来说,良好的API设计是UX

作为重视消费者的开发人员,我们希望为他们提供有意义且一致的API响应。这就是为什么在本文中,我们将实现一个可在具有以下功能的应用程序之间重用的自定义包装器:

  • 处理意外错误
  • 处理ModelState验证错误
  • 可配置的自定义API异常
  • 结果和错误的一致响应对象
  • 详细的结果回复
  • 详细的错误响应
  • 可配置的HTTP状态码
  • 支持Swagger

怎么做?

使用ASP.NET Core或标准Web API时,重要的是处理异常并为API处理的所有请求返回一致的响应,无论成功或失败。这使得使用API​​变得容易得多,而无需在客户端上使用复杂的代码。通过为ASP.NET Core和Web API响应使用自定义包装,可以确保所有响应具有一致的结构,并且将处理所有异常。

我们将研究如何实现一个自定义包装,该包装可以处理上面列出的ASP.NET Core和标准Web API的所有功能。

VMD.RESTApiResponseWrapper Nuget软件包

如果要跳过实际的代码实现,可以将两个单独的Nuget包直接集成到项目中:

  • VMD.RESTApiResponseWrapper.Core (对于ASP.NET Core Apps)
  • VMD.RESTApiResponseWrapper.Net (对于标准Web API应用)

这些库中的每一个都是单独创建的。VMD.RESTApiResponseWrapper.Core使用ASP.NET 2.0的Core和Visual Studio 2017构建。它使用middleware实现包装器并管理例外。从另一个方面说,VMD.RESTApiResponseWrapper.Net是使用Visual Studio 2015的完整的.NET Framework V4.6构建的。它使用DelegatingHandler实现包装器,并使用一个ExceptionFilterAttribute处理异常。

安装及使用

首先,您需要先安装Newtonsoft.json软件包,然后再安装VMD.RESTApiResponseWrapper软件包。

ASP.NET Core集成

对于ASP.NET Core应用,可以通过NPM或使用以下命令来安装软件包:

PM> Install-Package VMD.RESTApiResponseWrapper.Core -Version 1.0.3

安装之后,可以按照以下步骤开始将包装器集成到ASP.NET Core项目中:

1、在Startup.cs声明以下名称空间:

using VMD.RESTApiResponseWrapper.Core.Extensions;

2、在Startup.csConfigure()方法中注册以下中间件:

app.UseAPIResponseWrapperMiddleware();

注意:确保在“MVC中间件之前注册它。

3、做完了

这里的文章演示了如何将这个库集成到您的ASP.NET Core REST API项目中:  ASP.NET Core 2.1:将VMD.RESTApiResponseWrapper.Core集成到您的REST API应用程序中

ASP.NET Web API集成

对于标准的ASP.NET Web API应用程序,您可以执行以下操作:

PM> Install-Package VMD.RESTApiResponseWrapper.Net -Version 1.0.3

安装之后,可以按照以下步骤开始将包装器集成到ASP.NET Web API项目中:

1、在WebApiConfig.cs声明以下名称空间:

using VMD.RESTApiResponseWrapper.Net;
using VMD.RESTApiResponseWrapper.Net.Filters;

2、在WebApiConfig.cs注册以下内容

config.Filters.Add(new ApiExceptionFilter());
config.MessageHandlers.Add(new WrappingHandler());

3、做完了

引用:

注意:截止撰写本文时,两个软件包的最新版本均为v1.0.3

样本响应输出

以下是响应输出的示例:

成功的响应格式和数据:

{"Version": "1.0.0.0","StatusCode": 200,"Message": "Request successful.","Result": ["value1","value2"]
}

没有数据的成功响应格式:

{"Version": "1.0.0.0","StatusCode": 201,"Message": "Student with ID 6 has been created."
}

验证错误的响应格式:

{"Version": "1.0.0.0","StatusCode": 400,"Message": "Request responded with exceptions.","ResponseException": {"IsError": true,"ExceptionMessage": "Validation Field Error.","Details": null,"ReferenceErrorCode": null,"ReferenceDocumentLink": null,"ValidationErrors": [{"Field": "LastName","Message": "'Last Name' should not be empty."},{"Field": "FirstName","Message": "'First Name' should not be empty."}]}
}

错误的响应格式:

{"Version": "1.0.0.0","StatusCode": 404,"Message": "Unable to process the request.","ResponseException": {"IsError": true,"ExceptionMessage": "The specified URI does not exist. Please verify and try again.","Details": null,"ReferenceErrorCode": null,"ReferenceDocumentLink": null,"ValidationErrors": null}
}

定义自定义异常

这个库不仅仅是中间件或包装器。它还提供了一种可用于定义自己的异常的方法。例如,如果您想抛出自己的异常消息,则可以简单地执行以下操作:

throw new ApiException("Your Message",401, ModelStateExtension.AllErrors(ModelState));

该ApiException有可以设置以下参数:

ApiException(string message,int statusCode = 500,IEnumerable<ValidationError> errors = null,string errorCode = "",string refLink = "")

定义自己的响应对象

除了引发您自己的自定义异常外,您还可以通过使用API控制器中的ApiResponse对象来返回自己的自定义定义JSON响应。例如:

return new APIResponse(201,"Created");

该APIResponse有可以设置以下参数:

APIResponse(int statusCode,string message = "",object result = null,ApiError apiError = null,string apiVersion = "1.0.0.0")

包源代码

这些包装器的代码是开源的,可以在github上找到:

  • https://github.com/proudmonkey/RESTApiResponseWrapper.Core
  • https://github.com/proudmonkey/RESTApiResponseWrapper.Net

随时免费检出。

实现

让我们看看如何为ASP.NET Core和标准Web API实现自定义包装器。让我们从两个项目中使用的通用类开始。

包装器类

ASP.NET Core和标准Web API项目都使用以下类:

  • ValidationError
  • ApiError
  • ApiException
  • ApiResponse
  • ResponseMessageEnum

上面的每个类都将用于实现用于管理异常和响应一致性的自定义包装器。请记住,本文演示的代码只是库的基本基础。您显然可以自由地修改和添加自己的属性,甚至可以根据业务需要自定义实现。

这是每个类的实际代码:

ValidationError类

public class ValidationError
{[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]public string Field { get; }public string Message { get; }public ValidationError(string field, string message){Field = field != string.Empty ? field : null;Message = message;}
}

ValidationError类包含一些用于存储字段及其相应消息的属性。注意[JsonProperty(NullValueHandling=NullValueHandling.Ignore)]特性上的Field属性。这是为了确保在有null值的情况下不会对该字段进行序列化。

ApiError类

public class ApiError
{public bool IsError { get; set; }public string ExceptionMessage { get; set; }public string Details { get; set; }public string ReferenceErrorCode { get; set; }public string ReferenceDocumentLink { get; set; }public IEnumerable<ValidationError> ValidationErrors { get; set; }public ApiError(string message){this.ExceptionMessage = message;this.IsError = true;}public ApiError(ModelStateDictionary modelState){this.IsError = true;if (modelState != null && modelState.Any(m => m.Value.Errors.Count > 0)){this.ExceptionMessage = "Please correct the specified validation errors and try again.";this.ValidationErrors = modelState.Keys.SelectMany(key => modelState[key].Errors.Select(x => new ValidationError(key, x.ErrorMessage))).ToList();}}
}

ApiError类是一种自定义序列化类型,用于通过JSON将错误信息返回给使用者。此类包含一些重要的特性,以提供有意义的信息给消费者,如ExceptionMessage,Details,ReferenceErrorCode,ReferenceDocumentLink和ValidationErrors。该类还具有一个重载构造函数,以传入ModelStateDictionary以将验证错误列表返回给使用者。

ApiException类

public class ApiException : System.Exception
{public int StatusCode { get; set; }public IEnumerable<ValidationError> Errors { get; set; }public string ReferenceErrorCode { get; set; }public string ReferenceDocumentLink { get; set; }public ApiException(string message,int statusCode = 500,IEnumerable<ValidationError> errors = null,string errorCode = "",string refLink = "") :base(message){this.StatusCode = statusCode;this.Errors = errors;this.ReferenceErrorCode = errorCode;this.ReferenceDocumentLink = refLink;}public ApiException(System.Exception ex, int statusCode = 500) : base(ex.Message){StatusCode = statusCode;}
}

ApiException类是用于抛出显示的和应用产生的错误的自定义异常。这些通常用于验证错误或可能具有已知负面响应(例如,失败的登录尝试)的常见操作。目的是返回明确定义的错误消息,以供使用者安全使用。

ApiResponse类

[DataContract]
public class APIResponse
{[DataMember]public string Version { get; set; }[DataMember]public int StatusCode { get; set; }[DataMember]public string Message { get; set; }[DataMember(EmitDefaultValue = false)]public ApiError ResponseException { get; set; }[DataMember(EmitDefaultValue = false)]public object Result { get; set; }public APIResponse(int statusCode, string message = "", object result = null, ApiError apiError = null, string apiVersion = "1.0.0.0"){this.StatusCode = statusCode;this.Message = message;this.Result = result;this.ResponseException = apiError;this.Version = apiVersion;}
}

APIResponse类是被用于为所有的API响应提供一致的数据结构的自定义包装响应对象。这包含了一些基本特性,例如Version、StatusCode、Message、ResponseException和Result。我们使用DataContract属性来定义要返回的属性,例如,ResponseException和Result属性不会返回,如果值为null。

ResponseMessage枚举

public enum ResponseMessageEnum
{[Description("Request successful.")]Success,[Description("Request responded with exceptions.")]Exception,[Description("Request denied.")]UnAuthorized,[Description("Request responded with validation error(s).")]ValidationError,[Description("Unable to process the request.")]Failure
}

ResponseMessageEnum提供了枚举,用于响应描述,例如Success,Exception,UnAuthorize和ValidationError。

ASP.NET Core实现

现在我们已经准备好了包装器类,是时候对它们进行实际的实现了。

对于ASP.NET Core实现,我们将使用中间件来实现上面列出的自定义包装器功能。中间件是构成处理应用程序请求和响应的管道的组件。每个被调用的中间件都可以选择对请求进行一些处理,然后再在线调用下一个中间件。在执行从调用返回到下一个中​​间件之后,就有机会对响应进行处理。有关更多详细信息,请参见ASP.NET Core中间件。

我们需要在中间件类中做一些工作,因为我们想吐出自己的预定义Response对象,并且希望捕获或过滤掉显式和未处理的API异常。

这是自定义ASP.NET Core中间件的代码。

public class APIResponseMiddleware
{private readonly RequestDelegate _next;public APIResponseMiddleware(RequestDelegate next){_next = next;}public async Task Invoke(HttpContext context){if (IsSwagger(context))await this._next(context);else{var originalBodyStream = context.Response.Body;using (var responseBody = new MemoryStream()){context.Response.Body = responseBody;try{await _next.Invoke(context);if (context.Response.StatusCode == (int)HttpStatusCode.OK){var body = await FormatResponse(context.Response);await HandleSuccessRequestAsync(context, body, context.Response.StatusCode);}else{await HandleNotSuccessRequestAsync(context, context.Response.StatusCode);}}catch (System.Exception ex){await HandleExceptionAsync(context, ex);}finally{responseBody.Seek(0, SeekOrigin.Begin);await responseBody.CopyToAsync(originalBodyStream);}}}}private static Task HandleExceptionAsync(HttpContext context, System.Exception exception){ApiError apiError = null;APIResponse apiResponse = null;int code = 0;if (exception is ApiException){var ex = exception as ApiException;apiError = new ApiError(ex.Message);apiError.ValidationErrors = ex.Errors;apiError.ReferenceErrorCode = ex.ReferenceErrorCode;apiError.ReferenceDocumentLink = ex.ReferenceDocumentLink;code = ex.StatusCode;context.Response.StatusCode = code;}else if (exception is UnauthorizedAccessException){apiError = new ApiError("Unauthorized Access");code = (int)HttpStatusCode.Unauthorized;context.Response.StatusCode = code;}else{
#if !DEBUGvar msg = "An unhandled error occurred.";string stack = null;
#elsevar msg = exception.GetBaseException().Message;string stack = exception.StackTrace;
#endifapiError = new ApiError(msg);apiError.Details = stack;code = (int)HttpStatusCode.InternalServerError;context.Response.StatusCode = code;}context.Response.ContentType = "application/json";apiResponse = new APIResponse(code, ResponseMessageEnum.Exception.GetDescription(), null, apiError);var json = JsonConvert.SerializeObject(apiResponse);return context.Response.WriteAsync(json);}private static Task HandleNotSuccessRequestAsync(HttpContext context, int code){context.Response.ContentType = "application/json";ApiError apiError = null;APIResponse apiResponse = null;if (code == (int)HttpStatusCode.NotFound)apiError = new ApiError("The specified URI does not exist. Please verify and try again.");else if (code == (int)HttpStatusCode.NoContent)apiError = new ApiError("The specified URI does not contain any content.");elseapiError = new ApiError("Your request cannot be processed. Please contact a support.");apiResponse = new APIResponse(code, ResponseMessageEnum.Failure.GetDescription(), null, apiError);context.Response.StatusCode = code;var json = JsonConvert.SerializeObject(apiResponse);return context.Response.WriteAsync(json);}private static Task HandleSuccessRequestAsync(HttpContext context, object body, int code){context.Response.ContentType = "application/json";string jsonString, bodyText = string.Empty;APIResponse apiResponse = null;if (!body.ToString().IsValidJson())bodyText = JsonConvert.SerializeObject(body);elsebodyText = body.ToString();dynamic bodyContent = JsonConvert.DeserializeObject<dynamic>(bodyText);Type type;type = bodyContent?.GetType();if (type.Equals(typeof(Newtonsoft.Json.Linq.JObject))){apiResponse = JsonConvert.DeserializeObject<APIResponse>(bodyText);if (apiResponse.StatusCode != code)jsonString = JsonConvert.SerializeObject(apiResponse);else if (apiResponse.Result != null)jsonString = JsonConvert.SerializeObject(apiResponse);else{apiResponse = new APIResponse(code, ResponseMessageEnum.Success.GetDescription(), bodyContent, null);jsonString = JsonConvert.SerializeObject(apiResponse);}}else{apiResponse = new APIResponse(code, ResponseMessageEnum.Success.GetDescription(), bodyContent, null);jsonString = JsonConvert.SerializeObject(apiResponse);}return context.Response.WriteAsync(jsonString);}private async Task<string> FormatResponse(HttpResponse response){response.Body.Seek(0, SeekOrigin.Begin);var plainBodyText = await new StreamReader(response.Body).ReadToEndAsync();response.Body.Seek(0, SeekOrigin.Begin);return plainBodyText;}private bool IsSwagger(HttpContext context){return context.Request.Path.StartsWithSegments("/swagger");}
}

我们的定制中间件的主要方法是Invoke()。此方法接受HttpContext作为参数。上下文从管道对象中保持当前的Request和Response。这使我们可以截取上下文并进行一些自定义处理,在这种情况下:a处理异常b返回标准的自定义响应对象。

APIResponseMiddleware还包含三个主要private方法:

  • HandleExceptionAsync()
  • HandleNotSuccessRequestAsync()
  • HandleSuccessRequestAsync()。

HandleExceptionAsync()方法处理已引发的异常,然后从中构造一个自定义响应对象,并将其作为最终响应对象返回。HandleNotSuccessRequestAsync()方法根据状态码处理特定的响应。在这个例子中,我们筛选出NotFound和NoContent StatusCodes并再构建一个自定义的响应。最后,HandleSuccessRequestAsync()方法处理成功的响应并构造一个自定义响应对象,该对象将返回给使用者。

请注意,以上所有方法都使用APIResponse类作为最终响应对象。

既然我们已经实现了自定义中间件,那么我们可以创建一个static类来简化将中间件添加到应用程序管道中的过程:

public static class ApiResponseMiddlewareExtension
{public static IApplicationBuilder UseAPIResponseWrapperMiddleware(this IApplicationBuilder builder){return builder.UseMiddleware<APIResponseMiddleware>();}
}

使用自定义中间件的最后一步是在Startup类的Configure()方法内调用我们在上面创建的扩展方法:

app.UseAPIResponseMiddleware();

标准ASP.NET Web API实现

由于中间件是为ASP.NET Core应用程序设计的,因此在标准Web API项目中,我们将使用ExceptionFilterAttribute来处理和管理异常,并使用DelegatingHandler来实现自定义响应包装器。

异常过滤器

让我们从异常过滤器实现开始。这是过滤器实现的代码:

public class ApiExceptionFilter : ExceptionFilterAttribute
{public override void OnException(HttpActionExecutedContext context){ApiError apiError = null;APIResponse apiResponse = null;int code = 0;if (context.Exception is ApiException){var ex = context.Exception as ApiException;apiError = new ApiError(ex.Message);apiError.ValidationErrors = ex.Errors;apiError.ReferenceErrorCode = ex.ReferenceErrorCode;apiError.ReferenceDocumentLink = ex.ReferenceDocumentLink;code = ex.StatusCode;}else if (context.Exception is UnauthorizedAccessException){apiError = new ApiError("Unauthorized Access");code = (int)HttpStatusCode.Unauthorized;}else{
#if !DEBUGvar msg = "An unhandled error occurred.";string stack = null;
#elsevar msg = context.Exception.GetBaseException().Message;string stack = context.Exception.StackTrace;
#endifapiError = new ApiError(msg);apiError.Details = stack;code = (int)HttpStatusCode.InternalServerError;}apiResponse = new APIResponse(code, ResponseMessageEnum.Exception.GetDescription(), null, apiError);HttpStatusCode c = (HttpStatusCode)code;context.Response = context.Request.CreateResponse(c, apiResponse);}
}

就像ASP.NET Core的HandleExceptionAsync()方法一样,自定义异常筛选器方法处理从应用程序引发的异常。筛选器的实现与ASP.NET Core HandleExceptionAsync()方法的实现几乎相同。

让我们详细说明一下过滤器的实际作用。异常过滤器区分几种不同的异常类型。首先,它查看自定义ApiException类型,这是特殊应用程序生成的Exception,可用于向消费者显示有意义的响应。

接下来是UnAuthorized的异常,通过返回强制401异常来特殊处理,该异常可以在客户端上用于强制认证。

最后,还有Unhandled异常——这些是应用程序未明确知道的意外失败。这可能是硬件故障,null引用异常,意外的分析错误。基本上所有未处理的内容。这些错误会在生产中生成通用错误消息,因此不会返回任何敏感数据。

委托处理程序

DelegatingHandlers对于横切关注点非常有用。它们连接到请求-响应管道的最早和最晚阶段,因此非常适合在将响应发送回客户端之前对其进行操作。这是委托处理程序的代码。

public class WrappingHandler : DelegatingHandler
{protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){if (IsSwagger(request)){return await base.SendAsync(request, cancellationToken);}else{var response = await base.SendAsync(request, cancellationToken);return BuildApiResponse(request, response);}}private static HttpResponseMessage BuildApiResponse(HttpRequestMessage request, HttpResponseMessage response){dynamic content = null;object data = null;string errorMessage = null;ApiError apiError = null;var code = (int)response.StatusCode;if (response.TryGetContentValue(out content) && !response.IsSuccessStatusCode){HttpError error = content as HttpError;//handle exceptionif (error != null){content = null;if (response.StatusCode == HttpStatusCode.NotFound)apiError = new ApiError("The specified URI does not exist. Please verify and try again.");else if (response.StatusCode == HttpStatusCode.NoContent)apiError = new ApiError("The specified URI does not contain any content.");else{errorMessage = error.Message;#if DEBUGerrorMessage = string.Concat(errorMessage, error.ExceptionMessage, error.StackTrace);
#endifapiError = new ApiError(errorMessage);}data = new APIResponse((int)code, ResponseMessageEnum.Failure.GetDescription(), null, apiError);}elsedata = content;}else{if (response.TryGetContentValue(out content)){Type type;type = content?.GetType();if (type.Name.Equals("APIResponse")){response.StatusCode = Enum.Parse(typeof(HttpStatusCode), content.StatusCode.ToString());data = content;}else if (type.Name.Equals("SwaggerDocument"))data = content;elsedata = new APIResponse(code, ResponseMessageEnum.Success.GetDescription(), content);}else{if (response.IsSuccessStatusCode)data = new APIResponse((int)response.StatusCode, ResponseMessageEnum.Success.GetDescription());}}var newResponse = request.CreateResponse(response.StatusCode, data);foreach (var header in response.Headers){newResponse.Headers.Add(header.Key, header.Value);}return newResponse;}private bool IsSwagger(HttpRequestMessage request){return request.RequestUri.PathAndQuery.StartsWith("/swagger");}
}

上面的代码以不同的方式实现,但完成的功能与ASP.NET Core中间件中实现的功能相同。对于这种情况,我们使用委托处理程序来拦截当前上下文并为使用者构造一个自定义响应对象。我们使用Request.CreateResponse()方法使用适当的格式化程序创建了新的响应,然后在返回最终响应对象之前复制旧的未包装响应的任何头。

要使用过滤器和包装器,只需在WebApiConfig.cs文件中注册它们:

config.Filters.Add(new ApiExceptionFilter());
config.MessageHandlers.Add(new WrappingHandler());

总结

在本文中,我们学习了如何为ASP.NET Core和标准Web API项目创建用于管理API异常和一致响应的简单自定义包装。我们还学习了如何轻松地将VMS.RESTApiResponseWrapper库集成到ASP.NET Core和标准Web API项目中,而无需执行本文中演示的所有代码实现。

随时下载源代码或查看github存储库。谢谢!:)

参考文献

  • ASP.NET Core中间件
  • ASP.NET Core 2.1:将VMD.RESTApiResponseWrapper.Core集成到REST API应用程序
  • ASP.NET Web API中的HTTP消息处理程序
  • ASP.NET Web API中的异常处理
  • ASP.NET Core中的日志请求和响应
  • ASP.NET Core API的错误处理和ExceptionFilter依赖项注入
  • 包装ASP.NET Web API响应以保持一致性并提供其他信息

ASP.NET Core和Web API:用于管理异常和一致响应的自定义包装器相关推荐

  1. C#编写ASP.NET Core的Web API并部署到IIS上的详细教程(API用于准确获取Word/Excel/PPT/PDF的页数)6 -将项目部署到IIS,及常见错误解决方案

    C#编写ASP.NET Core的Web API并部署到IIS上的详细教程(API用于准确获取Word/Excel/PPT/PDF的页数)6 -将项目部署到IIS,及常见错误解决方案 1.前言 2.安 ...

  2. 使用angular4和asp.net core 2 web api做个练习项目(四)

    第一部分: http://www.cnblogs.com/cgzl/p/7755801.html 第二部分: http://www.cnblogs.com/cgzl/p/7763397.html 第三 ...

  3. ASP.NET Core 教学 - Web API JSON 序列化设定

    用 JSON 作为 Web API 资料传递格式,并使用 camelCase 作为名称命名规则,几乎已成为通用的标准.ASP.NET Core Web API 也很贴心的把回传物件格式预设为 JSON ...

  4. 使用angular4和asp.net core 2 web api做个练习项目(二), 这部分都是angular

    上一篇: http://www.cnblogs.com/cgzl/p/7755801.html 完成client.service.ts: import { Injectable } from '@an ...

  5. asp编程工具_使用ASP.NET Core构建RESTful API的技术指南

    译者荐语:利用周末的时间,本人拜读了长沙.NET技术社区翻译的技术文章<微软RESTFul API指南>,打算按照步骤写一个完整的教程,后来无意中看到了这篇文章,与我要写的主题有不少相似之 ...

  6. Asp Net Core 5 REST API 使用 RefreshToken 刷新 JWT - Step by Step(三)

    翻译自 Mohamad Lawand 2021年1月25日的文章 <Refresh JWT with Refresh Tokens in Asp Net Core 5 Rest API Step ...

  7. ASP.NET Core WebApi构建API接口服务实战演练

    一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...

  8. 使用ASP.NET Core构建RESTful API的技术指南

    译者荐语:利用周末的时间,本人拜读了长沙.NET技术社区翻译的技术文章<微软RESTFul API指南>,打算按照步骤写一个完整的教程,后来无意中看到了这篇文章,与我要写的主题有不少相似之 ...

  9. Asp.Net Core 5 REST API 使用 JWT 身份验证 - Step by Step(二)

    翻译自 Mohamad Lawand 2021年1月22日的文章 <Asp Net Core 5 Rest API Authentication with JWT Step by Step> ...

最新文章

  1. 更改git bash默认的路径
  2. 15.4:泛型化方法
  3. Zookeeper基于Java访问-节点事件监听
  4. Lucene概述第一部分:创建索引
  5. Go语言实战-nginx日志处理
  6. SpreadJS企业表格技术实践一:自定制表格编辑器
  7. PHP+mysql共享自行车租赁管理系统
  8. 一个方便快捷gif在线水印制作(支持文字和图片)
  9. 泛函分析 04.05 有界线性算子 - 闭算子与闭图像定理
  10. codeigniter配置
  11. IntelliJ IDEA Dependency 'XXXX' not found 或 java:程序包XXXX不存在,找不到的解决方案
  12. C++程序设计之客户消费积分管理
  13. android 录屏工具,android实现录屏小功能
  14. python中反差分操作
  15. Win10开始菜单卡死解决办法
  16. swiper滑到最后一页交互
  17. 反射之invoke方法
  18. 独孤思维:没有复盘的项目,不会赚钱
  19. login登录页面 html,login.html
  20. 有了钉钉智能前台 美女前台可能要失业了

热门文章

  1. cmake copy dll输出目录_VScode下 多文件,小工程使用cmake 速记
  2. laravel没有route.php,Laravel中的RouteCollection.php中的NotFoundHttpException
  3. java正方形个圆形面积_java计算图形面积(圆形,正方形, 长方形).pptx
  4. 游戏迷英雄联盟游戏高清壁纸!扮演你的英雄
  5. 国内设计灵感网站,设计师学习必备
  6. 电商淘宝天猫美妆页面PSD分层模板,优秀作品促进品牌提升
  7. 计算机两万字符英语文献翻译,求一篇2万字符以上的工程类外文文献及翻译
  8. Python字典中 get() 函数的使用
  9. CI/CD(持续集成构建/持续交付):如何测试/集成/交付项目代码?(Jenkins,TravisCI)
  10. gettid()和pthread_self()的区别