"${#fields.hasErrors('*')}" always false - Redirect Problem - forms

My controller looks like this:
#PostMapping("/event/{id}")
public String save(#PathVariable("id") long id, #Valid Form form, BindingResult bindingResult ) {
if (!bindingResult.hasErrors()) {
//No errors
//No return
}
return "redirect:/event/{id}";
}
My #GetMapping is:
#GetMapping("/event/{id}")
public ModelAndView eventDetail(#PathVariable("id") long id) {
ModelAndView model = new ModelAndView("event/details");
Event event = eventoRepository.findById(id).get();
model.addObject("event", evento);
model.addObject("guests", event.getGuests());
model.addObject("guest",new Guest());
return model;
}
I know that "${#fields.hasErrors('*')}" is always false because redirect. (right?)
How return to this path /event/{id} without redirect?

I know that "${#fields.hasErrors('*')}" is always false because redirect. (right?)
Right. It looks like you always redirect. Because of that a method annotated with #GetMapping("/event/{id}") is called, the form most likely is reseted to fresh state and there are no more errors making expression always false.
How return to this path /event/{id} without redirect?
Simply return name of the view (template) containing the form. Most likely it's the same what's returned by method annotated with #GetMapping("/event/{id}").
You should follow an approach from this guide. Return without redirect if the form contains error, and redirect otherwise.
Edit:
You should also provide additional objects to the model. Instead of populating model in each method (Get, Post etc.) you may extract common objects to a method annotated with #ModelAttribute. According to javadoc such a method can accept similar parameters as methods annotated with #RequestMapping.
For your case something like this should work fine:
#ModelAttribute
void supplyModel(#PathVariable("id") long id, Model model) {
Event event = eventoRepository.findById(id).get();
model.addAttribute("event", evento);
model.addAttribute("guests", event.getGuests());
model.addAttribute("guest",new Guest());
}
#GetMapping("/event/{id}")
public String eventDetail(#PathVariable("id") long id) {
return "event/details";
}
#PostMapping("/event/{id}")
public String save(#PathVariable("id") long id, #Valid Form form, BindingResult bindingResult ) {
if (bindingResult.hasErrors()) {
// Has errors
return "event/details";
}
// No errors
return "redirect:/event/" + id;
}

Related

Error code 404 or 405, for GET and PUT methods

Good day I have created a simple Web API for my app.
I was able to successfully make a POST method without conflict, and also GET without parameters(https://www.something.com/api/something/) works too, but when I insert parameter for my GET (https://www.something.com/api/something/1) it gives me a 404 on POSTMAN. When I try PUT Method, and I don't put a parameter, it gives me a 405. And 404 when I put a parameter. Below is my code.
I'm using MongoDB for my database.
database has _id, and category as Partition key.
_id also works as id(Property name JSON)
Controller
// To get a specific record
[HttpGet("{id:length(24)}")]
public ActionResult< SomeModel > Get(string id)
{
var some = _someThing.Get(id);
if (some == null)
{
return NotFound();
}
return some;
}
// For Updating a record
[HttpPut("{id:length(24)}")]
public IActionResult Update(string id, SomeModel pModel)
{
var something = _someModel.Get(id);
if (something == null)
{
return NotFound();
}
_someModel.Update(id, pModel);
return NoContent();
}
Services
// For Finding a specific record
public SomeModel Get(string id) =>
_scores.Find< SomeModel >(scores => scores.id == id).FirstOrDefault();
// For Updating record
public void Update(string id, SomeModel newScore) =>
_scores.ReplaceOne(scores => scores.id == id, newScore);
From this Microsoft page section about route constraints:
Length: Matches a string with the specified length or within a specified range of lengths.
It looks to me like your controller is expecting an ID of 24 characters long.
Try changing it to this:
[HttpGet("{id:length(1,24)}")]
Or using minlength or maxlength instead.

Efficient method to Update using JAX-RS

I am working on a JPA/Jersey web app and want to know if there is a better way to update a record. Currently, I am doing:
#PUT
#Path("update/{id}")
#Produces("application/json")
#Consumes("application/x-www-form-urlencoded")
public Response createDevice(
#PathParam("id") int id,
#FormParam("name") String name,
#FormParam("type") int type
/*MultivaluedMap<String, String> formParams*/
) {
try {
Devices newDevice = entityManager.find(Devices.class, id);
if(name==null){name=newDevice.getName();}
if(type != newDevice.getType()){newDevice.setType(type);}
newDevice.setName(name);
//newDevice.setType(type);
entityManager.merge(newDevice);
return Response.status(201).entity(new ResponseObject("success")).build();
} finally {
entityManager.close();
}
}
This works, but if my Devices table had more fields, I would have to check for equality of ALL fields with the values on the original object to see if they've changed, so that my
entityManager.merge(newDevice);
will only change the values passed in.
Is there a better way to do this?

Gson: Custom deserialization if certain field is present

I have a class that looks as follows
class Person {
Long id;
String firstName;
int age;
}
and my input either looks like this:
{ "id": null, "firstName": "John", "age": 10 }
or like this:
{ "id": 123 }
The first variant represents a "new" (non-persisted) person and the second refers to a person by its database id.
If id is non-null, I would like to load the object from database during deserialization, otherwise fallback on regular parsing and deserialize it as a new object.
What I've tried: I currently have a JsonDeserializer for database-deserialization, but as I understand it, there is no way to "fall back" on regular parsing. According to this answer I should use a TypeAdapterFactory and the getDelegateAdapter. My problem with this approach is that I'm given a JsonReader (and not for instance a JsonElement) so I can't determine if the input contains a valid id without consuming input.
Any suggestions on how to solve this?
I don't know if I understand your question correctly, but if you already have a JsonDeserializer, you should have a method like this one in there:
public Person deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { ... }
In this method you have the object context of type JsonDeserializationContext, which allows you to invoke default deserialization on a specified object.
So, you could do something like inside your custom deserializer:
//If id is null...
Person person = context.deserialize(json, Person.class);
See JsonDeserializationContext documentation.
I think I managed to figure this out with the help of the answer over here.
Here is a working type adapter factory:
new TypeAdapterFactory() {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter =
gson.getAdapter(JsonElement.class);
// Are we asked to parse a person?
if (!type.getType().equals(Person.class))
return null;
return new TypeAdapter<T>() {
#Override
public T read(JsonReader reader) throws IOException {
JsonElement tree = elementAdapter.read(reader);
JsonElement id = tree.getAsJsonObject().get("id");
if (id == null)
return delegate.fromJsonTree(tree);
return (T) findObj(id.getAsLong());
}
#Override
public void write(JsonWriter writer, T obj) throws IOException {
delegate.write(writer, obj);
}
};
}
}
I haven't fully tested it yet and I'll get back and revise it if needed. (Posting it now to open up for feed back on the approach.)

Preserving model state with Post/Redirect/Get pattern

At the moment I am trying to implement the Post/Redirect/Get pattern with Spring MVC 3.1. What is the correct way to preserve and recover the model data + validation errors? I know that I can preserve the model and BindingResult with the RedirectAttributes in my POST method. But what is the correct way of recovering them in the GET method from the flash scope?
I have done the following to POST:
#RequestMapping(value = "/user/create", method = RequestMethod.POST)
public String doCreate(#ModelAttribute("user") #Valid User user, BindingResult result, RedirectAttributes rA){
if(result.hasErrors()){
rA.addFlashAttribute("result", result);
rA.addFlashAttribute("user", user);
return "redirect:/user";
}
return "redirect:/user/success";
}
And the following to GET the user creation form:
#RequestMapping(value = "/user", method = RequestMethod.GET)
public ModelAndView showUserForm(#ModelAttribute("user") User user, ModelAndView model){
model.addObject("user", user);
model.setViewName("userForm");
return model;
}
This allows me to preserve the given user data in the case of an error. But what is the correct way of recovering the errors?(BindingResult) I'd like to show them in the form with the spring form tags:
<form:errors path="*" />
In addition it would be interesting how to access the flash scope from the get method?
public class BindingHandlerInterceptor extends HandlerInterceptorAdapter {
public static final String BINDING_RESULT_FLUSH_ATTRIBUTE_KEY = BindingHandlerInterceptor.class.getName() + ".flashBindingResult";
private static final String METHOD_GET = "GET";
private static final String METHOD_POST = "POST";
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(METHOD_POST.equals(request.getMethod())) {
BindingResult bindingResult = getBindingResult(modelAndView);
FlashMap outFlash = RequestContextUtils.getOutputFlashMap(request);
if(bindingResult == null || ! bindingResult.hasErrors() || outFlash == null ) {
return;
}
outFlash.put(BINDING_RESULT_FLUSH_ATTRIBUTE_KEY, bindingResult);
}
Map<String, ?> inFlash = RequestContextUtils.getInputFlashMap(request);
if(METHOD_GET.equals(request.getMethod()) && inFlash != null && inFlash.containsKey(BINDING_RESULT_FLUSH_ATTRIBUTE_KEY)) {
BindingResult flashBindingResult = (BindingResult)inFlash.get(BINDING_RESULT_FLUSH_ATTRIBUTE_KEY);
if(flashBindingResult != null) {
BindingResult bindingResult = getBindingResult(modelAndView);
if(bindingResult == null) {
return;
}
bindingResult.addAllErrors(flashBindingResult);
}
}
}
public static BindingResult getBindingResult(ModelAndView modelAndView) {
if(modelAndView == null) {
return null;
}
for (Entry<String,?> key : modelAndView.getModel().entrySet()) {
if(key.getKey().startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return (BindingResult)key.getValue();
}
}
return null;
}
}
Why don't you show the update form after the binding fails, so the user can try to resubmit the form?
The standard approach for this seems to be to return the update form view from the POST handler method.
if (bindingResult.hasErrors()) {
uiModel.addAttribute("user", user);
return "user/create";
}
You can then display errors with the form:errors tag.
what is the correct way of recovering them in the GET method from the
flash scope
I'm not sure I understand what you mean by recovering them. What you add as flash attributes before the redirect will be in the model after the redirect. There is nothing special that needs to be done for that. I gather you're trying to ask something else but I'm not sure what that is.
As phahn pointed out why do you redirect on error? The common way to handle this is to redirect on success.

Pass a parameter to REST web service via URL

I'm creating a small REST web service using Netbeans. This is my code:
private UriInfo context;
private String name;
public GenericResource() {
}
#GET
#Produces("text/html")
public String getHtml() {
//TODO return proper representation object
return "Hello "+ name;
}
#PUT
#Consumes("text/html")
public void putHtml(String name) {
this.name = name;
}
I'm calling the get method ok since when I call http://localhost:8080/RestWebApp/resources/greeting I get "Hello null" but I'm trying to pass a parameter using http://localhost:8080/RestWebApp/resources/greeting?name=Krt_Malta but the PUT method is not being called... Is this the correct way to pass a parameter or am I missing something?
I'm a newbie to Rest bdw, so sry if it's a simple question.
Thanks! :)
Krt_Malta
The second URL is a plain GET request. To pass data to a PUT request you have to pass it using a form. The URL is reserved for GET as far as I know.
If you build the HTTP-header yourself, you must use POST instead of GET:
GET /RestWebApp/resources/greeting?name=Krt_Malta HTTP/1.0
versus
POST /RestWebApp/resources/greeting?name=Krt_Malta HTTP/1.0
If you use a HTML-form, you must set the method-attribute to "PUT":
<form action="/RestWebApp/resources/greeting" method="PUT">
For JAX-RS to mactch a method annotated with #PUT, you need to submit a PUT request. Normal browsers don't do this but cURL or a HTTP client library can be used.
To map a query parameter to a method argument, JAX-RS provides the #QueryParam annotation.
public void putWithQueryParam(#QueryParam("name") String name) {
// do something
}
You can set:
#PUT
#path{/putHtm}
#Consumes("text/html")
public void putHtml(String name) {
this.name = name;
}
and if you use something like google`s Volley library you can do.
GsonRequest<String> asdf = new GsonRequest<String>(ConnectionProperties.happyhourURL + "/putHtm", String.class, yourString!!, true,
new Response.Listener<Chain>() {
#Override
public void onResponse(Chain response) {
}
}, new CustomErrorListener(this));
MyApplication.getInstance().addToRequestQueue(asdf);
and GsonRequest will look like:
public GsonRequest(String url, Class<T> _clazz, T object, boolean needLogin, Listener<T> successListener, Response.ErrorListener errorlistener) {
super(Method.PUT, url, errorlistener);
_headers = new HashMap<String, String>();
this._clazz = _clazz;
this.successListener = successListener;
this.needsLogin = needLogin;
_object = object;
setTimeout();
}