Modifying SwaggerUI to remove required PathParam - spring-boot

I have a requirement to use optional path parameters. So made it like below;
#ApiOperation(httpMethod = "GET", value = "Get User Details With Optional Path Parameters", notes = "Output depends on values provided")
#ApiResponses(value = {
#ApiResponse(code = 404, message = "We do not unserstand what you mean"),
#ApiResponse(code = 400, message = "You are not requesting like a BOSS.") })
#RequestMapping(value = { "/getuser/userid/{userid}",
"/getuser",
"/getuser/userid/{userid}/alias/{alias}", "getuser/alias/{alias}" }, method = RequestMethod.GET, produces = {
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
#ResponseStatus(HttpStatus.OK)
private UserSchema getUserDetails(
#PathVariable Optional<String> userid,
#PathVariable Optional<String> alias) {
Users user = null;
UserSchema returningSchema = buildDefaultSchema();
if (alias.isPresent()) {
//Get The Value
} else {
//Try Get Other Value and do stuff etc.
}
//Similar for userid
try {
//Get User Data From DB
user = dao.getUserData(userid,alias);
//Bind data to returning schema
} catch (Exception ex) {
Log.error(getClass().getName(), ex);
returningSchema.setResponseText("Something is Wrong");
}
return returningSchema;
}
But with swagger, its not allowing to make a request, as PathVariables are required type. I do not know much javascript. Tried this solution to modify the swagger-ui.js, but seem to get lost in the huge file and cannot find the portion mentioned.
I use the latest Swagger-UI version. Is it possible i can make the request with optional path variables and the correct path should be shown in swagger?
NOTE: I know swagger spec does not allow optional path variables. But i want to change this in my app only.
Thanks.

Yes, you can definitely fork swagger-js and swagger-ui to support this. Take a look at the Operation.prototype.getMissingParams in operation.js.

Related

When returning a json, how can you tell using Spring that you want to go to a certain url?

In my code, I am returning a path in String form(/successPassword or /systemError). Basically this tells Spring to go to the path /successPassword or /systemError, /successPassword goes to successPassword.html and /systemError goes to systemError.html. These html files are located in src/main/resources/templates.
#RequestMapping(value = "/user/new_password", method = RequestMethod.POST)
public String createNewPassword(#RequestParam(value = "newpassword", required = false) String password,
#RequestParam(value = "hash") String hash, Model model) {
LOG.info("set new password using hash " + hash);
LOG.info("new password " + password);
boolean hashValid = passwordService.isHashValid(hash);
if (hashValid) {
ValidateMessage message = passwordService.validateNewPassword(password);
if (!message.isError()) {
Account account = passwordService.getAccount(hash);
passwordResetService.saveNewPassword(password, account);
model.addAttribute("account", account);
return "/successPassword";
} else {
LOG.info("Password not complex enough.");
return "/systemError";
}
} else {
LOG.info("Invalid hash.");
return "/systemError";
}
}
Now it looks like I need to return a json object instead. I know that I have to annotate the method with #ResponseBody instead, but my question is how would i package now the object that I will return so that whoever receives it will know to which path it needs to go to? My html codes are using Thymeleaf (no javascript whatsoever).

HttpClient always do Basic Authentication check after provide an authorization header?

The web api control, UserControl, has two methods, RetrieveUserID which needs Basic Authorization check
[HttpGet]
[Route("RetrieveUserID/{strUsername}")]
[Authorize]
public string RetrieveUserID(string strUsername)
{
//retrieve userID and return it
......
return strUserID;
}
Another method, FailAuthenticationReason, is used if fail to retrieve userID, it returns the detail fail info such as wrong username, wrong password, account is locked out, etc. which doesn't need to do any authentication check
[HttpGet]
[Route("FailAuthenticationReason/{strUsername}")]
public string FailAuthenticationReason(string strUsername)
{
//retrieve detail failed reason
......
return strFailedReason;
}//End of
It works well when I use a browser to check them. But when I use it in my app, after I provide the authorization header and fail to retrieve userID because of incorrect username and/or password, it also do the authorization check when it call FailAuthenticationReason
var authData = string.Format("{0}:{1}", entUsername.Text,entPassword.Text);
var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
App.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
var uri = new Uri(string.Format(App.strWebAPIURI + "/RetrieveUserID/{0}", entUsername.Text));
try
{
var response = await App.httpClient.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
......
}
else
{
//Fail to pass authorization
uri = new Uri(string.Format(App.strWebAPIURI + "/FailAuthenticationReason/{0}", entUsername.Text));
response = await App.httpClient.GetAsync(uri);
......
}
How can the program call FailAuthenticationReason without the authorization check?

Spring MVC Ajax Post 400 Error (Bad Request)

So I'm using Spring MVC and I'm trying to do an ajax post to add a comment to a Post entity, like a typical social network. And I'm getting an error in the Chrome Developer's Tool's that says this Failed to load resource: the server responded with a status of 400 (Bad Request). I'm thinking that might mean something is going wrong in my controller, however the way this is set up, it's not letting me check it out in debug mode.
I'll show you guys all the pieces of code that work together so you guys can get a better understanding of my problem.
So here's my Ajax, and everything is running through, and sending a "there was an error" message, so It's at least running through the code and reaching the controller.
Also the CDATA stuff is for Thymeleaf.
<script th:inline="javascript">
/*<![CDATA[*/
var postById = /*[[${postById.id}]]*/'1';
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
$(document).ready(function(){
$("#submit").on("click", function(ev) {
ev.preventDefault();
$.ajax({
url : "newComment",
type : "post",
data : {
"postById" : postById,
"newComment" : $("#newComment").val()
},
success : function(data) {
console.log(data);
location.reload();
},
error : function() {
console.log("There was an error");
//location.reload();
}
});
});
});
/*]]>*/
</script>
Here's my Controller Get Method
#RequestMapping(value="viewCourse/post/{postId}", method=RequestMethod.GET)
public ModelAndView postViewGet (#RequestParam(value = "pageSize", required = false) Integer pageSize,
#RequestParam(value = "page", required = false) Integer page, #PathVariable Long postId) {
ModelAndView modelAndView = new ModelAndView("post");
{
// Evaluate page size. If requested parameter is null, return initial
// page size
int evalPageSize = pageSize == null ? INITIAL_PAGE_SIZE : pageSize;
// Evaluate page. If requested parameter is null or less than 0 (to
// prevent exception), return initial size. Otherwise, return value of
// param. decreased by 1.
int evalPage = (page == null || page < 1) ? INITIAL_PAGE : page - 1;
//StudySet studySet = studySetRepo.findOne(studySetId);
//List <Row> rows = studySet.getRows();
//Set<Row> rowSet = new TreeSet<Row>(rows);
Post postById = postRepo.findOne(postId);
Comment comment = new Comment();
Page<Comment> postComments = commentService.findByPostOrderByIdDesc((postById), new PageRequest(evalPage, evalPageSize));
Pager pager = new Pager(postComments.getTotalPages(), postComments.getNumber(), BUTTONS_TO_SHOW);
modelAndView.addObject("postId", postId);
modelAndView.addObject("postById", postById);
modelAndView.addObject("postComments", postComments);
modelAndView.addObject("comment", comment);
modelAndView.addObject("selectedPageSize", evalPageSize);
modelAndView.addObject("pageSizes", PAGE_SIZES);
modelAndView.addObject("pager", pager);
return modelAndView;
}
}
Here's my Controller Post Method
#RequestMapping(value="viewCourse/post/newComment", method=RequestMethod.POST)
public #ResponseBody Post newComment (#Valid #RequestParam Long postId, #RequestParam String newComment, ModelMap model, #AuthenticationPrincipal User user)
{
Post post = postRepo.findOne(postId);
Comment comment = new Comment();
comment.setComment(newComment);
comment.setPost(post);
comment.setDate(LocalDate.now());
comment.setTime(LocalTime.now());
comment.setDateTime(LocalDateTime.now());
comment.setUser(user);
user.getComments().add(comment);
post.getComments().add(comment);
commentRepo.save(comment);
Post savedPost = postRepo.save(post);
return savedPost;
}
Also I have some annotations in the entity objects, that could have something to do with it.
Here's my User Entity
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
public Set<Comment> getComments() {
return comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
Here's my Comment entity
#ManyToOne
#JsonBackReference
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
Also here's a picture of my console in the Chrome Developer Tools, so you guys can see exactly what it's showing me.
If anyone can see where I'm going wrong and point me in the right direction, that would be great, thanks in advance.
Also if you guys need to see any other code, just let me know.
My problem was in the Ajax, I was looking for the postById.id, when I should have been using just the postId that I added to the model in the Get method in the controller.

How to create HttpResponseMessage in a non-APIController

I have a service layer called GatewayService which calls another WebApi to get the product information and manipulate the Inventory in the response and return the same response to the caller.
Here is the code I have. The problem that I have is, I can't use Request.CreateResponse(...) which will give me a compilation error because GatewayService does not inherit ApiController.
Is there a way to update the response and return as HttpResponseMessage?
public class GatewayService
{
// Code Removed for bravity
public HttpResponseMessage Get(SingleProductSearcRequest request)
{
var response = productServiceWebApi.Get(request); // Returns HttpResponseMessage
var p = response.Content.ReadAsAsync<JObject>().Result;
p["Inventory"] = "Not Available";
return Request.CreateResponse(p); // COMPILER ERROR!!!
}
}
Request.CreateResponse() is just an extension method for HttpRequest. You can manually construct an HttpResponseMessage as well as give it content by doing something like:
var p = response.Content.ReadAsAsync<JObject>().Result;
HttpResponseMessage message = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
message.Content = new ObjectContent(p);
You can transfer headers and other information over as well, if necessary. Depending on the need, there's also StringContent, etc.

SpringBoot/MVC & Thymleaf form validation on POST with URL parameters

I have a form and validation works. The problem comes in when a url parameter was added. The url parameter is a token and is required. So this is what my controller looks like:
#RequestMapping(value = "/resetpassword", method = RequestMethod.GET)
public String showResetForm(ResetPassword resetPassword, Model model,
#RequestParam(value = "token", required = true) String token,
#RequestParam(value = "msg", required = false) String msg){
model.addAttribute("token", token);
return "resetpassword";
}
#RequestMapping(value = "/resetpassword", method = RequestMethod.POST)
public String setPwd(#ModelAttribute("resetPassword") #Valid ResetPassword resetPassword,// RedirectAttributes reDirectAttr,
BindingResult bindingResult, Model model,
#RequestParam(value = "token", required = true) String token,
#RequestParam(value = "msg", required = false) String msg){
if (bindingResult.hasErrors()) {
//reDirectAttr.addFlashAttribute("org.springframework.validation.BindingResult.resetPassword",bindingResult);
//reDirectAttr.addFlashAttribute("resetPassword",resetPassword);
return "resetpassword?token="+token;
}
else {
if (token == null) {
// TODO: no token, what to do here??
return "redirect:/resetpassword?token=\"\"&msg=notoken";
}
ResetPasswordResponseDto response = super.resetUserPassword(
resetPassword.getUname(), resetPassword.getPassword(),
token);
if (response.getPasswordResetResult() == PasswordResetResult.SUCCESSFUL) {
// TODO: it worked, what now?
return "redirect:/login";
} else if (response.getPasswordResetResult() == PasswordResetResult.INVALID_TOKEN) {
// TODO: bad token
return "redirect:/resetpassword?token="+token+"&msg=badtoken";
} else if (response.getPasswordResetResult() == PasswordResetResult.OUT_OF_POLICY_PW) {
// TODO: out of policy pw
return "redirect:/resetpassword?token="+token+"&msg=outofpolicy";
} else if (response.getPasswordResetResult() == PasswordResetResult.LDAP_FAILURE) {
// TODO: other failure
return "redirect:/resetpassword?token="+token+"&msg=error";
}
}
return "redirect:/resetpassword?token="+token+"&msg=error";
//return new RedirectView("resetpassword?token=\"\"&msg=notoken");
}
So I tried a bunch of things but nothing seems to work. Here is what I would like to happen when the view is requested /resetpassword?token=1232453 the view is displayed. Then if the form has errors the url parameter persists in the url and the form displays the errors. Right now I get an error saying that the template cannot be resolved. Ok fair enough, so I tried doing a redirect instead
return "redirect:/resetpassword?token="+token;
and that seems to work, however the URL parameter is lost and the view loses the bindingResult errors. In the code, I posted I also tried FlashAttributes but I just get an error "Validation failed for object='resetPassword'. Error count: 4" which is correct but I need it to show the form and the errors I coded with Thymeleaf. Any help or suggestions would be great!
Resources I have looked at:
Spring - Redirect after POST (even with validation errors)
&
SpringMVC controller: how to stay on page if form validation error occurs
Have you tried returning a ModelAndView instead of just the redirect string? Attributes on the model will be available as URL query parameters.
ModelAndView redirect = new ModelAndView("redirect:/resetpassword");
redirect.addObject("token", token);
redirect.addObject("msg", "error");
return redirect;

Resources