Spring #JsonView how can use request parameter or header - spring

#JsonView
how can use like parameter from request:
#JsonView(header="range")
when response value,read request header range to exclude/include some field

JsonView provides "static" view mapping. so for your dynamic behaviour you can do like this:
// actual request handling is happened here
private ResponseEntity<SomeObject> processRequest();
// request handling with view limit in result
#JsonView(YourDefinedView.class)
#RequestMapping(value = "/request", headers={"range=include"})
public ResponseEntity<SomeObject> processWithView() {
return processRequest();
}
// request handling without view (no headers specified)
#RequestMapping(value = "/request")
public ResponseEntity<SomeObject> processWithoutView() {
return processRequest();
}
this will map your client to same request url, but depending on header it will provide view or not. Than you can create a set of methods, that will be using different #JsonView depending on headers information.
But with this you will limit only the data transfered to client, and the whole data load will happen on server. For example with database and JPA, if you would like not to fetch from database all that data you will end with javax.persistence.NamedEntityGraphs, which will change the general logic of your application - and will at the end of the day produce 2 different methods.
And if you would like to expose custom header with list of fields, to be serialized - custom DTO object, or Map<String, Object> (ugly-ugly-ugly) or custom HandlerMethodReturnValueHandler comes to your help.

Related

Can I transform the input of a json inside a spring boot controller?

I have a very simple spring boot web application which consumes requests with json body.
For each json which the application will receive (from any client) I would like to manipulate it as a first step.
For example if the client sends the following body:
{
"hello": "world!!!"
}
I would like to replace each ! with a ?. In this case the result is:
{
"hello": "world???"
}
This json transformation should be valid for each controller and for any json entering the system.
Is this kind of operation possible?
Thanks.
You may use string.replace to do the same.
Or also you can add custom annotation to manipulate the values of any keys.
You can use any replacement methods or regex in your classes.
#GetMapping
public String replace(RequestItem item){
// item = item.regex/replacement method
// call your service or whatever
return item;
}
When you got the data, you can do whatever you want to do.

How to just get the data using CRUD POST method?

I have developed Small Spring boot Rest api app. I can able to get the data or create new record and search with paging and sorting.
Now i'm looking for provide input data in body to get the data instead of providing in URL with GET method. Is this method also default function ? Please advise.
public interface CodeTextRepository extends PagingAndSortingRepository<CodeText, Long> {
}
How to write POST method to just get the data ?
http://localhost:8080/api/code
method : POST
{
"code":1
}
If I understand you correctly, you want to create a controller that will get the a model as body parameter ({ "code": 1 }) in a POST method and then do something with it.
To do that, you can create a controller that looks like the following (I inserted pseudo-code as an example):
#RestController
#RequestMapping(value = "/api/code")
public class CodeTextController {
private CodeTextRepository codeTextRepository;
// constructor injection
public CodeTextController(CodeTextRepository codeTextRepository) {
this.codeTextRepository = codeTextRepository;
}
#PostMapping
public CodeText postCodeText(#RequestBody CodeTextRequest codeTextRequest) {
// some code to get from the DB
return codeText;
}
}
public class CodeTextRequest {
private int code;
// getters and setters
}
Simply add Accept header to the request, like
accept: application/json
Spring Data-Rest will return the body after a POST request if either the returnBodyOnCreate flag was explicitly set to true in the RepositoryRestConfiguration OR if the flag was NOT set AND the request has an Accept header.
You can set the flag directly during configuration, or you can set it via the application.properties:
spring.data.rest.returnBodyOnCreate = true
you can also set it separately for update:
spring.data.rest.returnBodyOnUpdate = true
---- edit
Maybe I misunderstood your question. If you simply want to GET an existing data using POST method, then DO NOT DO IT AT ALL! That's not a REST API any more. There must be some reason you want to do it, but you should try do resolve that original problem instead in another way!

Command object automatically added to model?

I have a controller method like this:
#RequestMapping("/hello")
public String hello(UserForm user) {
return "hello";
}
It receives some request parameters in the UserForm command object. But I have not written any code to add the object to the Model. Still, in the view hello.jsp, I'm able to access the data, like this:
Hello, ${userForm.name}!
Does it mean that Spring MVC adds command objects to the Model automatically?
You don't need #ModelAttribute just to use a Bean as a parameter.
You'll need to use #ModelAttribute or model.addAttribute() to load default data into your model - for example from a database.
Most of the Spring controllers in the real world accept a lot of different types of parameters - Path variables, URL parameters, request headers, request body and sometimes even the entire HTTP Request object. This provides a flexible mechanism to create APIs. Spring is really good at parsing these parameters in to Java types as long as there is an ObjectMapper (like Jackson) configured to take care of the de-serialization.
The RequestMappingHandlerAdapter makes sure the arguments of the method are resolved from the HttpServletRequest.
Spring model data created prior to (or during) the handler method
execution gets copied to the HttpServletRequest before the next view
is rendered.
By now, Spring has processed the HTTP request and it creates the ModelAndView object from the method’s return value. Also, note that you are not required to return a ModelAndView instance from a controller method. You may return a view name, or a ResponseEntity or a POJO that will be converted to a JSON response etc.
ServletInvocableHandlerMethod invocableMethod
= createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(
this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(
this.returnValueHandlers);
}
The returnValueHandlers object is a composite of HandlerMethodReturnValueHandler objects. There are also a lot of different value handlers that can process the result of your method to create ModelAndViewobject expected by the adapter.
Then, it has to render the HTML page that the user will see in the browser. It does that based on the model and the selected view encapsulated in the ModelAndView object.
Now, at this stage, the view gets access to the userForm (as in your example above) from the request scope.

How to map different HTTP methods on the same url to different controllers?

I have my API for a small part of my application split over two controllers, because of (external) requirements on the casing of JSON data in the API (some requests should use camelCasing, while others should use PascalCasing).
Now, I have a url that I want to map with PascalCasing for GET, but camelCasing for PUT, so I tried the following:
[PascalCasing] // custom attribute, part of our code
// We configure all controllers that *don't* have this to use
// camelCasing
public class PascalCasedController : ApiController
{
[HttpGet]
[Route("url/to/my/resource/{id}")]
public IHttpActionResult(int id)
{
return Ok(GetResource(id));
}
}
public class CamelCasedController : ApiController
{
[HttpPut]
[Route("url/to/my/resource/{id}")]
public IHttpActionResult(int id, Resource resource)
{
SaveResource(id, resource);
return Ok();
}
}
The GET request works as expected, but if I try to PUT something there with Fiddler, I get the following error message:
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types:
MyProject.PascalCaseController
MyProject.CamelCaseController
I realize this is probably because WebAPI maps routes to controllers first and actions next, but if HTTP methods are considered, there really isn't any ambiguity here. Is there any way that I can tell WebAPI how to do this, without having to have the methods in the same controller?
#Tomas - There's an interface "System.Web.Http.Dispatcher.IHttpControllerSelector" exposed in System.Web.Http assembly. You can use that interface and create your own HttpControllerSelector. You can then replace the DefaultControllerSelector with your custom controller selector in the HttpConfiguration during AreaRegistration.
httpConfig.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector(services.GetHttpControllerSelector()));
In this custom controller selector you can write your own implementation of SelectController() method of IHttpControllerSelector in which you can call GetControllerMapping() method of IHttpControllerSelector. This will give you the list of all the controllers registered. For every controller you can check for the DeclaredMethods and check for the CustomAttributes for each of the DeclaredMethods. In your case it will be either HttpGetAttribute or HttpPutAttribute.
Check the Method type of the incoming HttpRequestMessage (GET/PUT) and compare it against the value of the CustomAttributes. If you find a match of the combination of incoming request URL and the the respective Http Verb then you take that HttpControllerDiscriptor and return it from the SelectController() method..
This will allow you to have same URL with different methods in two different controllers.

Can a Spring MVC controller return both a HttpServletResponse and a view?

My existing code is like:
String myController(#PathVariable someId, ModelMap map){
....
return "myViewName";
}
Now I want to set a cookie in some cases, so I need to get hold of a HttpServletResponse obj. Can I just add such a response obj to the list of params and operate on it in the controller?
If so, I wonder how my own response is kind of reconciled with the response generated by the JSP that resolves the "myViewName".
Yes.
#RequestMapping
public String myController(#PathVariable someId, ModelMap map, HttpServletResponse response) {
// Do what you need to do on the response, like set a cookie
return "myViewName";
}
Regarding your other question : "how my own response is kind of reconciled with the response generated by the JSP that resolves the "myViewName"."
When you return a view say "myViewName", it will be resolved to a particular resource (JSP View or JSON View or any other view). Once that view resource is obtained depending on what you return, that view does the rendering on to the response. This response object is the same that was passed to the controller function (myController). So say if you set some cookie/headers on the response in the controller function, the response that is being used by the view to do the rendering will also have the same properties.
In case you want to handle the actual rendering/response yourself, you can always get the outputstream of the response and write to it and close the stream. Then the view that you return is just ignored as the dispatcher will check that the response is already handled and will just do post handle stuff.
Hope that clears up for anyone looking for the dispatcher logic behind it.

Resources