Inserting Post Method with Spring Boot - spring-boot

I'm learning Kotlin, part of my project is to integrate JSON as an object and use the POST method to change or add information.
I'm not able to do this, I need help.
package com.example.blog
import org.springframework.web.bind.annotation.*
data class Relatorio(
val titulo: String,
val autor: String,
val serie: String
)
#RestController
#RequestMapping("/Bradesco")
class BradescoController {
#GetMapping()
public fun relatorio(): Relatorio {
val result = Relatorio(
"Investimentos",
"Luis Felipe",
"Bradesco Analises"
)
return result
}
#PostMapping
#RequestMapping( #RequestBody "/empiricus")
public fun relatorio2() {
"titulo" = "Contra as altas taxas"
return "Atualizado";
}
}

It looks like some annotations are out of place in your relatorio2 method. You want to register a REST-endpoint for the POST-method and the path /empiricus.
This can happen one of two ways:
Annotate the method with #RequestMapping(value = "/empiricus", method = RequestMethod.POST)
Annotate the method with `#PostMapping("/empiricus") (you can omit the method-parameter from the example above, since this a shortcut for exactly that.
The #RequestBody annotation needs to be placed in the parameter of the relatorio2 method since it tells Spring to map the POST request-body to an object.
Therefore the method should look something like this:
#PostMapping("/empiricus")
public fun relatorio2(#RequestBody relatorio: Relatorio) {
"titulo" = "Contra as altas taxas"
return "Atualizado";
}
Since you added a path on class level, the complete path to call the method is /Bradesco/empiricus. When the object is available in the relatorio2 method, you can use it in your business logic.

Related

Custom ConditionalGenericConverter annotation doesn't convert parameters in Spring GraphQL

I have a REST API developed in Spring and now I'm learning how to use GraphQL in my Spring project so I'm "translating" my REST endpoints to GraphQL queries.
Just to give some background on what I'm trying to do, I will show how one of my REST controllers look like (code in Kotlin):
Rest Controller
#RestController
#RequestMapping("countries")
class CountryController(private val countryService: CountryService) {
#GetMapping("{code}")
fun findByCode(#PathVariable #Uppercase code: String): Country {
return countryService.findByCode(code)
}
}
It's basically an endpoint that fetches a country based on a code. For example /countries/bra will fetch information about the country Brazil.
In the code above there's also a custom annotation called #Uppercase. This annotation extends ConditionalGenericConverter and the only thing it does is to take the code parameter and convert it to uppercase. This is the code for this annotation:
#Uppercase implementation
#Target(AnnotationTarget.VALUE_PARAMETER)
annotation class Uppercase
#Component
class UppercaseConverter : ConditionalGenericConverter {
override fun matches(sourceType: TypeDescriptor, targetType: TypeDescriptor): Boolean {
return targetType.getAnnotation(Uppercase::class.java) != null
}
override fun getConvertibleTypes(): MutableSet<ConvertiblePair>? {
return mutableSetOf(ConvertiblePair(String::class.java, String::class.java))
}
override fun convert(source: Any?, sourceType: TypeDescriptor, targetType: TypeDescriptor): String {
return (source as String).uppercase()
}
}
Everything works as expected in the code above and the code parameter is always converted to uppercase if I use the #Uppercase annotation in my REST controller.
Now I created a similar controller for Spring GraphQL that looks like this:
GraphQL Controller
#Controller
class CountryResolver(private val countryService: CountryService) {
#QueryMapping(name = "country")
fun findByCode(#Argument #Uppercase code: String): Country {
return countryService.findByCode(code)
}
}
And my GraphQL is executed properly with the code above, except that the parameter code is not converted to uppercase when I use my custom annotation #Uppercase. It seems that my custom annotation only works with #PathVariable in the REST controller, but it doesn't with #Argument in the GraphQL controller.
Any idea why on how to make it work with GraphQL as well? Thanks in advance!

Spring Boot, MockMvc: how do I add an attribute to the model?

I'm using MockMvc to test a method on my controller. But that controller method, as designed, expects a certain attribute to have been added to the model already, which would be the case in normal use. But when testing just this method, I can't figure out how to add the attribute to the model.
For example:
#RequestMapping(value = 'myEndpoint', method = RequestMethod.POST)
public String IWantToBeTested(Model model, #RequestParam(myParam) String paramValue) {
String needThis = model.asMap().get("NeedThisAttributeSetAlready");
String newThing = needThis + myParam;
model.AddAttribute("NewThing", newThing);
}
when I run the test with MockMvc, it supplies an empty model to this function, and I get an null pointer exception.
My test looks like this:
mockMvc.perform(post("/myEndpoint")
.param("myParam", "howdy")
.contentType("application/x-www-form-urlencoded"))
.andExpect(model().attributeExists("NewThing"))
.andExpect(model().attribute("NewThing", "oldThinghowdy"));
I've tried
#Mock
Model model;
but I don't know what to use for when().thenReturn(), because there aren't any get methods on the Model interface in Spring Boot, so I don't know what to mock. I tried:
when(model.asMap()).thenReturn(mockModel.asMap());
where mockModel is a static in my test class and has "NeedThisAttributeSetAlready" in it. That didn't work.
Any ideas?

Map #CookieValue, #RequestHeader etc. to POJO in Spring Controller

I have a bunch of params in my controller and want to map all of them to a separate POJO to keep readability. There is also a #CookieValue, #RequestHeader I need to evaluate and aim for a solution to also map them to that POJO. But how?
I saw a possible solution on a blog but it doesn't work, the variable stays null.
Controller:
#RequestMapping(path = MAPPING_LANGUAGE + "/category", produces = MediaType.TEXT_HTML_VALUE)
#ResponseBody
public String category(CategoryViewResolveModel model) {
doSomething();
}
And my POJO is this:
public class CategoryViewResolveModel {
private String pageLayoutCookieValue;
public CategoryViewResolveModel() {
}
public CategoryViewResolveModel(
#CookieValue(value = "SOME_COOKIE", required = false) String pageLayoutCookieValue) {
this.pageLayoutCookieValue = pageLayoutCookieValue;
}
... some other RequestParams, PathVariables etc.
}
According to the documentation it's not possible for #CookieValue and #RequestHeader.
This annotation is supported for annotated handler methods in Servlet
and Portlet environments.
Take a look at:
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/
instead of using getParameter to access request parameters you can use getHeader to retrieve the header value and so define your CategoryViewResolveModel just as you were requesting

Can I use both #Post and #Get on the same method

I would like to use both #Post and #Get on the same method like
#GET
#POST
#Path("{mode}")
public void paymentFinish(#PathParam("mode") String mode, String s) {
logger.debug("Enter PayStatus POST");
logger.debug(mode);
}
Even I write like this, I got error. What I want is whatever get or post to the sameurl, the same method works. Is it possible? Now I separate two methods, one for get and one for post.
Unfortunately, only one should be used in order to avoid Jersey exception.
But you could do something like :
#GET
#Path("{mode}")
public void paymentFinish(#PathParam("mode") String mode, String s) {
commonFunction(mode);
}
#POST
#Path("{mode}")
public void paymentFinishPOST(#PathParam("mode") String mode, String s) {
commonFunction(mode);
}
private void commonFunction(String mode)
{
logger.debug("Enter PayStatus POST");
logger.debug(mode);
}
By doing so, if you want to change inner behavior of your functions, you will only have to change one function.
Note that method name in java for get vs post need to be different.
After searching a lot trying to avoid the solution above, I found nothing....
Then I decided to create a custom annotation so I didn't have to waste time duplicating methods.
Here's the github link: Jersey-Gest
It allows you to create GET and Post Methods on a single Annotation by generating a new class from it.
I hope it helps you the same way it helped me :)
Edit:
If for some reason the above link stops working, here's what I did:
Created a compile-time annotation #RestMethod for class methods.
Created a compile-time annotation #RestClass for classes.
Create an AnnotationProcessor which generates a new class with Jersey's corresponding annotations and for each method creates a GET and a POST method which callsback to the original method annotated with #RestClass.
All methods annotated with #RestMethod must be static and contained within a class annotated with #RestClass.
Example (TestService.java):
#RestClass(path = "/wsdl")
public class TestService
{
#RestMethod(path = "/helloGest")
public static String helloGest()
{
return "Hello Gest!";
}
}
Generates something like (TestServiceImpl.java):
#Path("/wsdl")
#Produces("application/xml")
public class TestServiceImpl
{
#GET
#Path("/helloGest")
#Produces(MediaType.APPLICATION_XML)
public String helloGestGet()
{
return TestService.helloGest();
}
#POST
#Path("/helloGest")
#Consumes(MediaType.WILDCARD)
#Produces(MediaType.APPLICATION_XML)
public String helloGestPost()
{
return TestService.helloGest();
}
}

Trouble with request mapping attribute with restful service

I have a whole bunch of methods as shown below in a class.
#RequestMapping(value="/person/foo" method = RequestMethod.POST, headers = "Accept=application/xml, application/json")
public #ResponseBody Person update(#RequestBody final Person person) {
//
}
I want to put the annotations at the class level so my methods look like below:
#RequestMapping(value="foo") // for Post requests
public #ResponseBody Person update(#RequestBody final Person person) {
//
}
Most of my methods are POST, so I use that at the class level. The methods that are GET, I want to put it at the method level.
But it doesn't work. Some of the Post methods work, but GET methods don't work at all.
If you have #RequestMapping defined at the class level e.g.:
#Controller
#RequestMapping( value="/person" )
public class BeautifulPeopleController { ... }
You cannot make some of the methods to ignore it => all of the methods would assume they are prepended with /person.
Here is from the #RequestMapping API docs:
Method-level mappings are only allowed to narrow the mapping expressed at the class level (if any)

Resources