Api: exception handling
This commit is contained in:
parent
bea25886b2
commit
fd0466e286
75
common/ASC.Api.Core/Middleware/CommonApiResponse.cs
Normal file
75
common/ASC.Api.Core/Middleware/CommonApiResponse.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace ASC.Api.Core.Middleware
|
||||
{
|
||||
[DataContract]
|
||||
public class CommonApiResponse
|
||||
{
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public int? Count { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public int Status { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public object Response { get; set; }
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public CommonApiError Error { get; set; }
|
||||
|
||||
protected CommonApiResponse(HttpStatusCode statusCode, object response = null, Exception error = null)
|
||||
{
|
||||
Status = Convert.ToInt32((int)statusCode >= 400);
|
||||
StatusCode = statusCode;
|
||||
Response = response;
|
||||
Error = CommonApiError.FromException(error);
|
||||
}
|
||||
|
||||
public static CommonApiResponse Create(HttpStatusCode statusCode, object response = null)
|
||||
{
|
||||
return new CommonApiResponse(statusCode, response);
|
||||
}
|
||||
|
||||
public static CommonApiResponse CreateError(HttpStatusCode statusCode, Exception error = null)
|
||||
{
|
||||
return new CommonApiResponse(statusCode, error: error);
|
||||
}
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class CommonApiError
|
||||
{
|
||||
[DataMember]
|
||||
public string Message { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public Type Type { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public string Stack { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public int Hresult { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public IDictionary Data { get; set; }
|
||||
|
||||
public static CommonApiError FromException(Exception exception)
|
||||
{
|
||||
return new CommonApiError()
|
||||
{
|
||||
Message = exception.Message,
|
||||
Type = exception.GetType(),
|
||||
Stack = exception.StackTrace,
|
||||
Hresult = exception.HResult,
|
||||
Data = exception.Data
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
141
common/ASC.Api.Core/Middleware/ResponseParser.cs
Normal file
141
common/ASC.Api.Core/Middleware/ResponseParser.cs
Normal file
@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace ASC.Api.Core.Middleware
|
||||
{
|
||||
abstract class ResponseParser
|
||||
{
|
||||
public abstract object Deserialize(string response);
|
||||
|
||||
public abstract string Serialize(CommonApiResponse response);
|
||||
|
||||
public string WrapAndWrite(HttpStatusCode statusCode, string response)
|
||||
{
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
var result1 = CommonApiResponse.CreateError(statusCode, new UnauthorizedAccessException());
|
||||
return Serialize(result1);
|
||||
default:
|
||||
var result = CommonApiResponse.Create(statusCode, Deserialize(response));
|
||||
return Serialize(result);
|
||||
}
|
||||
}
|
||||
|
||||
public string WrapAndWrite(HttpStatusCode statusCode, Exception error)
|
||||
{
|
||||
var result = CommonApiResponse.CreateError(statusCode, error);
|
||||
return Serialize(result);
|
||||
}
|
||||
}
|
||||
|
||||
class JsonResponseParser : ResponseParser
|
||||
{
|
||||
public override object Deserialize(string response)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(response);
|
||||
}
|
||||
|
||||
public override string Serialize(CommonApiResponse response)
|
||||
{
|
||||
if (response.Status == 0)
|
||||
{
|
||||
response.Count = response.Response is JObject ? 1 : ((response.Response as JArray)?.Count ?? 0);
|
||||
}
|
||||
var settings = new JsonSerializerSettings()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
|
||||
};
|
||||
return JsonConvert.SerializeObject(response, settings);
|
||||
}
|
||||
}
|
||||
class XmlResponseParser : ResponseParser
|
||||
{
|
||||
public override object Deserialize(string response)
|
||||
{
|
||||
return XDocument.Parse(response);
|
||||
}
|
||||
|
||||
public override string Serialize(CommonApiResponse response)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
var result = new XElement("result");
|
||||
var responseElements = new List<XElement>();
|
||||
|
||||
if (response.Response != null)
|
||||
{
|
||||
var root = ((XDocument)response.Response).Root;
|
||||
if (root.Name.LocalName.StartsWith("ArrayOf"))
|
||||
{
|
||||
var elements = root.Elements();
|
||||
|
||||
foreach (var e in elements)
|
||||
{
|
||||
responseElements.Add(GetResponse(e));
|
||||
}
|
||||
|
||||
count = elements.Count();
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 1;
|
||||
responseElements.Add(GetResponse(root));
|
||||
}
|
||||
result.Add(new XElement(nameof(response.Count).ToCamelCase(), count));
|
||||
}
|
||||
else if (response.Error != null)
|
||||
{
|
||||
responseElements.Add(new XElement(nameof(response.Error).ToCamelCase(), response.Error));
|
||||
}
|
||||
|
||||
result.Add(new XElement(nameof(response.Status).ToCamelCase(), response.Status));
|
||||
result.Add(new XElement(nameof(response.StatusCode).ToCamelCase(), (int)response.StatusCode));
|
||||
result.Add(responseElements);
|
||||
|
||||
var doc = new XDocument(result);
|
||||
return doc.ToString();
|
||||
|
||||
XElement GetResponse(XElement xElement)
|
||||
{
|
||||
return ToLowerCamelCase(new XElement(nameof(response.Response).ToCamelCase(), xElement.Elements().Select(ToLowerCamelCase)));
|
||||
}
|
||||
|
||||
XElement ToLowerCamelCase(XElement xElement)
|
||||
{
|
||||
var lowerXElement = new XElement(xElement.Name.LocalName.ToCamelCase());
|
||||
|
||||
var elements = xElement.Elements();
|
||||
if (elements.Any())
|
||||
{
|
||||
lowerXElement.Add(elements.Select(ToLowerCamelCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
lowerXElement.Add(xElement.Nodes());
|
||||
}
|
||||
|
||||
return lowerXElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringExtension
|
||||
{
|
||||
public static string ToCamelCase(this string str)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(str) && str.Length > 1)
|
||||
{
|
||||
return Char.ToLowerInvariant(str[0]) + str.Substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,18 +2,11 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ASC.Web.Api.Middleware
|
||||
namespace ASC.Api.Core.Middleware
|
||||
{
|
||||
public class ResponseWrapper
|
||||
{
|
||||
@ -26,13 +19,22 @@ namespace ASC.Web.Api.Middleware
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
Exception error = null;
|
||||
var currentBody = context.Response.Body;
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
|
||||
context.Response.Body = memoryStream;
|
||||
|
||||
await next(context);
|
||||
try
|
||||
{
|
||||
await next(context);
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
context.Response.StatusCode = 500;
|
||||
error = exception;
|
||||
}
|
||||
|
||||
context.Response.Body = currentBody;
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
@ -51,7 +53,14 @@ namespace ASC.Web.Api.Middleware
|
||||
}
|
||||
|
||||
var readToEnd = new StreamReader(memoryStream).ReadToEnd();
|
||||
await context.Response.WriteAsync(responseParser.WrapAndWrite((HttpStatusCode)context.Response.StatusCode, readToEnd));
|
||||
if(error != null)
|
||||
{
|
||||
await context.Response.WriteAsync(responseParser.WrapAndWrite((HttpStatusCode)context.Response.StatusCode, error));
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.Response.WriteAsync(responseParser.WrapAndWrite((HttpStatusCode)context.Response.StatusCode, readToEnd));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -63,135 +72,4 @@ namespace ASC.Web.Api.Middleware
|
||||
return builder.UseMiddleware<ResponseWrapper>();
|
||||
}
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class CommonApiResponse
|
||||
{
|
||||
[DataMember]
|
||||
public int Count { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public int Status { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public object Response { get; set; }
|
||||
|
||||
protected CommonApiResponse(HttpStatusCode statusCode, object response = null, int status = 0)
|
||||
{
|
||||
Status = status;
|
||||
StatusCode = statusCode;
|
||||
Response = response;
|
||||
}
|
||||
|
||||
public static CommonApiResponse Create(HttpStatusCode statusCode, object response = null, int status = 0)
|
||||
{
|
||||
return new CommonApiResponse(statusCode, response, status);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ResponseParser
|
||||
{
|
||||
public abstract object Deserialize(string response);
|
||||
|
||||
public abstract string Serialize(CommonApiResponse response);
|
||||
|
||||
public string WrapAndWrite(HttpStatusCode statusCode, string response)
|
||||
{
|
||||
var result = CommonApiResponse.Create(statusCode, Deserialize(response));
|
||||
return Serialize(result);
|
||||
}
|
||||
}
|
||||
|
||||
class JsonResponseParser : ResponseParser
|
||||
{
|
||||
public override object Deserialize(string response)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(response);
|
||||
}
|
||||
|
||||
public override string Serialize(CommonApiResponse response)
|
||||
{
|
||||
response.Count = response.Response is JObject ? 1 : ((response.Response as JArray)?.Count ?? 0);
|
||||
var settings = new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() };
|
||||
return JsonConvert.SerializeObject(response, settings);
|
||||
}
|
||||
}
|
||||
class XmlResponseParser : ResponseParser
|
||||
{
|
||||
public override object Deserialize(string response)
|
||||
{
|
||||
return XDocument.Parse(response);
|
||||
}
|
||||
|
||||
public override string Serialize(CommonApiResponse response)
|
||||
{
|
||||
var root = ((XDocument)response.Response).Root;
|
||||
var count = 0;
|
||||
|
||||
var result = new XElement("result");
|
||||
var responseElements = new List<XElement>();
|
||||
|
||||
if (root.Name.LocalName.StartsWith("ArrayOf"))
|
||||
{
|
||||
var elements = root.Elements();
|
||||
|
||||
foreach(var e in elements)
|
||||
{
|
||||
responseElements.Add(GetResponse(e));
|
||||
}
|
||||
|
||||
count = elements.Count();
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 1;
|
||||
responseElements.Add(GetResponse(root));
|
||||
}
|
||||
|
||||
result.Add(new XElement(nameof(response.Count).ToCamelCase(), count));
|
||||
result.Add(new XElement(nameof(response.Status).ToCamelCase(), response.Status));
|
||||
result.Add(new XElement(nameof(response.StatusCode).ToCamelCase(), (int)response.StatusCode));
|
||||
result.Add(responseElements);
|
||||
|
||||
var doc = new XDocument(result);
|
||||
return doc.ToString();
|
||||
|
||||
XElement GetResponse(XElement xElement)
|
||||
{
|
||||
return ToLowerCamelCase(new XElement(nameof(response.Response).ToCamelCase(), xElement.Elements().Select(ToLowerCamelCase)));
|
||||
}
|
||||
|
||||
XElement ToLowerCamelCase(XElement xElement)
|
||||
{
|
||||
var lowerXElement = new XElement(xElement.Name.LocalName.ToCamelCase());
|
||||
|
||||
var elements = xElement.Elements();
|
||||
if (elements.Any())
|
||||
{
|
||||
lowerXElement.Add(elements.Select(ToLowerCamelCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
lowerXElement.Add(xElement.Nodes());
|
||||
}
|
||||
|
||||
return lowerXElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringExtension
|
||||
{
|
||||
public static string ToCamelCase(this string str)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(str) && str.Length > 1)
|
||||
{
|
||||
return Char.ToLowerInvariant(str[0]) + str.Substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -12,11 +15,7 @@ using Microsoft.Extensions.Hosting;
|
||||
using ASC.Api.Core;
|
||||
using ASC.Common.Logging;
|
||||
using ASC.Web.Api.Handlers;
|
||||
using ASC.Web.Api.Middleware;
|
||||
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using ASC.Api.Core.Middleware;
|
||||
|
||||
namespace ASC.Web.Api
|
||||
{
|
||||
@ -50,7 +49,8 @@ namespace ASC.Web.Api
|
||||
.Select(Assembly.LoadFrom)
|
||||
.Where(r => r.GetCustomAttribute<CustomApiAttribute>() != null);
|
||||
|
||||
foreach (var a in assemblies) {
|
||||
foreach (var a in assemblies)
|
||||
{
|
||||
builder.AddApplicationPart(a);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user