I'm using SpringBoot and Spring REST.
I would like to understand the HTTP PATCH method to update properties of my Model
Is there any good tutorial explaining how to make it works ?
HTTP PATCH method and body to be Send
Controller method and how to manage the update operation
I've noticed that many of the provided answers are all JSON patching or incomplete answers. Below is a full explanation and example of what you need with functioning real world code
First, PATCH is a selective PUT. You use it to update any number of fields for an object or list of objects. In a PUT you typically send the entire object with whatever updates.
PATCH /object/7
{
"objId":7,
"objName": "New name"
}
PUT /object/7
{
"objId":7,
"objName": "New name",
"objectUpdates": true,
"objectStatus": "ongoing",
"scoring": null,
"objectChildren":[
{
"childId": 1
},
............
}
This allows you to update records without huge amounts of endpoints. For example, with above, to update scoring you need object/{id}/scoring, then to update name you need object/{id}/name. Literally one endpoint for every item or you require the front end to post the entire object for every update. If you have a huge object, this can take a lot of network time or mobile data that is unnecessary. The patch lets you have 1 endpoint with the minimal object property sends that a mobile platform should use.
here is an example of a real world use for patch:
#ApiOperation(value = "Patch an existing claim with partial update")
#RequestMapping(value = CLAIMS_V1 + "/{claimId}", method = RequestMethod.PATCH)
ResponseEntity<Claim> patchClaim(#PathVariable Long claimId, #RequestBody Map<String, Object> fields) {
// Sanitize and validate the data
if (claimId <= 0 || fields == null || fields.isEmpty() || !fields.get("claimId").equals(claimId)){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); // 400 Invalid claim object received or invalid id or id does not match object
}
Claim claim = claimService.get(claimId);
// Does the object exist?
if( claim == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND); // 404 Claim object does not exist
}
// Remove id from request, we don't ever want to change the id.
// This is not necessary, you can just do it to save time on the reflection
// loop used below since we checked the id above
fields.remove("claimId");
fields.forEach((k, v) -> {
// use reflection to get field k on object and set it to value v
// Change Claim.class to whatver your object is: Object.class
Field field = ReflectionUtils.findField(Claim.class, k); // find field in the object class
field.setAccessible(true);
ReflectionUtils.setField(field, claim, v); // set given field for defined object to value V
});
claimService.saveOrUpdate(claim);
return new ResponseEntity<>(claim, HttpStatus.OK);
}
The above can be confusing for some people as newer devs don't normally deal with reflection like that. Basically, whatever you pass this function in the body, it will find the associated claim using the given ID, then ONLY update the fields you pass in as a key value pair.
Example body:
PATCH /claims/7
{
"claimId":7,
"claimTypeId": 1,
"claimStatus": null
}
The above will update claimTypeId and claimStatus to the given values for claim 7, leaving all other values untouched.
So the return would be something like:
{
"claimId": 7,
"claimSrcAcctId": 12345678,
"claimTypeId": 1,
"claimDescription": "The vehicle is damaged beyond repair",
"claimDateSubmitted": "2019-01-11 17:43:43",
"claimStatus": null,
"claimDateUpdated": "2019-04-09 13:43:07",
"claimAcctAddress": "123 Sesame St, Charlotte, NC 28282",
"claimContactName": "Steve Smith",
"claimContactPhone": "777-555-1111",
"claimContactEmail": "steve.smith#domain.com",
"claimWitness": true,
"claimWitnessFirstName": "Stan",
"claimWitnessLastName": "Smith",
"claimWitnessPhone": "777-777-7777",
"claimDate": "2019-01-11 17:43:43",
"claimDateEnd": "2019-01-11 12:43:43",
"claimInvestigation": null,
"scoring": null
}
As you can see, the full object would come back without changing any data other than what you want to change. I know there is a bit of repetition in the explanation here, I just wanted to outline it clearly.
There is nothing inherently different in PATCH method as far as Spring is concerned from PUT and POST. The challenge is what you pass in your PATCH request and how you map the data in the Controller. If you map to your value bean using #RequestBody, you'll have to figure what is actually set and what null values mean. Others options would be limit PATCH requests to one property and specify it in url or map the values to a Map.
See also Spring MVC PATCH method: partial updates
Create a rest template using -
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
RestTemplate rest = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
now make the PATCH call
ResponseEntity<Map<String, Object>> response = rest.exchange(api, HttpMethod.PATCH, request,
responseType);
Related
PUT, DELETE, POST can be operated as shown below.
By the way, I do not know how to do GET.
Please help me.
// PUT & DELETE (mapped to WRITE, DELETE of MD-SAL)
public void onDataTreeChanged(Collection<DataTreeModification<GreetingRegistry>> changes) {
for(DataTreeModification<GreetingRegistry> change: changes) {
DataObjectModification<GreetingRegistry> rootNode = change.getRootNode();
if(rootNode.getModificationType() == WRITE) {
...
}
else if(rootNode.getModificationType() == DELETE) {
...
}
}
// POST (mapped to RPC of MD-SAL)
public Future<RpcResult<HelloWorldOutput>> helloWorld(HelloWorldInput input)
{
HelloWorldOutputBuilder helloBuilder = new HelloWorldOutputBuilder();
helloBuilder.setGreeting("Hello " + input.getName());
return RpcResultBuilder.success(helloBuilder.build()).buildFuture();
}
// GET (???)
How should I implement it?
You don't actually have to implement anything for GET in your code, when you want to read from YANG modeled MD-SAL data, the GET method is be available by default, and returns whatever data you ask for in the URL. It is important to point to the correct URL.
If you want to do some processing on data before returning it to the user, you can use RPCs with POST, and do the processing in the RPC based methods. In your example above, you could pot the search keys into HelloWorldInput, do the processing in helloWorld(), and return results in HelloWorldOutput.
I have below code:
Mono<Property> property = propertyService.findById(id);
String title;
Flux<Photo> photos = property.flatMapMany(prop ->
{
title = prop.title + '-' + prop.type;
return photoService.findByPropertyId(prop.getId());
}
);
model.addAttribute("prop", property);
model.addAttribute("title", title);
model.addAttribute("photos", photos);
// ajx is query param coming from request
if(ajx != null && !ajx.isEmpty() && ajx.equals("1"))
return Mono.just("fragments/propertyfrag");
else
return Mono.just("property");
The code shows what I want to achieve but it does not even compile. It gives error saying title and type on prop are not visible.
Note that the last statement is reference to thymeleaf template named property. Withn thyeleaf template I have access to variable prop as if it was not reactive but plain prop object that enables me to directly access parameters on prop object. Does that mean within thymeleaf template property.block() has been performed?
In actual code there is some business logic that I need to do after getting title variable in above code and therefore I cannot avail the use of prop passed as model attribute to thymleaf template to directly get title within thymeleaf.
How to solve this problem?
Keep in mind your Flux<Photo> is an asynchronous process, so it cannot update the title variable outside of it in this imperative style. Note that your Flux<Photo> is also never subscribed to or composed upon, so it will effectively never be invoked...
To answer your other question, yes in Spring Framework 5 having a Mono in the Map passed to Thymeleaf will resolve that Mono lazily and inject the resulting value in the Thymeleaf model.
Pending more information on the use of photos flux, for the title generation you probably need to compose more:
propertyService.findById(id)
.doOnNext(prop -> model.addAttribute("prop", prop)) //reactively add the prop to the model
.flatMapMany(prop -> {
String title = prop.title + '-' + prop.type;
if(validate(title)) //do some validation
return photoService.findByPropertyId(prop.getId());
else
return Mono.error(new IllegalArgumentException("validation failed"));
}) //not sure what you do with the `Photo`s there :/
//for now let's ignore the flux photos and at the end simply emit a String to change the view:
.thenReturn("property"); //then(Mono.just("property")) in older versions of reactor 3.1.x
I've only began with DDD and currently trying to grasp the ways to do different things with it. I'm trying to design it using asynchronous events (no event-sourcing yet) with CQRS. Currently I'm stuck with validation of commands. I've read this question: Validation in a Domain Driven Design , however, none of the answers seem to cover complex validation across different aggregate roots.
Let's say I have these aggregate roots:
Client - contains list of enabled services, each service can have a value-object list of discounts and their validity.
DiscountOrder - an order to enable more discounts on some of the services of given client, contains order items with discount configuration.
BillCycle - each period when bills are generated is described by own billcycle.
Here's the usecase:
Discount order can be submitted. Each new discount period in discount order should not overlap with any of BillCycles. No two discounts of same type can be active at the same time on one service.
Basically, using Hibernate in CRUD style, this would look something similar to (java code, but question is language-agnostic):
public class DiscountProcessor {
...
#Transactional
public void processOrder(long orderId) {
DiscOrder order = orderDao.get(orderId);
BillCycle[] cycles = billCycleDao.getAll();
for (OrderItem item : order.getItems()) {
//Validate billcycle overlapping
for (BillCycle cycle : cycles) {
if (periodsOverlap(cycle.getPeriod(), item.getPeriod())) {
throw new PeriodsOverlapWithBillCycle(...);
}
}
//Validate discount overlapping
for (Discount d : item.getForService().getDiscounts()) {
if (d.getType() == item.getType() && periodsOverlap(d.getPeriod(), item.getPeriod())) {
throw new PeriodsOverlapWithOtherItems(...);
}
}
//Maybe some other validations in future or stuff
...
}
createDiscountsForOrder(order);
}
}
Now here are my thoughts on implementation:
Basically, the order can be in three states: "DRAFT", "VALIDATED" and "INVALID". "DRAFT" state can contain any kind of invalid data, "VALIDATED" state should only contain valid data, "INVALID" should contain invalid data.
Therefore, there should be a method which tries to switch the state of the order, let's call it order.validate(...). The method will perform validations required for shift of state (DRAFT -> VALIDATED or DRAFT -> INVALID) and if successful - change the state and transmit a OrderValidated or OrderInvalidated events.
Now, what I'm struggling with, is the signature of said order.validate(...) method. To validate the order, it requires several other aggregates, namely BillCycle and Client. I can see these solutions:
Put those aggregates directly into the validate method, like
order.validateWith(client, cycles) or order.validate(new
OrderValidationData(client, cycles)). However, this seems a bit
hackish.
Extract the required information from client and cycle
into some kind of intermediate validation data object. Something like
order.validate(new OrderValidationData(client.getDiscountInfos(),
getListOfPeriods(cycles)).
Do validation in a separate service
method which can do whatever it wants with whatever aggregates it
wants (basically similar to CRUD example above). However, this seems
far from DDD, as method order.validate() will become a dummy state
setter, and calling this method will make it possible to bring an
order unintuitively into an corrupted state (status = "valid" but
contains invalid data because nobody bothered to call validation
service).
What is the proper way to do it, and could it be that my whole thought process is wrong?
Thanks in advance.
What about introducing a delegate object to manipulate Order, Client, BillCycle?
class OrderingService {
#Injected private ClientRepository clientRepository;
#Injected private BillingRepository billRepository;
Specification<Order> validSpec() {
return new ValidOrderSpec(clientRepository, billRepository);
}
}
class ValidOrderSpec implements Specification<Order> {
#Override public boolean isSatisfied(Order order) {
Client client = clientRepository.findBy(order.getClientId());
BillCycle[] billCycles = billRepository.findAll();
// validate here
}
}
class Order {
void validate(ValidOrderSpecification<Order> spec) {
if (spec.isSatisfiedBy(this) {
validated();
} else {
invalidated();
}
}
}
The pros and cons of your three solutions, from my perspective:
order.validateWith(client, cycles)
It is easy to test the validation with order.
#file: OrderUnitTest
#Test public void should_change_to_valid_when_xxxx() {
Client client = new ClientFixture()...build()
BillCycle[] cycles = new BillCycleFixture()...build()
Order order = new OrderFixture()...build();
subject.validateWith(client, cycles);
assertThat(order.getStatus(), is(VALID));
}
so far so good, but there seems to be some duplicate test code for DiscountOrderProcess.
#file: DiscountProcessor
#Test public void should_change_to_valid_when_xxxx() {
Client client = new ClientFixture()...build()
BillCycle[] cycles = new BillCycleFixture()...build()
Order order = new OrderFixture()...build()
DiscountProcessor subject = ...
given(clientRepository).findBy(client.getId()).thenReturn(client);
given(cycleRepository).findAll().thenReturn(cycles);
given(orderRepository).findBy(order.getId()).thenReturn(order);
subject.processOrder(order.getId());
assertThat(order.getStatus(), is(VALID));
}
#or in mock style
#Test public void should_change_to_valid_when_xxxx() {
Client client = mock(Client.class)
BillCycle[] cycles = array(mock(BillCycle.class))
Order order = mock(Order.class)
DiscountProcessor subject = ...
given(clientRepository).findBy(client.getId()).thenReturn(client);
given(cycleRepository).findAll().thenReturn(cycles);
given(orderRepository).findBy(order.getId()).thenReturn(order);
given(client).....
given(cycle1)....
subject.processOrder(order.getId());
verify(order).validated();
}
order.validate(new OrderValidationData(client.getDiscountInfos(),
getListOfPeriods(cycles))
Same as the above one, you still need to prepare data for both OrderUnitTest and discountOrderProcessUnitTest. But I think this one is better as order is not tightly coupled with Client and BillCycle.
order.validate()
Similar to my idea if you keep validation in the domain layer. Sometimes it is just not any entity's responsibility, consider domain service or specification object.
#file: OrderUnitTest
#Test public void should_change_to_valid_when_xxxx() {
Client client = new ClientFixture()...build()
BillCycle[] cycles = new BillCycleFixture()...build()
Order order = new OrderFixture()...build();
Specification<Order> spec = new ValidOrderSpec(clientRepository, cycleRepository);
given(clientRepository).findBy(client.getId()).thenReturn(client);
given(cycleRepository).findAll().thenReturn(cycles);
subject.validate(spec);
assertThat(order.getStatus(), is(VALID));
}
#file: DiscountProcessor
#Test public void should_change_to_valid_when_xxxx() {
Order order = new OrderFixture()...build()
Specification<Order> spec = mock(ValidOrderSpec.class);
DiscountProcessor subject = ...
given(orderingService).validSpec().thenReturn(spec);
given(spec).isSatisfiedBy(order).thenReturn(true);
given(orderRepository).findBy(order.getId()).thenReturn(order);
subject.processOrder(order.getId());
assertThat(order.getStatus(), is(VALID));
}
Do the 3 possible states reflect your domain or is that just extrapolation ? I'm asking because your sample code doesn't seem to change Order state but throw an exception when it's invalid.
If it's acceptable for the order to stay DRAFT for a short period of time after being submitted, you could have DiscountOrder emit a DiscountOrderSubmitted domain event. A handler catches the event and (delegates to a Domain service that) examines if the submit is legit or not. It would then issue a ChangeOrderState command to make the order either VALIDATED or INVALID.
You could even suppose that the change is legit by default and have processOrder() directly take it to VALIDATED, until proven otherwise by a subsequent INVALID counter-order given by the validation service.
This is not much different from your third solution or Hippoom's one though, except every step of the process is made explicit with its own domain event. I guess that with your current aggregate design you're doomed to have a third party orchestrator (as un-DDD and transaction script-esque as it may sound) that controls the process, since the DiscountOrder aggregate doesn't have native access to all information to tell if a given transformation is valid or not.
I'm using Spring Validation within a Spring MVC application that delegates validation to Hibernate Validator 5. I'm successfully able to have beans validated and have the messages interpolated by the validator. However, it's important that I also be able to have access to the message template itself, pre-interpolation.
For example, in some bean I have validation #Size(min=5,max=15,message="{my.custom.message}". In a messages.properties file I have entry my.custom.message=test min {min} and max {max}. In my BindingResult, I see the ObjectError with error message "test min 5 and max 15", but I need to look a value up at this point based on the non-interpolated my.custom.message raw value.
Can this be done? If it can't out of the box, can someone point me in the right direction for how I might customize spring's LocalValidatorFactoryBean to preserve this?
Update
I'm looking at extending org.springframework.validation.beanvalidation.SpringValidatorAdapter, and wrapping the getArgumentsForConstraint to automatically append the pre-interpolated message to the returned list of arguments. The notion of exactly what these 'arguments' are and how they're used is unclear to me, but if it's purely used for message interpolation, it seems relatively safe for me to append at the end. Any reason this might not work? Problems it might cause? Better ideas?
Solution
Didn't find any great solutions other than my 'update' above, so I ended up subclassing LocalValidatorFactoryBean with this:
#Override
protected Object[] getArgumentsForConstraint(String objectName, String field, ConstraintDescriptor<?> descriptor) {
if (null == descriptor) return super.getArgumentsForConstraint(objectName, field, descriptor);
Object[] orig = super.getArgumentsForConstraint(objectName, field, descriptor);
if (null == orig || orig.length < 1) return new Object[] { descriptor };
Object[] retval = new Object[orig.length+1];
System.arraycopy(orig, 0, retval, 0, orig.length);
retval[retval.length-1] = descriptor;
return retval;
}
In subsequent code, I look at the last object in this array and test to see if it's an instance of ConstraintDescriptor. Good enough I suppose.
However, it's important that I also be able to have access to the message template itself, pre-interpolation.
In which context do you need to access the template? If it is after validation, then getMessageTemplate() on ConstraintViolation gives you this. If it is within a constraint validator implementation, then you could use getDefaultConstraintMessageTemplate() on ConstraintValidatorContext.
If this was a regular post of a form I could go to Request.Form['somevalue'] and get the value. If this was a get with a query string I could go to Request.QueryString["somevalue"] and get the value.
Where is the raw data when you post an ajax request. I need a value out of the raw data string in a filter method.
Any help will be appreciated!!
Edits below:
public class ValidateAntiForgeryId : FilterAttribute, IAuthorizationFilter {
public void OnAuthorization(AuthorizationContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}
Guid filterGuid;
Guid.TryParse(filterContext.RequestContext.HttpContext.Request.Form["__sessionId"], out filterGuid);
if (filterGuid == Guid.Empty)
throw new AuthenticationException("Authentication failure");
try {
var cookieGuid = (Guid)filterContext.RequestContext.HttpContext.Items["SessionId"];
} catch {
throw new AuthenticationException("Authentication failure");
}
}
The posted data looks like this:
{"SsnLastFour":"2222","AccountNumber":"B112233","__sessionId":"dca0a504-3c40-4118-ae19-afefb9bfc8bd"}
I need access to the __sessionId chunk inside the filter.
There's nothing magic about AJAX posts. They're just plain old HTTP. That means you have plain old HTTP post values, and/or plainold HTTP Get values.
If you're not seeing them, it probably means you're not actually submitting them.
EDIT:
Two issues you did not include in your original question: 1) That this is JSON, and 2) That this is in an AuthorizationFilter (rather than an action method).
Both change the answers. Since ASP.NET does not natively understand JSON post values, you will have to parse them, via Request.InputStream. MVC3 by default has a JSON model binder, but AuthorizationFilters execute before model binders do, so you will be accessing things prior to the model binders being executed, and as such FormsCollection won't be populated (Request.Form[] won't work either, because as I said, asp.net doesn't natively understand JSON).
You may find that installing JSON.net via nuget may help with this task. Or you might just write a simple parse routine, since you know exactly what you're looking for.
You can accept the parameter values the same way you accept in normal form post.
Ex :
$.get("User/Get", { userId : "24"} ,function(data){
alert(data);
});
or
$("#yourDivId").load("User/Get?userId=23");
Your action method should look like
public ActionResult Get(int userId)
{
// you have value present in userId
if(Request.IsAjax())
{
return View("Partial/MyPartialView");
}
return View();
}
One thing you have to remember is, the parameter name of your action method should be same as of what your parameter/querystring name.
The fitlerContext has an ActionParameters collection which should have the parsed JSON properties (in case that helps). This may be easier than parsing the InputStream.
var sessionId = filterContext.ActionParameters["__sessionId"];