Как я могу сделать POST в .NET Core 2.2, если содержимое отформатировано в XML? - PullRequest
0 голосов
/ 14 февраля 2019

Я действительно удивлен этой проблемой, потому что помню, как преуспел в ней в более ранней версии .NET CoreЯ работаю над приложением .NET Core 2.2, которое теперь должно вызываться другим приложением (разработанным извне), которое может публиковать только xml ....

Это мой метод ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddXmlSerializerFormatters();
}

Это мой контроллер:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // POST api/values
    [HttpPost]
    public ActionResult<object> Post([FromBody] object value)
    {
        return ("Hi", "Hi2");
    }
}

Следующие запросы вызывают ответ с кодом состояния 200:

POST http://localhost:58774/api/values
Content-Type: application/json
Accept: application/xml
User-Agent: vscode-restclient
Accept-Encoding: gzip

{"a":5}

, который дает мне xml в качестве ответа и

POST http://localhost:58774/api/values
Content-Type: application/json
Accept: application/json
User-Agent: vscode-restclient
Accept-Encoding: gzip

{"a":5}

, который дает JSON в качестве ответа.

Однако этот вызов приводит к ответу с кодом состояния 500 (что является именно моей проблемой):

POST http://localhost:58774/api/values
Content-Type: application/xml
Accept: application/xml
User-Agent: vscode-restclient
Accept-Encoding: gzip

<A a="5"/>

Так что теперь у меня проблемы.Работает форматирование XML, которое становится понятным, если я принимаю в качестве типа вывода.Однако, если я опубликую его как Content-Type и протестирую его, я получу 500. Я также попробовал этот (старый) подход , но, похоже, он не работает.NET Core 2.2.Что я делаю неправильно?Как я могу опубликовать свой xml в .net core 2.2?

Обновление после полезного замечания. Вот исключение, вызывающее 500:

Система.InvalidOperationException: в XML-документе произошла ошибка (1, 11).---> System.InvalidOperationException: не ожидалось.

Однако, если я добавлю xmlns (на основе this ), у меня все равно будет 500:

POST http://localhost:5000/api/values
Content-Type: application/xml
Accept: application/xml
User-Agent: vscode-restclient
Accept-Encoding: gzip

<f:table xmlns:f="https://www.w3schools.com/furniture">
  <f:name>African Coffee Table</f:name>
  <f:width>80</f:width>
  <f:length>120</f:length>
</f:table>

Тогда сообщение об исключении:

System.InvalidOperationException: в XML-документе произошла ошибка (1, 56).---> System.InvalidOperationException: https://www.w3schools.com/furniture'> не ожидалось.

Возможно, мне нужно изменить мой xml.Как?Даже пример из w3cschools мне не помогает.

1 Ответ

0 голосов
/ 14 февраля 2019

Для Content-Type: application/json с {"a":5} вы получите { "a": 5 } на стороне сервера.Он получил простой текст.

Для Content-Type: application/xml с <A a="5"/>, если вы предпочитаете получать <A a="5" />, вы можете реализовать пользовательский XDocumentInputFormatter, такой как

public class XDocumentInputFormatter : InputFormatter, IInputFormatter, IApiRequestFormatMetadataProvider
{
    public XDocumentInputFormatter()
    {
        SupportedMediaTypes.Add("application/xml");
    }

    protected override bool CanReadType(Type type)
    {
        if (type.IsAssignableFrom(typeof(XDocument))) return true;
        return base.CanReadType(type);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var xmlDoc = await XDocument.LoadAsync(context.HttpContext.Request.Body, LoadOptions.None, CancellationToken.None);

        return InputFormatterResult.Success(xmlDoc);
    }
}

Зарегистрируйте его в Startup.cs

services.AddMvc(config =>
        {
            config.InputFormatters.Insert(0, new XDocumentInputFormatter());
        })
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
        .AddXmlSerializerFormatters();

По умолчанию для XmlSerializer нам необходимо предоставить Type type, тип объекта не сможет десериализоваться.

Если тип object value определен как

public class A
{
    public int a { get; set; }
}

Вы можете изменить свой метод как

public ActionResult<object> Post([FromBody] A value)
{
    return new A { a = 1 };//("Hi", "Hi2");
}

И с запросом как

<A>
    <a>1</a>
</A>

заполнит значение объектом класса А.

...