diff --git a/src/Kyoo.Core/CoreModule.cs b/src/Kyoo.Core/CoreModule.cs index f601ad25..dd5f2e39 100644 --- a/src/Kyoo.Core/CoreModule.cs +++ b/src/Kyoo.Core/CoreModule.cs @@ -26,7 +26,6 @@ using Autofac.Extras.AttributeMetadata; using Kyoo.Abstractions; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models.Utils; -using Kyoo.Core.Api; using Kyoo.Core.Controllers; using Kyoo.Core.Models.Options; using Kyoo.Core.Tasks; @@ -35,11 +34,12 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.StaticFiles; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using Serilog; using IMetadataProvider = Kyoo.Abstractions.Controllers.IMetadataProvider; +using JsonOptions = Kyoo.Core.Api.JsonOptions; namespace Kyoo.Core { @@ -67,20 +67,6 @@ namespace Kyoo.Core { "logging", null } }; - /// - /// The configuration to use. - /// - private readonly IConfiguration _configuration; - - /// - /// Create a new core module instance and use the given configuration. - /// - /// The configuration to use - public CoreModule(IConfiguration configuration) - { - _configuration = configuration; - } - /// public void Configure(ContainerBuilder builder) { @@ -140,17 +126,13 @@ namespace Kyoo.Core /// public void Configure(IServiceCollection services) { - Uri publicUrl = _configuration.GetPublicUrl(); + services.AddTransient, JsonOptions>(); services.AddMvcCore() + .AddNewtonsoftJson() .AddDataAnnotations() .AddControllersAsServices() .AddApiExplorer() - .AddNewtonsoftJson(x => - { - x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer(publicUrl); - x.SerializerSettings.Converters.Add(new PeopleRoleConverter()); - }) .ConfigureApiBehaviorOptions(options => { options.SuppressMapClientErrors = true; diff --git a/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs b/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs index 6cc70187..3bb12b3a 100644 --- a/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs +++ b/src/Kyoo.Core/Views/Helper/ResourceViewAttribute.cs @@ -24,6 +24,7 @@ using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models.Attributes; +using Kyoo.Abstractions.Models.Utils; using Kyoo.Utils; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; @@ -64,13 +65,13 @@ namespace Kyoo.Core.Api type = Utility.GetGenericDefinition(type, typeof(ActionResult<>))?.GetGenericArguments()[0] ?? type; type = Utility.GetGenericDefinition(type, typeof(Page<>))?.GetGenericArguments()[0] ?? type; + context.HttpContext.Items["ResourceType"] = type.Name; + PropertyInfo[] properties = type.GetProperties() .Where(x => x.GetCustomAttribute() != null) .ToArray(); if (fields.Count == 1 && fields.Contains("all")) - { fields = properties.Select(x => x.Name).ToList(); - } else { fields = fields @@ -82,10 +83,9 @@ namespace Kyoo.Core.Api ?.Name; if (property != null) return property; - context.Result = new BadRequestObjectResult(new - { - Error = $"{x} does not exist on {type.Name}." - }); + context.Result = new BadRequestObjectResult( + new RequestError($"{x} does not exist on {type.Name}.") + ); return null; }) .ToList(); @@ -110,7 +110,7 @@ namespace Kyoo.Core.Api if (result.DeclaredType == null) return; - ILibraryManager library = context.HttpContext.RequestServices.GetService(); + ILibraryManager library = context.HttpContext.RequestServices.GetRequiredService(); ICollection fields = (ICollection)context.HttpContext.Items["fields"]; Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>)); @@ -119,13 +119,13 @@ namespace Kyoo.Core.Api foreach (IResource resource in ((dynamic)result.Value).Items) { foreach (string field in fields!) - await library!.Load(resource, field); + await library.Load(resource, field); } } else if (result.DeclaredType.IsAssignableTo(typeof(IResource))) { foreach (string field in fields!) - await library!.Load((IResource)result.Value, field); + await library.Load((IResource)result.Value, field); } } } diff --git a/src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs b/src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs new file mode 100644 index 00000000..c272e942 --- /dev/null +++ b/src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs @@ -0,0 +1,54 @@ +// Kyoo - A portable and vast media library solution. +// Copyright (c) Kyoo. +// +// See AUTHORS.md and LICENSE file in the project root for full license information. +// +// Kyoo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// Kyoo is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Kyoo. If not, see . + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +namespace Kyoo.Core.Api +{ + /// + /// The custom options of newtonsoft json. This simply add the and set + /// the . It is on a separate class to use dependency injection. + /// + public class JsonOptions : IConfigureOptions + { + /// + /// The http context accessor given to the . + /// + private readonly IHttpContextAccessor _httpContextAccessor; + + /// + /// Create a new . + /// + /// + /// The http context accessor given to the . + /// + public JsonOptions(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + /// + public void Configure(MvcNewtonsoftJsonOptions options) + { + options.SerializerSettings.ContractResolver = new JsonSerializerContract(_httpContextAccessor); + options.SerializerSettings.Converters.Add(new PeopleRoleConverter()); + } + } +} diff --git a/src/Kyoo.Core/Views/Helper/Serializers/JsonPropertyIgnorer.cs b/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs similarity index 52% rename from src/Kyoo.Core/Views/Helper/Serializers/JsonPropertyIgnorer.cs rename to src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs index 40a682a9..0fef35fc 100644 --- a/src/Kyoo.Core/Views/Helper/Serializers/JsonPropertyIgnorer.cs +++ b/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs @@ -16,27 +16,39 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . -using System; -using System.Collections; +using System.Collections.Generic; using System.Reflection; using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Utils; +using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace Kyoo.Core.Api { - public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver + /// + /// A custom json serializer that respects and + /// . It also handle via the + /// fields query parameter and items. + /// + public class JsonSerializerContract : CamelCasePropertyNamesContractResolver { - private readonly Uri _host; - private int _depth = -1; + /// + /// The http context accessor used to retrieve the fields query parameter as well as the type of + /// resource currently serializing. + /// + private readonly IHttpContextAccessor _httpContextAccessor; - public JsonPropertyIgnorer(Uri host) + /// + /// Create a new . + /// + /// The http context accessor to use. + public JsonSerializerContract(IHttpContextAccessor httpContextAccessor) { - _host = host; + _httpContextAccessor = httpContextAccessor; } + /// protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); @@ -44,19 +56,14 @@ namespace Kyoo.Core.Api LoadableRelationAttribute relation = member.GetCustomAttribute(); if (relation != null) { - if (relation.RelationID == null) - property.ShouldSerialize = x => _depth == 0 && member.GetValue(x) != null; - else + property.ShouldSerialize = _ => { - property.ShouldSerialize = x => - { - if (_depth != 0) - return false; - if (member.GetValue(x) != null) - return true; - return x.GetType().GetProperty(relation.RelationID)?.GetValue(x) != null; - }; - } + string resType = (string)_httpContextAccessor.HttpContext!.Items["ResourceType"]; + if (member.DeclaringType!.Name != resType) + return false; + ICollection fields = (ICollection)_httpContextAccessor.HttpContext!.Items["fields"]; + return fields!.Contains(member.Name); + }; } if (member.GetCustomAttribute() != null) @@ -66,24 +73,10 @@ namespace Kyoo.Core.Api // TODO use http context to disable serialize as. // TODO check https://stackoverflow.com/questions/53288633/net-core-api-custom-json-resolver-based-on-request-values - SerializeAsAttribute serializeAs = member.GetCustomAttribute(); - if (serializeAs != null) - property.ValueProvider = new SerializeAsProvider(serializeAs.Format, _host); + // SerializeAsAttribute serializeAs = member.GetCustomAttribute(); + // if (serializeAs != null) + // property.ValueProvider = new SerializeAsProvider(serializeAs.Format, _host); return property; } - - protected override JsonContract CreateContract(Type objectType) - { - JsonContract contract = base.CreateContract(objectType); - if (Utility.GetGenericDefinition(objectType, typeof(Page<>)) == null - && !objectType.IsAssignableTo(typeof(IEnumerable)) - && objectType.Name != "AnnotatedProblemDetails") - { - contract.OnSerializingCallbacks.Add((_, _) => _depth++); - contract.OnSerializedCallbacks.Add((_, _) => _depth--); - } - - return contract; - } } } diff --git a/tests/Kyoo.Tests/Kyoo.Tests.csproj b/tests/Kyoo.Tests/Kyoo.Tests.csproj index 330c41f7..c02609c5 100644 --- a/tests/Kyoo.Tests/Kyoo.Tests.csproj +++ b/tests/Kyoo.Tests/Kyoo.Tests.csproj @@ -1,12 +1,8 @@ - net5.0 default false - - Zoe Roux - SDG @@ -37,5 +33,4 @@ -