Spring Data Rest: how to transform all urls in Kebab Case? - java

I want to use spring-data-rest to expose my repositories via an API. URLs are generated from domains name. Those URLs are camelized.
For example, considering domain named EntityA, the associated url and rel will be entityA (in camelCase).
How can I configure spring-data-rest (or what have I to override) to get kebab-case by default ? (for example, entity-a instead of entityA)
I know that I can use #RestRepository and provide URLs for each repositories. But I'm looking for better solution. I want to use kebak-case for entities and searches methods.

Related

Is there a way to filter or sort data in a RESTful API?

From my understanding (although I am just learning this so I am likely wrong), we use URIs in RESTful APIs to identify resources.
In particular, I've read this comment many times across the web:
#RequestParam is more useful on a traditional web application where data is mostly passed in the query parameters while #PathVariable is more suitable for RESTful web services where URL contains values.
When reading this, it seems like we need to use #PathVariable to properly build a RESTful API instead of using #RequestParam (which is not RESTFUL given the way it identifies resources through query parameters instead of URIs). Is this correct? If so, how can I build a RESTful API that is also capable of sorting or filtering like you would be able to do with #RequestParam?
An example is here:
#GetMapping("/{id}")
Resource<Car> get(#PathVariable Long id) {
Car car = carService.findById(id);
return assembler.toResource(car);
}
vs
#GetMapping("/{id}")
Resource<Car> get(#RequestParam(required = true) Long id) {
Car car = carService.findById(id);
return assembler.toResource(car);
}
For filtering, one option is to use Spring Data JPA Specifications, which allows you to return entities that match your specification, which you define as predicates using the Spring criteria API.
Spring Data JPA also includes sorting capability, utilized by either providing a PageRequest or using Sort directly. Spring defines special parameters such as Pageable and Sort, which can be used to apply paging and sorting to your queries dynamically.
Or you can sort via URL parameters indicating the name of the property on which you wish to sort the results.
You should examine the Pageable type in Spring. Generally, what you've shown here is called something like an item resource: It's one specific Car, specified by ID at /cars/{id}. There's nothing to sort or filter. Those are applied to the collection resource /cars, perhaps something like /cars?sort=year&page=0&size=10. Spring has integrated support for automatically extracting query parameters indicating sorting and paging and passing those instructions to Spring Data repositories.

How to validate website URL field of an JPA entity?

I have a JPA entity representing a company. It has a field representing the company's website URL. I need to validate that the URL is a valid website URL prior to persisting. I suppose only URLs starting with http or https would be valid website URLs. How can I enforce this? I'm using the latest. version of Hibernate as my JPA provider.
#dimitrisli suggests you a correct solution. You should use #URL constraint. If you want to allow a specific protocol - there's a protocol attribute for this constraint:
#URL(protocol = "http")
private String companyUrl;
In addition there's also a regexp attribute which you can use to make your urls to match any pattern you'd like.
#URL(regexp = "^(http|ftp).*")
private String companyUrl;
You can check for more details in the doc (look for URL in the linked section).
Also if you'd like to use the URL instead of String, you can write your own validator implementation for URL class and URL constraint, but note if you'd try to create an invalid URL it will fail during creation process and not when validated. So writing a validator for URL might make sense only if you'd like to check the protocol and things like that.
To write your own validator you would need to implement
public class URLValidator implements ConstraintValidator<org.hibernate.validator.constraints.URL, URL> {
...
}
for more details check out this blog post.
Take a look at the #URL annotation of Hibernate Validator.

Securing JSON-PATCH paths in Spring Boot Data Rest application

I'm using a pretty vanilla spring-boot-starter-data-rest setup and enabled the PATCH method. All is working, but I have a security concern and wonder what's the recommended way of mitigating it.
The problem is that PATCH paths allow reachable entities to be updated from a different endpoint. So, suppose I have a comments endpoint and an article endpoint. Each comment has a to-one association with its article. A user that has permission to edit a comment could then do something like this:
PATCH http://some.domain.foo/api/comments/1234
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/article/title", "value": "foobar2" }
]
and thereby change the title of the article !!
Clearly this ain't good.
In this case, for other parts of the API the association to the "article" needs to be traversable. But it must be read-only.
So... how do I accomplish this in Spring?
Intercept the request?
Implement a handler method?
Write my own Controller from scratch ?
Thanks!
Seems that current implementation on spring-data-rest converts paths to SpEL to apply values directly on beans. See PatchOperation (v2.5.x).
Consider these options:
Instead of json-patch use json-merge PATCH request to send partial updates (with "application/json" or "application/merge-patch+json" content type). This will respect #JsonIgnore and other Jackson annotations and also treat associations differently.
You can disable "json-patch+json" completely, for example by adding a security filter
You can always create your custom json-patch implementation, if you still need it
Use application-level joining not relying on JPA, i.e. only exposing IDs of the linked entities and providing custom links in your ResourceProcessor.
Additionally, if you're using JPA and Comment.article is annotated with #ManyToOne make sure that there's no cascading on association. Even if the article object is modified with patch it won't be saved together with the comment.
There's been a recent fix in Spring Data Rest:
https://github.com/spring-projects/spring-data-rest/issues/2177
The commit that resolves this issue is:
https://github.com/spring-projects/spring-data-rest/commit/5d0687d1a1bb9a84264ecb4cd088907837c382d3
From a quick read this seems to be checking that when applying a JSON patch to an entity a check is done with Jackson that the path should be accessible (read/write).
This should prevent users from being able to specify paths in the JSON patch that aren't normally exposed through POST/GET requests that are mapped directly onto entities through Jackson. I think that if you had marked the article as readable from the comment and the title attribute as writeable then the new code should allow it. Or that's I think what org.springframework.data.rest.webmvc.json.patch.JsonPointerMapping#verify is trying to do.
So if you marked the article on the comment as not being readable through Jackson (#JsonIgnore on the getter) then JSON patch shouldn't allow the title of the article to be set through a comment. The association would still exist, just not be exposed through JSON serialization, I'm not sure if this would cause problems for your application.
The change is released in Spring Data Rest 4.0.0 which is part of Spring Data 2022.0.1 which is in Spring Boot 3.0.2.
https://github.com/spring-projects/spring-data-rest/releases/tag/4.0.0
https://github.com/spring-projects/spring-data-bom/releases/tag/2022.0.1
https://github.com/spring-projects/spring-boot/releases/tag/v3.0.2

Spring MVC: Domain restriction for a single controller

I know that we can use Spring security to restrict access to a controller by IP address or even I can create my custom annotation to add some custom logic for this purpose.
I need to restrict access to a controller in my application to only specific domain name ( a third party ), I checked with this third party provider for the IP ranges which I can use to configure at my end, however they want to have more freedom around IP address and would like me to create access based on the domain name.
I checked through the doc but unable to find any such use cases, Can any one help me to understand if this is possible or I need to go back to IP based access mechanism ?
i think you can use Spring's CORS support for this.
ie if the domain that you expect the request is example.com you can have in your controller method the following annotation
#CrossOrigin(origins = "http://example.com")
This #CrossOrigin annotation enables cross-origin requests only for
this specific method. By default, its allows all origins, all headers,
the HTTP methods specified in the #RequestMapping annotation and a
maxAge of 30 minutes is used. You can customize this behavior by
specifying the value of one of the annotation attributes: origins,
methods, allowedHeaders, exposedHeaders, allowCredentials or maxAge.
In this example, we only allow http://localhost:8080 to send
cross-origin requests.
Have a look in the following url https://spring.io/guides/gs/rest-service-cors/

SpringMVC - dispatcher servler's url pattern style

I am trying to build a restful style API, using springMVC.
When config the url-pattern for springMVC's DispatcherServlet, there seems to have 2 choice, and I need some advice.
Choice A:
config pattern as: <url-pattern>*.action</url-pattern>
and action use path like #RequestMapping("/role/add.action")
Choice B:
config pattern as: <url-pattern>/api/*</url-pattern>
and action use path like #RequestMapping("/api/role/add")
I prefer to use a style that has no suffix, but in that case I need do add a sub path.
But I am not sure which is more proper to use in a project that serve as a backend to provide restful API, with browser / IOS / Android as its client.
There might be a choice C, but I am not sure:
config pattern as: <url-pattern>/*</url-pattern>
and action use path like #RequestMapping("/role/add")
In this case built-in servlet will be override, e.g jsp won't work normally.
But I don't have any jsp, and also, static resource like html / js / css / image / document / music / video are all put on another port or server served by nginx, request to tomcat only provide ajax service via json data.
So in this case is it proper to use choice C, or it has some bad side-effects?
if your goal is restful api my choice is the second one since you identify the resource in the url; say you must manage a role resource you should have some mapping like these ones:
#RequestMapping("/api/role" method = RequestMethod.POST)
to insert a new role (may be the api does not allow this)
#RequestMapping("/api/role/{roleId}" method = RequestMethod.PUT)
to update an existing role
#RequestMapping("/api/role/{roleId}" method = RequestMethod.DELETE)
to delete a role
#RequestMapping("/api/role" method = RequestMethod.GET)
to retrieve roles (you may implement some filters via query string)
The same applies for other resources (User, etc) the naming schema is the same.
I vould avoid option C since I think it's best to have a dedicated mapping for the api if you app also ship a web interface that does not use the api
I will go with the Choice B for RESTful services, consider performing CRUD operations using REST. And you can map the url-pattern as ,
config pattern as: <url-pattern>/api/*</url-pattern>
So to perform add , you can just make sure that you post the JSON object from the page and have a url like /api/add
And in case of delete , you can simply follow the same . Consider you are going to delete a object from the list using its id . You can simply make it out as,
/api/delete/${id}
And handle it like ,
#RequestMapping(value="/{id}", method=RequestMethod.GET)

Categories

Resources