Api: replace middleware with filter

This commit is contained in:
pavelbannov 2019-06-13 15:12:21 +03:00
parent 8b174a9cf7
commit a56dfd498e
4 changed files with 32 additions and 217 deletions

View File

@ -1,5 +1,7 @@
using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using System.Net;
using System.Runtime.Serialization;
@ -56,6 +58,19 @@ namespace ASC.Api.Core.Middleware
{
Status = 0;
Response = response;
if (response is IEnumerable<object> collection)
{
Count = collection.Count();
}
else if(response == null)
{
Count = 0;
}
else
{
Count = 1;
}
}
}

View File

@ -1,148 +0,0 @@
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(SuccessApiResponse response);
public abstract string Serialize(ErrorApiResponse response);
public string WrapAndWrite(HttpStatusCode statusCode, string response, Exception error = null)
{
if (error != null)
{
var result = CommonApiResponse.CreateError(statusCode, error);
return Serialize(result);
}
else
{
var result = CommonApiResponse.Create(statusCode, Deserialize(response));
return Serialize(result);
}
}
}
class JsonResponseParser : ResponseParser
{
public JsonSerializerSettings Settings { get; }
public JsonResponseParser()
{
Settings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
}
public override object Deserialize(string response)
{
return JsonConvert.DeserializeObject(response);
}
public override string Serialize(SuccessApiResponse response)
{
response.Count = response.Response is JObject ? 1 : ((response.Response as JArray)?.Count ?? 0);
return JsonConvert.SerializeObject(response, Settings);
}
public override string Serialize(ErrorApiResponse response)
{
return JsonConvert.SerializeObject(response, Settings);
}
}
class XmlResponseParser : ResponseParser
{
public override object Deserialize(string response)
{
return XDocument.Parse(response);
}
public override string Serialize(SuccessApiResponse response)
{
var count = 0;
var result = new XElement("result");
var responseElements = new List<XElement>();
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));
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)));
}
}
public override string Serialize(ErrorApiResponse response)
{
var result = new XElement("result");
result.Add(new XElement(nameof(response.Status).ToCamelCase(), response.Status));
result.Add(new XElement(nameof(response.StatusCode).ToCamelCase(), (int)response.StatusCode));
result.Add(new XElement(nameof(response.Error).ToCamelCase(), response.Error));
var doc = new XDocument(result);
return doc.ToString();
}
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;
}
}
}

View File

@ -1,79 +1,27 @@
using System;
using System.IO;
using System.Net;
using System.Security.Authentication;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace ASC.Api.Core.Middleware
{
public class ResponseWrapper
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly RequestDelegate next;
public ResponseWrapper(RequestDelegate next)
public override void OnException(ExceptionContext context)
{
this.next = next;
context.Result = new ObjectResult(new ErrorApiResponse((HttpStatusCode)context.HttpContext.Response.StatusCode, context.Exception));
}
public async Task Invoke(HttpContext context)
{
Exception error = null;
var currentBody = context.Response.Body;
using var memoryStream = new MemoryStream();
context.Response.Body = memoryStream;
try
{
await next(context);
if(context.Response.StatusCode == 401)
{
error = new AuthenticationException(HttpStatusCode.Unauthorized.ToString());
}
}
catch(AuthenticationException exception)
{
context.Response.StatusCode = 401;
error = exception;
}
catch(Exception exception)
{
context.Response.StatusCode = 500;
error = exception;
}
context.Response.Body = currentBody;
memoryStream.Seek(0, SeekOrigin.Begin);
ResponseParser responseParser;
switch (context.Request.RouteValues["format"])
{
case "xml":
responseParser = new XmlResponseParser();
break;
case "json":
default:
responseParser = new JsonResponseParser();
break;
}
var readToEnd = new StreamReader(memoryStream).ReadToEnd();
await context.Response.WriteAsync(responseParser.WrapAndWrite((HttpStatusCode)context.Response.StatusCode, readToEnd, error));
}
}
public static class ResponseWrapperExtensions
public class CustomResponseFilterAttribute : ResultFilterAttribute
{
public static IApplicationBuilder UseResponseWrapper(this IApplicationBuilder builder)
public override void OnResultExecuting(ResultExecutingContext context)
{
return builder.UseMiddleware<ResponseWrapper>();
if (context.Result is ObjectResult result)
{
result.Value = new SuccessApiResponse((HttpStatusCode)context.HttpContext.Response.StatusCode, result.Value);
}
base.OnResultExecuting(context);
}
}
}

View File

@ -51,8 +51,10 @@ namespace ASC.Web.Api
var builder = services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
config.Filters.Add(new TypeFilterAttribute(typeof(FormatFilter)));
config.Filters.Add(new AuthorizeFilter(policy));
config.Filters.Add(new CustomResponseFilterAttribute());
config.Filters.Add(new CustomExceptionFilterAttribute());
config.Filters.Add(new TypeFilterAttribute(typeof(FormatFilter)));
});
var container = services.AddAutofac(Configuration);
@ -88,8 +90,6 @@ namespace ASC.Web.Api
app.UseAuthentication();
app.UseResponseWrapper();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();