Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example for GET request query string parameters #66

Closed
m-demydiuk opened this issue Nov 3, 2018 · 19 comments
Closed

Example for GET request query string parameters #66

m-demydiuk opened this issue Nov 3, 2018 · 19 comments

Comments

@m-demydiuk
Copy link

Hello.

I have GET request with 2 query string parameters From and To. I created request example like:

public class RequestExample : IExamplesProvider<Request>
    {
        public Request GetExamples()
        {
            return new Request
            {
                From = "USD",
                To = "AUD"
            };
        }
    }

But I do not see this example in the swagger. Instead I only see 2 input fields in the UI. Is it possible to show request example for query params?

Thanks in advance!

@mattfrear
Copy link
Owner

mattfrear commented Nov 3, 2018 via email

@mattfrear
Copy link
Owner

Thank you for raising the issue, I have added it to the list of Known issues in the Readme.

@spottedmahn
Copy link
Contributor

Hi @mattfrear 👋 - can you expand upon:

Hi, no it’s not possible for GETs.

Where is the limitation?

@spottedmahn
Copy link
Contributor

And there must be a workaround 🤔?

@mattfrear
Copy link
Owner

mattfrear commented Dec 13, 2018 via email

@mattfrear
Copy link
Owner

Off the top of my head, I think you can set an example for a parameter in a get, and it will be there in the swagger JSON, but swagger-ui will not display it. (I think, but I'm not sure, need to check that)

OK, I've dug a bit deeper, and what I said earlier was incorrect. The limitation for request examples is that examples can only be given for parameters that are in the request body (i.e. not in the query string). This is because the Swagger 2.0 spec says that:

If in is "body":

Field Name Type Description
schema Schema Object Required. The schema defining the type used for the body parameter.

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject

@lonix1
Copy link
Contributor

lonix1 commented May 9, 2019

@spottedmahn I assume you found no workaround / hack / custom javascript / etc.?

@spottedmahn
Copy link
Contributor

I didn't, sorry. @lonix1

@dawu415
Copy link

dawu415 commented Apr 17, 2020

Will there be any upcoming work to get examples in for Get Request parameters? I was looking at the OAI 3.0.1 and it seems to show that support for examples in parameters is available

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#exampleObject

@mattfrear
Copy link
Owner

mattfrear commented Apr 17, 2020

Good spotting @dawu415 . I'll have a look into it. It would be great to have examples on querystring parameters.

@mattfrear mattfrear reopened this Apr 17, 2020
@mattfrear
Copy link
Owner

Just had a quick play and it works 😀

image

I'll need to tidy this up, add unit tests etc. This will be a great addition once it's ready.

@mattfrear
Copy link
Owner

mattfrear commented Apr 19, 2020

OK so I started implementing this, and I remembered that querystring and route parameters can only be primitive types.

Therefore an IExamplesProvider would look something like:

    public class PersonIdExample : IExamplesProvider<int>
    {
        public int GetExamples()
        {
            return 99;
        }
    }

To me that rules out using this with "automatic annotation", because every time you have an int as a parameter in any of your controller methods, the example of 99 would be used.

So then I thought, OK, we're only gonna use this with ye olde [SwaggerRequestExample] attribute (which I haven't used in years). It has this signature:

public SwaggerRequestExampleAttribute(
    Type requestType, 
    Type examplesProviderType, 
    Type contractResolver = null, 
    Type jsonConverter = null)

And then I thought, well, that's not really gonna work here because if you had multiple of the same type and wanted different examples, e.g.

[SwaggerRequestExample(typeof(int), typeof(IdExample))]
[SwaggerRequestExample(typeof(int), typeof(PersonIdExample))]
public PersonResponse GetPerson(int id, int age)

It wouldn't work, because both parameters are of type int (I think the example generated by the first attribute, IdExample would be used for both parameters). So then I thought well maybe I need to add yet another parameter to SwaggerRequestExampleAttribute, e.g.

public SwaggerRequestExampleAttribute(
    Type requestType, 
    Type examplesProviderType, 
    Type contractResolver = null, 
    Type jsonConverter = null,
    string parameterName)

So that you could differentiate between the two int parameters above and have a different IExamplesProvider for each of them:

[SwaggerRequestExample(typeof(int), typeof(IdExample), parameterName:"id")]
[SwaggerRequestExample(typeof(int), typeof(PersonIdExample), parameterName: "personId")]
public PersonResponse GetPerson(int id, int age)

And then I thought, wth, why not just create another attribute, e.g.

public SwaggerParameterExampleAttribute(
    string parameterName,
    string example)

which would be much easier to use than SwaggerRequestExampleAttribute.

[SwaggerParameterExample("id", "1")]
[SwaggerParameterExample("age", "23")]
public PersonResponse GetPerson(int id, int age)

So I started implementing that, and then I thought, hang on, doesn't Swashbuckle.AspNetCore have example generation built in? So I started looking at its code, and then thought, ah this would be a better fit in Swashbuckle.AspNetCore. So I went and added it there and created a pull request domaindrivendev/Swashbuckle.AspNetCore#1629.

Usage:

/// <param name="id" example="123">The product id</param>
public Product GetById(int id)

If the PR doesn't get accepted then I'll finish my SwaggerParameterExampleAttribute.

And that was how I spent 4 hours on a Sunday.

@dawu415
Copy link

dawu415 commented Apr 19, 2020

Nice.

How would it work for binding request models? Do the incoming request parameters still get mapped as if it were all individual parameters to a Get request?

e.g.

Route[...]
public PersonResponse GetPerson([FromRoute]Person p)

where Person is a class like this.

public class Person 
{
     [Required]
     int Id
     [Required]
     int age
     ...  
}

@mattfrear
Copy link
Owner

Route[...]
public PersonResponse GetPerson([FromRoute]Person p)

Is that possible? To put [FromRoute] in front of a reference type? Please paste a working example as I've never seen it done.

@dawu415
Copy link

dawu415 commented Apr 20, 2020

@mattfrear Here is a sample: https://github.com/dawu415/dotnetcore-webapi-sample

        // e.g. https://localhost:5001/weatherforecast/AU/MEL/1/2/2020
        [HttpGet]
        [Route("{country}/{city}/{day}/{month}/{year}")]
        public string Get([FromRoute]WeatherRequest wr)
        {
            return JsonSerializer.Serialize(wr);
        }

with WeatherRequest defined as

using System.ComponentModel.DataAnnotations;

namespace sample.Models 
    {
    public class WeatherRequest {
        [Required]
        public string country { get; set;}
        [Required]
        public string city { get; set; }
        [Required]
        public int day { get; set; }
        [Required]
        public int month { get; set; }
        [Required]
        public int year { get; set; }
    }
}

You can see this in the browser e.g. https://localhost:5001/weatherforecast/AU/MEL/1/2/2020
The controller is in /Controllers and the WeatherRequest model is in /Models

@mattfrear
Copy link
Owner

mattfrear commented Apr 21, 2020

@dawu415 thanks for the sample. I didn't know you could do that with [FromRoute].

I opened up your solution and I did try adding XML examples, as specified on Swashbuckle.AspNetCore's readme:

    public class WeatherRequest {
        /// <summary>
        /// The country
        /// </summary>
        /// <example>New Zealand, bro</example>
        [Required]
        public string country { get; set;}

And the example works 😀
image

@mattfrear
Copy link
Owner

So I went and added it there and created a pull request Swashbuckle.AspNetCore#1629.

My PR has been merged so this will be in the next release of Swashbuckle.AspNetCore. I'm closing this issue.

@mattfrear
Copy link
Owner

@dawu415 FYI I've edited my previous answer after I spotted my mistake and got it working.

@spottedmahn
Copy link
Contributor

My PR has been merged so this will be in the next release of Swashbuckle.AspNetCore

thanks @mattfrear 😊🤝

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants