JSON에서의 역직렬화를 위한 주조 인터페이스.그물
여러 웹사이트에서 JSON 오브젝트를 받아들여(정보 스크랩을 생각해) C# 오브젝트로 번역하는 리더를 만들려고 합니다.저는 현재 JSON을 사용하고 있습니다.디시리얼라이제이션프로세스의 NET이 문제는 클래스 내의 인터페이스레벨 속성을 처리하는 방법을 모른다는 것입니다.그래서 어떤 성질의 것:
public IThingy Thing
다음 오류가 발생합니다.
ITingy 형식의 인스턴스를 만들 수 없습니다.Type은 인터페이스 또는 추상 클래스이므로 인스턴스화할 수 없습니다.
작업하고 있는 코드는 기밀성이 높고 유닛 테스트가 매우 중요하기 때문에 Thingy가 아닌 IThingy로 하는 것이 비교적 중요합니다.Thingy와 같은 완전한 오브젝트에서는 원자 테스트 스크립트의 오브젝트를 조롱할 수 없습니다.인터페이스여야 합니다.
JSON에 대해 곰곰이 생각해봤어지금까지의 NET의 문서나, 이 사이트에서 볼 수 있는 질문은 모두 1년 이상 전의 것입니다.도움이 필요하신가요?
또, 문제가 되는 경우는, 에 제 앱이 기재되어 있습니다.NET 4.0.
@SamualDavis는 관련 질문에서 훌륭한 솔루션을 제공했습니다.이것에 대해서, 이하에 정리하겠습니다.
JSON 스트림을 인터페이스 속성이 있는 콘크리트 클래스로 역직렬화해야 하는 경우 클래스의 생성자에 대한 매개 변수로 콘크리트 클래스를 포함할 수 있습니다.Newton Soft의 디시리얼라이저는 특성을 디시리얼라이즈하기 위해 이러한 구체적인 클래스를 사용할 필요가 있다는 것을 충분히 이해할 수 있습니다.
다음은 예를 제시하겠습니다.
public class Visit : IVisit
{
/// <summary>
/// This constructor is required for the JSON deserializer to be able
/// to identify concrete classes to use when deserializing the interface properties.
/// </summary>
public Visit(MyLocation location, Guest guest)
{
Location = location;
Guest = guest;
}
public long VisitId { get; set; }
public ILocation Location { get; set; }
public DateTime VisitDate { get; set; }
public IGuest Guest { get; set; }
}
왜컨컨 를용? ??? 용??에는 고유의 기능이 .Newtonsoft.Json하려면: 「」를 참조해 주세요.
★★TypeNameHandling JsonSerializerSettings로로 합니다.TypeNameHandling.Auto
JsonConvert.SerializeObject(
toSerialize,
new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
});
이렇게 하면 모든 유형이 json에 추가됩니다. json은 구체적인 유형의 인스턴스가 아니라 인터페이스 또는 추상 클래스로서 보유됩니다.
시리얼라이제이션과 시리얼 해제에 같은 설정을 사용하고 있는 것을 확인합니다.
시험해 봤는데, 마법처럼 작동하더군요. 목록에도요.
사이트 링크가 있는 검색 결과 웹 결과
⚠경고:
이 값은 알려진 신뢰할 수 있는 소스의 json에만 사용하십시오.사용자가 snipsnipsnip을 올바르게 언급하고 있습니다.이거는 정말 취약성입니다.
자세한 내용은 CA2328 및 SCS0028을 참조하십시오.
소스 및 대체 수동 구현:블로그 내부 코드
착신 JSON을 제어할 수 없는 경우(따라서 $type 속성을 포함할 수 없는 경우) 구체적인 유형을 명시적으로 지정할 수 있는 커스텀 컨버터를 작성했습니다.
public class Model
{
[JsonConverter(typeof(ConcreteTypeConverter<Something>))]
public ISomething TheThing { get; set; }
}
이것은 Json의 기본 시리얼라이저 구현만 사용합니다.구체적인 유형을 명시적으로 지정하면서 그물망.
개요는 이 블로그 포스트에서 확인할 수 있습니다.소스 코드는 다음과 같습니다.
public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
//assume we can convert to anything for now
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//explicitly specify the concrete type we want to create
return serializer.Deserialize<TConcrete>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//use the default serialization - it works fine
serializer.Serialize(writer, value);
}
}
추상 유형을 실제 유형에 매핑하려면 이 클래스를 사용합니다.
public class AbstractConverter<TReal, TAbstract>
: JsonConverter where TReal : TAbstract
{
public override Boolean CanConvert(Type objectType)
=> objectType == typeof(TAbstract);
public override Object ReadJson(JsonReader reader, Type type, Object value, JsonSerializer jser)
=> jser.Deserialize<TReal>(reader);
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer jser)
=> jser.Serialize(writer, value);
}
역직렬화 시:
var settings = new JsonSerializerSettings
{
Converters = {
new AbstractConverter<Thing, IThingy>(),
new AbstractConverter<Thing2, IThingy2>()
},
};
JsonConvert.DeserializeObject(json, type, settings);
인터페이스의 복수 실장의 역직렬화를 netable로 하려면 , Json Converter 를 사용할 수 있습니다만, 어트리뷰트를 개입시켜서는 사용할 수 없습니다.
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Converters.Add(new DTOJsonConverter());
Interfaces.IEntity entity = serializer.Deserialize(jsonReader);
DTOJson Converter 는, 각 인터페이스를 구체적인 실장으로 매핑 합니다.
class DTOJsonConverter : Newtonsoft.Json.JsonConverter
{
private static readonly string ISCALAR_FULLNAME = typeof(Interfaces.IScalar).FullName;
private static readonly string IENTITY_FULLNAME = typeof(Interfaces.IEntity).FullName;
public override bool CanConvert(Type objectType)
{
if (objectType.FullName == ISCALAR_FULLNAME
|| objectType.FullName == IENTITY_FULLNAME)
{
return true;
}
return false;
}
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
if (objectType.FullName == ISCALAR_FULLNAME)
return serializer.Deserialize(reader, typeof(DTO.ClientScalar));
else if (objectType.FullName == IENTITY_FULLNAME)
return serializer.Deserialize(reader, typeof(DTO.ClientEntity));
throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
}
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
DTOJsonConverter가 필요한 것은 디시리얼라이저뿐입니다.시리얼화 프로세스는 변경되지 않습니다.Json 개체는 구체적인 유형 이름을 포함할 필요가 없습니다.
이 SO 포스트에서는 범용 Json Converter에서 한 단계 더 나아가 동일한 솔루션을 제공합니다.
Nicholas Westby는 멋진 기사에서 훌륭한 해결책을 제시했습니다.
다음과 같은 인터페이스를 실장할 수 있는 많은 클래스 중 하나로 JSON을 역직렬화하는 경우:
public class Person
{
public IProfession Profession { get; set; }
}
public interface IProfession
{
string JobTitle { get; }
}
public class Programming : IProfession
{
public string JobTitle => "Software Developer";
public string FavoriteLanguage { get; set; }
}
public class Writing : IProfession
{
public string JobTitle => "Copywriter";
public string FavoriteWord { get; set; }
}
public class Samples
{
public static Person GetProgrammer()
{
return new Person()
{
Profession = new Programming()
{
FavoriteLanguage = "C#"
}
};
}
}
커스텀 JSON 컨버터를 사용할 수 있습니다.
public class ProfessionConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IProfession);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var profession = default(IProfession);
switch (jsonObject["JobTitle"].Value())
{
case "Software Developer":
profession = new Programming();
break;
case "Copywriter":
profession = new Writing();
break;
}
serializer.Populate(jsonObject.CreateReader(), profession);
return profession;
}
}
커스텀 컨버터를 사용하는 것을 알리기 위해서, 「Profession」속성을 JsonConverter 속성으로 장식할 필요가 있습니다.
public class Person
{
[JsonConverter(typeof(ProfessionConverter))]
public IProfession Profession { get; set; }
}
그런 다음 인터페이스를 사용하여 클래스를 캐스팅할 수 있습니다.
Person person = JsonConvert.DeserializeObject<Person>(jsonString);
난 이게 유용하다고 생각했어.너도 그럴 거야.
사용 예
public class Parent
{
[JsonConverter(typeof(InterfaceConverter<IChildModel, ChildModel>))]
IChildModel Child { get; set; }
}
커스텀 생성 컨버터
public class InterfaceConverter<TInterface, TConcrete> : CustomCreationConverter<TInterface>
where TConcrete : TInterface, new()
{
public override TInterface Create(Type objectType)
{
return new TConcrete();
}
}
다음 두 가지 방법을 시도해 보십시오.
테스트/파스 모델 구현:
public class Organisation {
public string Name { get; set; }
[JsonConverter(typeof(RichDudeConverter))]
public IPerson Owner { get; set; }
}
public interface IPerson {
string Name { get; set; }
}
public class Tycoon : IPerson {
public string Name { get; set; }
}
public class Magnate : IPerson {
public string Name { get; set; }
public string IndustryName { get; set; }
}
public class Heir: IPerson {
public string Name { get; set; }
public IPerson Benefactor { get; set; }
}
public class RichDudeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IPerson));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// pseudo-code
object richDude = serializer.Deserialize<Heir>(reader);
if (richDude == null)
{
richDude = serializer.Deserialize<Magnate>(reader);
}
if (richDude == null)
{
richDude = serializer.Deserialize<Tycoon>(reader);
}
return richDude;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Left as an exercise to the reader :)
throw new NotImplementedException();
}
}
또는 객체 모델에서 이를 수행할 수 있는 경우 IPerson과 리프 객체 사이에 구체적인 기본 클래스를 구현하고 해당 클래스로 역직렬화합니다.
첫 번째 방법은 실행 시 오류가 발생할 수 있으며 두 번째 방법은 개체 모델을 변경해야 하며 출력을 가장 낮은 공통 분모로 균질화합니다.
Concrete List에 대해 궁금하신 분들을 위해Oliver가 참조한 TypeConverter는 다음과 같습니다.
public class ConcreteListTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var res = serializer.Deserialize<List<TImplementation>>(reader);
return res.ConvertAll(x => (TInterface) x);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
인터페이스는 모두 정의상 추상적이기 때문에 어떤 객체도 IT힌지가 될 수 없습니다.
처음 직렬화된 개체는 추상 인터페이스를 구현하는 구체적인 유형입니다.이 구체적인 클래스에서 직렬화된 데이터를 복원해야 합니다.
그 결과 오브젝트는 찾고 있는 추상 인터페이스를 구현하는 타입이 됩니다.
이 매뉴얼을 참조해 주십시오.
(Thingy)JsonConvert.DeserializeObject(jsonString, typeof(Thingy));
디시리얼라이즈시에 JSON에게 통지합니다.콘크리트 타입에 관한 NET.
어쨌든, 대부분의 경우 이 일은 내가 직접 처리해야 했다.각 오브젝트에는 Diserialize(string jsonStream) 메서드가 있습니다.몇 가지 예를 들어보겠습니다.
JObject parsedJson = this.ParseJson(jsonStream);
object thingyObjectJson = (object)parsedJson["thing"];
this.Thing = new Thingy(Convert.ToString(thingyObjectJson));
이 경우 새로운 Thingy(string)는 적절한 콘크리트 유형의 Diserialize(string json Stream) 메서드를 호출하는 컨스트럭터입니다.이 스킴은 json의 기준점에 도달할 때까지 하향 및 하향으로 계속됩니다.NET은 처리만 하면 됩니다.
this.Name = (string)parsedJson["name"];
this.CreatedTime = DateTime.Parse((string)parsedJson["created_time"]);
기타 등등.이 설정을 통해 json을 얻을 수 있었습니다.라이브러리 자체의 대부분을 리팩터링할 필요 없이 처리할 수 있는 NET 셋업이나 관련된 오브젝트의 수 때문에 라이브러리 전체에 장애가 발생했을 가능성이 있는 다루기 어려운 트라이/파스 모델을 사용할 필요가 없습니다.또한 특정 객체에 대한 json 변경을 효과적으로 처리할 수 있으며 객체가 접촉하는 모든 것에 대해 걱정할 필요가 없습니다.이 솔루션은 결코 이상적인 솔루션은 아니지만 유닛과 통합 테스트에서 상당히 잘 작동합니다.
다음과 같은 자동 팩 설정을 가정합니다.
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
// use Autofac to create types that have been registered with it
if (_container.IsRegistered(objectType))
{
contract.DefaultCreator = () => _container.Resolve(objectType);
}
return contract;
}
}
그럼, 다음과 같은 수업이 있다고 가정해 봅시다.
public class TaskController
{
private readonly ITaskRepository _repository;
private readonly ILogger _logger;
public TaskController(ITaskRepository repository, ILogger logger)
{
_repository = repository;
_logger = logger;
}
public ITaskRepository Repository
{
get { return _repository; }
}
public ILogger Logger
{
get { return _logger; }
}
}
따라서 디시리얼라이제이션에서의 리졸바 사용은 다음과 같습니다.
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<TaskRepository>().As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogService(new DateTime(2000, 12, 12))).As<ILogger>();
IContainer container = builder.Build();
AutofacContractResolver contractResolver = new AutofacContractResolver(container);
string json = @"{
'Logger': {
'Level':'Debug'
}
}";
// ITaskRespository and ILogger constructor parameters are injected by Autofac
TaskController controller = JsonConvert.DeserializeObject<TaskController>(json, new JsonSerializerSettings
{
ContractResolver = contractResolver
});
Console.WriteLine(controller.Repository.GetType().Name);
자세한 것은, http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm 를 참조해 주세요.
이 문제에 대한 저의 해결책은 다음과 같습니다.이 솔루션은 매우 일반적이기 때문에 마음에 듭니다.
/// <summary>
/// Automagically convert known interfaces to (specific) concrete classes on deserialisation
/// </summary>
public class WithMocksJsonConverter : JsonConverter
{
/// <summary>
/// The interfaces I know how to instantiate mapped to the classes with which I shall instantiate them, as a Dictionary.
/// </summary>
private readonly Dictionary<Type,Type> conversions = new Dictionary<Type,Type>() {
{ typeof(IOne), typeof(MockOne) },
{ typeof(ITwo), typeof(MockTwo) },
{ typeof(IThree), typeof(MockThree) },
{ typeof(IFour), typeof(MockFour) }
};
/// <summary>
/// Can I convert an object of this type?
/// </summary>
/// <param name="objectType">The type under consideration</param>
/// <returns>True if I can convert the type under consideration, else false.</returns>
public override bool CanConvert(Type objectType)
{
return conversions.Keys.Contains(objectType);
}
/// <summary>
/// Attempt to read an object of the specified type from this reader.
/// </summary>
/// <param name="reader">The reader from which I read.</param>
/// <param name="objectType">The type of object I'm trying to read, anticipated to be one I can convert.</param>
/// <param name="existingValue">The existing value of the object being read.</param>
/// <param name="serializer">The serializer invoking this request.</param>
/// <returns>An object of the type into which I convert the specified objectType.</returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
return serializer.Deserialize(reader, this.conversions[objectType]);
}
catch (Exception)
{
throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
}
}
/// <summary>
/// Not yet implemented.
/// </summary>
/// <param name="writer">The writer to which I would write.</param>
/// <param name="value">The value I am attempting to write.</param>
/// <param name="serializer">the serializer invoking this request.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
Type Dictionary 인수를 사용하는 생성자를 추가함으로써 더 일반적인 변환기로 변환할 수 있습니다.변환 인스턴스 변수를 인스턴스화하는 데 사용하는 <Type, Type>.
몇 년 후, 나는 비슷한 문제가 있었다.내 경우, 범용 클래스에서 동작하도록 런타임에 구체적인 클래스를 생성하기 위한 설정이 많이 중첩된 인터페이스와 함께 사용할 수 있습니다.
런타임에 Newtonsoft가 반환한 오브젝트를 랩하는 프록시 클래스를 만들기로 했습니다.
이 어프로치의 장점은 클래스의 구체적인 실장이 필요 없고, 네스트된 인터페이스의 깊이를 자동적으로 처리할 수 있다는 것입니다.자세한 내용은 제 블로그에서 확인하실 수 있습니다.
using Castle.DynamicProxy;
using Newtonsoft.Json.Linq;
using System;
using System.Reflection;
namespace LL.Utilities.Std.Json
{
public static class JObjectExtension
{
private static ProxyGenerator _generator = new ProxyGenerator();
public static dynamic toProxy(this JObject targetObject, Type interfaceType)
{
return _generator.CreateInterfaceProxyWithoutTarget(interfaceType, new JObjectInterceptor(targetObject));
}
public static InterfaceType toProxy<InterfaceType>(this JObject targetObject)
{
return toProxy(targetObject, typeof(InterfaceType));
}
}
[Serializable]
public class JObjectInterceptor : IInterceptor
{
private JObject _target;
public JObjectInterceptor(JObject target)
{
_target = target;
}
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
if(invocation.Method.IsSpecialName && methodName.StartsWith("get_"))
{
var returnType = invocation.Method.ReturnType;
methodName = methodName.Substring(4);
if (_target == null || _target[methodName] == null)
{
if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
{
invocation.ReturnValue = null;
return;
}
}
if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
{
invocation.ReturnValue = _target[methodName].ToObject(returnType);
}
else
{
invocation.ReturnValue = ((JObject)_target[methodName]).toProxy(returnType);
}
}
else
{
throw new NotImplementedException("Only get accessors are implemented in proxy");
}
}
}
}
사용방법:
var jObj = JObject.Parse(input);
InterfaceType proxyObject = jObj.toProxy<InterfaceType>();
이 JsonKnown 사용타입은 매우 유사한 방법으로 json에 식별자를 추가합니다.
[JsonConverter(typeof(JsonKnownTypeConverter<Interface1>))]
[JsonKnownType(typeof(MyClass), "myClass")]
public interface Interface1
{ }
public class MyClass : Interface1
{
public string Something;
}
이제 json에서 개체를 직렬화할 때 추가됩니다."$type"와 함께"myClass"값 및 이 값은 역직렬화에 사용됩니다.
Json:
{"Something":"something", "$type":"derived"}
내 솔루션은 컨스트럭터에 인터페이스 요소를 추가했습니다.
public class Customer: ICustomer{
public Customer(Details details){
Details = details;
}
[JsonProperty("Details",NullValueHnadling = NullValueHandling.Ignore)]
public IDetails Details {get; set;}
}
또, 외부 라이브러리가 필요 없는 커스텀 TextInputFormatter 를 사용할 수도 있습니다.또, 어떠한 타입의 데이터라도 시리얼화를 처리할 수 있는(탈퇴할 수 있는) 방법에 대해서도, 통찰을 얻을 수 있습니다.
public class MyInputTypeFormatter : TextInputFormatter
{
public MyInputTypeFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
SupportedEncodings.Add(Encoding.UTF8);
}
protected override bool CanReadType(Type type)
{
return type == typeof(MyClass);
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
var httpContext = context.HttpContext;
var serviceProvider = httpContext.RequestServices;
var logger = serviceProvider.GetRequiredService<ILogger<ImageTypeConverter>>();
using var reader = new StreamReader(httpContext.Request.Body, encoding);
{
var data = await reader.ReadToEndAsync();
if (data.Contains("Hello"))
{
var myClass= new MyClass(data);
return await InputFormatterResult.SuccessAsync(myClass);
}
else
{
return await InputFormatterResult.FailureAsync();
}
}
}
}
그런 다음 이 입력 포메터를 입력 포메터 목록에 추가합니다.
services.AddControllers(options=> {
options.InputFormatters.Insert(0, new MyInputFormatter());
});
여기서 0은 모델 바인딩 시 호출되는 첫 번째 입력 포메터임을 의미합니다.
일이 많은 것 같지만 대부분은 그냥 보일러판이에요.이게 어떻게 작동하는지 설명할게요
MyClass 유형의 매개 변수를 가진 액션 메서드/루트가 있습니다.요청이 발생하면 입력 포맷자의 CanReadType이 호출되고 역직렬화를 처리한다는 의미의 true가 반환됩니다.다음으로 ReadRequestBodyAsync 메서드가 호출되어 요청 데이터가 제공됩니다.
데이터로 원하는 작업을 수행할 수 있으며 역직렬화가 성공하면 MyClass 유형의 개체를 반환합니다.그렇지 않으면 그냥 실패를 돌려주게 됩니다.
디시리얼라이제이션에서는
using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
// ...
}
입력이 json 객체로 해석된 후 DOM으로 유지될 때 요소를 통과할 수 있습니다.그런 다음 포함된 내용을 확인하고 데이터를 사용하여 수동으로 클래스를 만들고 인터페이스를 클래스로 변환할 수 있습니다.
주의: Json Document는 에서 도입되었습니다.Net 3.1 사용방법은 이쪽에서 확인하실 수 있습니다.
TextInputFormatter 및 TextOutputFormatter 사용 방법에 대한 자세한 내용은 커스텀 입력 포맷터를 사용하면 여러 인터페이스를 사용할 수 있는 커스텀클래스를 일원적으로 처리할 수 있다는 장점이 있습니다.또한 입력 데이터 처리를 세밀하게 제어할 수 있습니다.
언급URL : https://stackoverflow.com/questions/5780888/casting-interfaces-for-deserialization-in-json-net
'source' 카테고리의 다른 글
| LiquiBase 문제 , 클래스 경로 리소스 [db/changelog/db.changelog-master.yaml]이(가) 없으므로 URL로 해결할 수 없습니다. (0) | 2023.03.18 |
|---|---|
| create-react-app을 사용하여 새로운 React 컴포넌트를 작성하는 최선의 방법 (0) | 2023.03.18 |
| AJAX 요청으로 쿠키 값을 설정하려면 어떻게 해야 합니까? (0) | 2023.03.13 |
| WordPress API 오류: "죄송합니다. 새 게시물을 만들 수 없습니다." (0) | 2023.03.13 |
| AngularJS: 서비스 vs 프로바이더 vs 팩토리 (0) | 2023.03.13 |