darcane
darcane

Reputation: 483

How to envelope web api response conventionally int .netcore api

One of my web api project -uses .NetCore 2.1- i want to have a convention of returning messages. All successful result objects must be enveloped with a result object and all errors and such things must leave result null and fill the resultCode and resultMessage fields. Should i do this on startup.cs with formatters or create a response builder class and not use [ApiController] attribute tag features such as Ok(), NotFound() etc.. which i really want to use. I could not find any documentation or guideline for this purpose. Here is my code:

[HttpGet]
[Produces("applicaiton/json")]
public IActionResult GetValues()
{
    var toRet = new[] {"value1", "value2"};
    return Ok(toRet);
}

This codeblock returns ["value1","value2"] but i need it like this

{
    "result": ["value1","value2"],
    "resultCode": 200,
    "resultMessage": "OK"
}

How and where can i achieve this convention?

Edit

Excuse my on my example code. I will use custom error codes in my responses. Most probably 200 OK will be

{
    "result" : "some value",
    "resultCode" : 0,
    "resultMessage" : "All values returned"
}

And 400 Bad Request will be

{
    "result" : null,
    "resultCode" : 20468,
    "resultMessage" : "Dont have any value to show!"
}

Upvotes: 2

Views: 2087

Answers (2)

spender
spender

Reputation: 120480

Say you defined a new JsonOutputFormatter with an overridden WriteResponseBodyAsync:

public class SuperJsonOutputFormatter:JsonOutputFormatter
{
    public SuperJsonOutputFormatter(
        JsonSerializerSettings serializerSettings, 
        ArrayPool<char> charPool) : base(serializerSettings, charPool)
    {
    }
    public override async Task WriteResponseBodyAsync(
        OutputFormatterWriteContext context, 
        Encoding selectedEncoding)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));
        if (selectedEncoding == null)
            throw new ArgumentNullException(nameof(selectedEncoding));
        using (TextWriter writer = 
            context.WriterFactory(
                context.HttpContext.Response.Body, 
                selectedEncoding))
        {
            var rewrittenValue = new
            {
                result = context.Object, 
                resultCode = context.HttpContext.Response.StatusCode, 
                resultMessage =
                    ((HttpStatusCode) context.HttpContext.Response.StatusCode)
                        .ToString()
            };
            this.WriteObject(writer, rewrittenValue);
            await writer.FlushAsync();
        }
    }
}

You could de-register the old JsonOutputFormatter and replace it with your new-spanky formatter in Startup.ConfigureServices as follows:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(opts =>
    {
        var oldFormatter = 
            opts.OutputFormatters.OfType<JsonOutputFormatter>().Single();
        opts.OutputFormatters.Remove(oldFormatter);
        var replacementJsonOutputFormatter = 
            new SuperJsonOutputFormatter(oldFormatter.PublicSerializerSettings,
            ArrayPool<char>.Shared);
        opts.OutputFormatters.Add(replacementJsonOutputFormatter);
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

...and keep the code in your controllers exactly the same as before.

Of course, if (for instance) the client only accepts XML, then content-negotiation will skip this formatter. If you only want JSON output, deregister any other output formatters.

Upvotes: 1

Sefe
Sefe

Reputation: 14007

The status code 200 OK refers to the HTTP status code, which is part of the HTTP protocol. The status code is part of the HTTP header, whereas your result is part of the content.

Since this is part of the HTTP protocol it is universally supported and there is little reason for you to duplicate this information in your message. The other side of the HTTP connection will be able to extract this information from the header. It will also have already an infrastructure to handle the status code, like a web browser that will automatically redirect after a 3xx response and show an error page on a 4xx response.

All this is also true for a REST service like the one you are building. REST is based on HTTP and so there is nothing wrong to use the features of this protocol. So I suggest you think if you really need this information in the content.

If you still want this information as part of the message content, you can manually add it yourself:

public class ResultMessage<T> {
    public T Result { get; set; }
    public HttpStatusCode ResultCode { get; set; }
    public string ResultMessage { get; set; }
}

ResultMessage<T> GetResultMessage<T>(T value, HttpStatusCode resultCode) {
    return new ResultMessage<T> {
        Result = value,
        ResultCode = resultCode,
        ResultMessage = resultCode.ToString()
    };
}

[HttpGet]
[Produces("application/json")]
public IActionResult GetValues()
{
    var toRet = new[] {"value1", "value2"};
    return Ok(GetResultMessage(toRet, HttpStatusCode.OK));
}

Upvotes: 0

Related Questions