How to display parameter in Web API help page documentation for Response only?
Asked Answered
G

2

7

I've configured Web API help page documentation.

I am having below class which would be inherited in other model classes.

public class ResponseBase
{
    public string ErrorReason { get; set; }
    [IgnoreDataMember]
    public bool IsRejected { get; set; }
}

I don't want IsRejected to be serialized and available in response so I decorated it with IgnoreDataMember attribute.

Example of model class that inherits ResponseBase.

public class Reading : ResponseBase
{
    //Other properties
}

I've below method in my Web API Controller:

[HttpPost]
[ValidationResponseFilter]
[Route("")]
[ResponseType(typeof(Reading))]
public IHttpActionResult Add(List<Reading> readingList)
{
    //Logic here
}

Now for the documentation of request parameter which is list of Reading objects, it will list down all the properties of Reading along with ResponseBase class(inheritance). I want help page documentation to list down ErrorReason as one of the parameter in Response only.

Is there any configuration I can do other than setting attribute [ApiExplorerSettings(IgnoreApi = true)] on ErrorReason? If I do that, ErrorReason will not be available in both Request and Response Parameters. I want to display it in Response Parameters list only.

Gerrygerrymander answered 19/2, 2016 at 12:14 Comment(7)
An advice is to code a Representation of the model without IsRejected property. You can avoid tedious property mapping with automapper. Is a good design choice not to return or receive domain models. Instead of this, return and receive representations. List<ReadingRepresentation> readingListTroat
I'm using Automapper to map properties and IsRejected property is decorated with IgnoreDataMember, hence it won't be serialized. This class is only DTO class and not business object. Does it make sense?Gerrygerrymander
You need to code a specific representation. You have a requirement that is IsRejected must not be sent from user interface. That is, the user can't change this property. The only way, and more secure way to this is do a ReadingWriteRepresentation that has not this property. Don't be worried about create more files if this makes your code consistent.Troat
You mean to say that there's no out of the box solution. i.e setting attribute of property that allows it to be visible (listed in web api help page ) during Request and not during reponse If I use the ResponseBase class as base class.Gerrygerrymander
Not, I mean you thought in incorrect way to get this. You have an architecture / design problem. But you are trying to solve an odd behaviour in the help page. I advice you to program well instead of waste your time solving odd things.Troat
So there's no way I can mark any property as an optional. I had already implemented other approach that is Derive class from Reading and have ErrorReason and IsRejected property and it works. But in that case I need to have Response class having these two properties for each and every model I have in my project. :(Gerrygerrymander
You can define your own respone sample. eg SampleProductOfferingResponseLong which returns a string. That way you can make it have something more meaningful rather than the plain old "A String" private static void SetCustomSampleRersponsesAndRequests(HttpConfiguration config) { //Sync Versions //application/json config.SetSampleResponse(myResponseAndRequestSamples.SampleProductOfferingResponseLong(), new MediaTypeHeaderValue("application/json"), "ControllerName", "ActionName");Engedi
E
0

Generally, the best practice is to create a custom object that matches exactly what you want to accept. One of the reasons for this is security. See 'over-posting' and 'under-posting' : http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api To do this you would create a new object with only the properties you need.

public class ResponseBaseVM
    {
        public string ErrorReason { get; set; }
        /*public bool IsRejected { get; set; }*/ 
    }
public class ReadingVM : ResponseBaseVM
    {
        //Other properties that you only want available to user
    }

Then you would take in List of ReadingVM but the response type would still be typeof(reading)

[HttpPost]
[ValidationResponseFilter]
[Route("")]
[ResponseType(typeof(List<Reading>))] //will still display response with IsRejected
public IHttpActionResult Add(List<ReadingVM> readingListVM)
{
    //Logic here
}

--- Work Around ---

Again, I think you should follow the method above. You asked for a work around and here it is. Find your 'GenerateApiModel' method in the HelpPageConfigurationExtensions.cs class and repalce it with this:

private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
{
    HelpPageApiModel apiModel = new HelpPageApiModel()
    {
        ApiDescription = apiDescription,
    };

    ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator();
    HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
    GenerateUriParameters(apiModel, modelGenerator);
    GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
    GenerateResourceDescription(apiModel, modelGenerator);
    GenerateSamples(apiModel, sampleGenerator);

    //This will remove request body parameters from your Api Help Page matching 'IsRejected'
    var isRejectedParameter = apiModel.RequestBodyParameters.SingleOrDefault(x => x.Name == "IsRejected");
    if (isRejectedParameter != null)
        apiModel.RequestBodyParameters.Remove(isRejectedParameter);

    //This will remove elements with 'IsRejected' for the Help Page sample requests 
    var sampleRequests = new Dictionary<MediaTypeHeaderValue, object>();
    foreach (var kvp in apiModel.SampleRequests)
    {
        //1.) iterate through each object in SampleRequests dictionary.
        //2.) modify the json or xml to remove the "IsRejected" elements
        //3.) assign modified results to a new dictionary
        //4.) change the HelpPageApiModel. SampleRequests setter to be not private
        //5.) assign new dictionary to HelpPageApiModel.SampleRequests
        if (Equals(kvp.Key, new MediaTypeHeaderValue("application/json")))
        {
            var jObject = JObject.Parse(kvp.Value.ToString());
            jObject.Remove("IsRejected");
            sampleRequests.Add(new MediaTypeHeaderValue("application/Json"), jObject.ToString());
        }
        else if(Equals(kvp.Key, new MediaTypeHeaderValue("text/json")))
        {
            //do stuff
        }
        else if (Equals(kvp.Key, new MediaTypeHeaderValue("application/xml")))
        {
            //do stuff
        }
        else if (Equals(kvp.Key, new MediaTypeHeaderValue("text/xml")))
        {
            //do stuff
        }
        else
        {
            //form urlencoded or others
            sampleRequests.Add(kvp.Key,kvp.Value);
        }
    }
    apiModel.SampleRequests = sampleRequests;

    return apiModel;
}

Now as you can see 'IsRejected' exists in the Response, but not the Reqest. Request Help Page override

Equilateral answered 27/2, 2016 at 16:49 Comment(2)
I know hot to omit any property from being returned in response. That is why I used IgnoreDataMember for IsRejected. It works for both XML and Json. What I want is if I'm going with the approach I mentioned, I don't want ErrorReason to be displayed in Request in Web API Help page documentation. I hope it makes sense.Gerrygerrymander
@NileshThakkar I updated my answer to include a specific workaround for your situation.Equilateral
N
0

I suggest you use HTTP Status Codes as a way of conveying success or failure, such as they are meant to, unless there's a specific reason you want to return the errors as a HTTP Status Code 200 (OK) instead.

The general way of dealing with such a scenario is to return 2xx-status codes when everything is ok, and 4xx or 5xx status codes when an error occurred. This way the client can clearly distinguish error situations from successful operations.

A good blog post on the subject on error handling can be found on Exception Not Found -blog, from where this good example of a controller action is taken:

[Route("CheckId/{id}")]
[HttpGet]
public IHttpActionResult CheckId(int id)  
{
    if (id > 100)
    {
        var message = new HttpResponseMessage(HttpStatusCode.BadRequest)
        {
            Content = new StringContent("We cannot use IDs greater than 100.")
        };
        throw new HttpResponseException(message);
    }
    return Ok(id);
}
Natachanatal answered 5/9, 2016 at 10:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.