springdoc multiple 404 responses using #ApiResponse (java annotations) - openapi

How to create multiple 404 responses (or more broadly, multiple same HTTP code responses) using java annotations.
I've tried:
#ApiResponse(
responseCode = "404",
description = "Not Found 1"
)
#ApiResponse(
responseCode = "404",
description = "Not Found 2"
)
And also mulitple #Content:
#ApiResponse(
responseCode = "404",
content = {
#Content(schema = #Schema(name = "404-1", description = "404-1")),
#Content(schema = #Schema(name = "404-2", description = "404-2"))
}
)
The only way I can get something similar to multiple is by using #ExampleObject[]:
#ApiResponse(
responseCode = "404",
content = #Content(
mediaType = "application/json",
examples = {
#ExampleObject(name = "404-1", description = "Not Found 1 desc"),
#ExampleObject(name = "404-2", description = "Not Found 2 desc")
}
)
)
This is not ideal because it requires human interaction to view all of them and is just not wanted; the expectation is to have:
- 200
- 404 Description 1
- 404 Description 2
- 404 Description 3
or even better:
- 200
- 404 Description 1
Description 2
Description 3
I'm using springdoc and the following dep:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.4.3</version>
</dependency>

By design, and not springdoc, but OpenAPI-Specification, all the responses are stored in a type ApiResponses which extends LinkedHashMap.
https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#responsesObject
Eeach HTTP code, for an operation can only have one ApiResponse object assigned.
Using examples is a good way to go.
If your multiple 404 responses have different structure, you can use oneof as follow:
#RestController
public class HelloController {
#GetMapping("/hello")
#ApiResponses({
#ApiResponse(responseCode = "200"),
#ApiResponse(description = "Not found", responseCode = "404",
content = #Content(mediaType = "application/json", schema = #Schema(oneOf = {
Foo.class, Bar.class }))) })
String hello() {
return null;
}
#Schema(description = "this is bar")
class Bar {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
#Schema(description = "this is foo")
class Foo {
private String foo;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
}

I solved my issue by just adding an HTML <br/> tag to the description where I wanted a new line:
#Operation(
responses = {
#ApiResponse(responseCode = "404", content = #Content,
description =
"This is potential 404 #1 <br/>" +
"This is potential 404 #2"
)
}
)
Alternatively,
You could create an annotation to make this more readable, for example something like #ApiResponse404 and add it to the operation via OperationCustomizer:
#Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
ApiResponse404 notFounds = handlerMethod.getMethodAnnotation(ApiResponse404.class);
if (notFounds != null)
operation.getResponses()
.addApiResponse("404", new ApiResponse()
.description(String.join("<br/>", notFounds.value()))
);
return operation;
}
Of course you would have to take into consideration the #Content, which you can easily add to the annotation, but I don't need it my scenario, I just need the description.
Then in a controller you can use the annotation:
#GetMapping("/helloworld")
#ApiResponse404({"This is potential 404 #1", "This is potential 404 #2"})
String getHelloWorld() {
return "Hello. World.";
}

Related

In Jersey, how to return an object with 400 response code different than the 200 response object

I am new to Jersey and to REST in general, so this might be a stupid question....
In my code, I send a request (TemplateValidationRequest) to try to validate an object. If the object fails to validate, I want to return a String. How do I do this?
In the second code snippet at the bottom, you can see that I'm looking for TemplateValidationResponse object. How can I change my code so that:
I can return a string, and
I can get a String instead of a TemplateValidationResponse object.
Is this possible?
#POST
#Path("validate/modelTemplate")
#Produces({MediaType.APPLICATION_JSON})
#Consumes({MediaType.APPLICATION_JSON})
#Operation(
summary = "Convert model template to AMBOS interaction model and validate the result",
tags = { BLUEPRINTS_TAG },
requestBody = #RequestBody(
content = #Content(
schema = #Schema(
implementation = TemplateValidationRequest.class
)
)
),
responses = {
#ApiResponse(
responseCode = "200",
description = "Success.",
content = #Content(
schema = #Schema(
implementation = TemplateValidationResponse.class
)
)
),
#ApiResponse(
responseCode = "400",
description = "Failure",
content = #Content(
schema = #Schema(
implementation = String.class
)
)
)
}
)
#CustomerIdentityRequired
#AcceptsLanguageRequired
#AAA(serviceName = SERVICE_NAME, operationName = BLUEPRINTS_GET)
ModelTemplateValidationResponse validateModelTemplate(TemplateValidationRequest);
 
fun validateModelTemplate(modelTemplate: InteractionModel,
sampleData: Map<String, Any>): TemplateValidationResponse {
val request = TemplateValidationRequest()
request.modelTemplate = modelTemplate
request.sampleData = sampleData
return temp.validateModelTemplate(request)//this is where I call the above code
//If this request fails and results in a 400 error, I want to get a String
}
What about something like:
public Class YourResponse {
private boolean isError;
private String setThisWhenThereIsError;
private YourObject setThisWhen200;
}

Web API Returns Wrong Values after being Published to IIS

Please help me. The API I created works fine when I launch it with Visual Studio but (the POST methods) has issues I deployed to IIS.
It returns wrong values and sometimes, null values. If i go back to test it in debug mode it works well.
THIS IS WHERE I'M CALLING THE API
try
{
string apiToken = "DEFAULTAPI";
EmployeeObject EmployeeObject = new EmployeeObject()
{
username = "John Doe"
password = "12345"
apiToken = apiToken
};
var emp = JsonConvert.SerializeObject(EmployeeObject);
string url = "http://localhost/PublishVersion/api/Company";
//the url variable holds the published version link
var response = client.PostAsync(url, new StringContent(emp, Encoding.UTF8, "application/json"));
response.Wait();
var result = response.Result;
if (result.IsSuccessStatusCode)
{
Uri employeeUrl = result.Headers.Location;
var statusMessage = result.Content.ReadAsStringAsync().Result;
if (statusMessage == "yes")
{
status = true;
}
else if (statusMessage == "no")
{
status = false;
}
}
return status;
}
AND THIS IS THE API
public string Post([FromBody] Employees employees)
{
string message = "no";
if (employees != null)
{
string emp = employees.username;
string password = employees.password
string apiToken = employees.apiToken
APIToken token = _dbContext.MyTable.Where(x => x.apitoken == apitoken).FirstOrDefault();
//APIToken is a table which has properties company and boss (both string)
if (token != null)
{
string company = token.company;
string boss = token.boss;
return message = "yes" + " " + company + "" + boss;
}
else
{
return message = "invalid token";
}
}
return message + employee.username;
}
The api returns "no John Doe" to the caller, which shouldn't be that way, since it displayed the username value which shows that the employee object is not null. Why doesn't it go into the block of code since it passed the null check? This issue only comes up when I deploy the API project to the IIS (version 10). Works fine in Visual Studio debug mode.
Also, the API and Winsform(where i'm calling it from) are on the same machine.
Thank you.

Passing a map in GET, JAVA

How do i Pass a Map of Key Value pairs to a GET REST API,
Here is the call to the resource .. I am getting a method not allowed
String queryMap= String.format("{'softwareversion':'%s','peril':'%s','analysistype':'%s', 'region':'%s'}", "HD18", "Flood", "EP", "USFL");
String url = String.format("http://localhost:%d/templates/modelprofile?queryMap=%s", API_APPLICATION_RULE.getLocalPort(),queryMap);
Response response = ClientBuilder.newClient()
.target(url)
.request()
.header("Authorization", getToken())
.get();
I have the resource as below
#GET
#Path("/{templateType}")
#Timed
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get templates based on peril/region,version and type",
httpMethod = ApiConstants.GET)
#ApiResponses(value = {
#ApiResponse(code = ApiConstants.INT_200,
message = "TemplateReader was retrieved successfully from the database."),
#ApiResponse(code = ApiConstants.INT_400,
message = "Bad request (wrong or missing inputs)"),
#ApiResponse(code = ApiConstants.INT_500,
message = ApiConstants.INTERNAL_SERVER_ERROR)
})
public Template getTemplate(#ApiParam(hidden = true) #Auth User user,
#ApiParam(name = "templateType", value = "templateType")
#PathParam("templateType") String templateType,
#ApiParam(name = "queryMap", value = "queryMap")
#RequestParameters Map<String,String> queryMap
) throws ApiException
You can pass it using request parameter as below.
#RequestMapping(name = "/url/", method = RequestMethod.GET)
public void method_name(#RequestParam(name = "map_name")Map<String, Object> requestMap){
//Process map and perform your logic
}

MVC Blog URL Routing

I have my requirement to generate my URL as
/2013/10/custome-mvc-url-rout-to-display-mixture-of-id-and-urlslug
I have seen many questions to achieve it & my question may have possibility of Duplicate.. Like:-
asp-net-mvc-framework-part-2-url-routing
custome-mvc-url-rout-to-display-mixture-of-id-and-urlslug
etc...
I have achieved it as follows:-
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Post",
"{year}/{month}/{title}",
new { controller = "Blog", action = "Post" }
);
and my Hyperlink which would generate this would be :-
#Html.ActionLink("continue...", "post", "blog",
new {
year = Model.PostedOn.Year,
month = Model.PostedOn.Month,
day = Model.PostedOn.Day,
title = Model.UrlSlug
}, new { title = "continue..." })
My MVC Controller being :-
public ViewResult Post(int year, int month, string title)
{}
But the issue over here is , I am getting my URL as :
http://localhost:2083/blog/post?Year=2013&Month=10&Day=9&title=best_practices_in_programming
and not like :-
http://localhost:2083/blog/post/2013/10/best_practices_in_programming
What am I doing wrong ? Please can someone point it.
Thnks!
I tried this and it worked as long as you put this route before the default route in RouteConfig.cs like this:
routes.MapRoute(
null,
"{year}/{month}/{title}",
new { controller = "Blog", action = "Post" },
new { year = #"\d+", month = #"\d+", title = #"[\w\-]*" });
You should also change the title to use hyphens instead of underscores IMO. Here is a good helper to do this.
#region ToSlug(), AsMovedPermanently
public static class PermanentRedirectionExtensions
{
public static PermanentRedirectToRouteResult AsMovedPermanently
(this RedirectToRouteResult redirection)
{
return new PermanentRedirectToRouteResult(redirection);
}
}
public class PermanentRedirectToRouteResult : ActionResult
{
public RedirectToRouteResult Redirection { get; private set; }
public PermanentRedirectToRouteResult(RedirectToRouteResult redirection)
{
this.Redirection = redirection;
}
public override void ExecuteResult(ControllerContext context)
{
// After setting up a normal redirection, switch it to a 301
Redirection.ExecuteResult(context);
context.HttpContext.Response.StatusCode = 301;
context.HttpContext.Response.Status = "301 Moved Permanently";
}
}
public static class StringExtensions
{
private static readonly Encoding Encoding = Encoding.GetEncoding("Cyrillic");
public static string RemoveAccent(this string value)
{
byte[] bytes = Encoding.GetBytes(value);
return Encoding.ASCII.GetString(bytes);
}
public static string ToSlug(this string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return string.Empty;
}
var str = value.RemoveAccent().ToLowerInvariant();
str = Regex.Replace(str, #"[^a-z0-9\s-]", "");
str = Regex.Replace(str, #"\s+", " ").Trim();
str = str.Substring(0, str.Length <= 200 ? str.Length : 200).Trim();
str = Regex.Replace(str, #"\s", "-");
str = Regex.Replace(str, #"-+", "-");
return str;
}
}
#endregion
Then you would also have to have a helper that replaces each hyphen with a whitespace from the url parameter title that you will likely pass to the controller action Post to query for the Post in the DB.

How to store data for the application via REST post call

Hi am developing my first grails RESTful application... In that i have mapped the post request to save method.its showing some error like, Internal server Error..Can any one please help me to brief how to use the post method to save the data via REST Post request...?
My save method is as follows..
def save = {
def xml = request.XML
def post = new ImageProperties()
post.content = xml.content.text()
def markup
if (post.save()) { markup = { status("OK") } }
else { markup = { status("FAIL") } }
render contentType: "text/xml; charset=utf-8", markup } }
and ImageProperties class as follows...
class ImageProperties {
static hasMany={categories:Categories}
String name
String place
String description
String path
String category
Categories categories
}
Your are assigning the posted data to an expected content property of your ImageProperties
def post = new ImageProperties()
post.content = xml.content.text()
but where in your Entity is that property?
class ImageProperties {
static hasMany={categories:Categories}
String name
String place
String description
String path
String category
Categories categories
}
Update: If you want to simply populate all properties you can use the domain class constructor:
def post = new ImageProperties(xml.content)
See more in the section REST and Data Binding of the Grails manual.
Hi after a long trial and error method i found the answer.. Hope this will be helpful for many users...
def save = {
def xml = request.XML
def post = new ImageProperties()
post.name = xml.name.text()
post.place = xml.place.text()
post.path = xml.path.text()
post.description = xml.description.text()
post.category = xml.category.text()
post.categories = Categories.get(xml.categories.#id.text())
def markup
if (post.save()) {
markup = {
status("OK")
}
} else {
markup = {
status("FAIL")
}
}
render contentType: "text/xml; charset=utf-8",
markup
}