Должен ли я поместить идентификатор моей сущности в URL или в форму как скрытое поле? - PullRequest
0 голосов
/ 24 мая 2018

Я думаю, что с точки зрения REST, идентификатор должен быть помещен в URL, что-то вроде:

https://example.com/module/[ID]

, а затем я вызываю GET, PUT, DELETE для этого URL.Это вроде ясно, я думаю.В контроллерах Spring MVC я получал бы идентификатор с помощью @PathVariable.Работает.

Теперь моя практическая проблема с Spring MVC заключается в том, что если я сделаю это, мне НЕ нужно включать идентификатор как часть формы (также), Spring выдает предупреждения типа

Skipping URI variable 'id' since the request contains a bind value with the same name.

в противном случае.И также имеет смысл отправлять его только один раз, верно?Что бы вы сделали, если они не совпадают?

Это было бы хорошо, но у меня есть собственный валидатор для компонента поддержки формы, который должен знать идентификатор!(Необходимо проверить, используется ли определенное уникальное имя для другого экземпляра объекта, но не может, не зная идентификатора отправленной формы).

Я не нашел хорошего способа сообщить валидаторуэтот идентификатор из @PathVariable, так как проверка происходит еще до того, как код в моем методе контроллера выполняется.

Как бы вы решили эту дилемму?

Это мой контроллер (изменен):

@Controller
@RequestMapping("/channels")
@RoleRestricted(resource = RoleResource.CHANNEL_ADMIN)
public class ChannelAdminController
{
    protected ChannelService channelService;
    protected ChannelEditFormValidator formValidator;

    @Autowired
    public ChannelAdminController(ChannelService channelService, ChannelEditFormValidator formValidator)
    {
        this.channelService = channelService;
        this.formValidator = formValidator;
    }

    @RequestMapping(value = "/{channelId}/admin", method = RequestMethod.GET)
    public String editChannel(@PathVariable Long channelId, @ModelAttribute("channelForm") ChannelEditForm channelEditForm, Model model)
    {
        if (channelId > 0)
        {
            // Populate from persistent entity
        }
        else
        {
            // Prepare form with default values
        }
        return "channel/admin/channel-edit";
    }

    @RequestMapping(value = "/{channelId}/admin", method = RequestMethod.PUT)
    public String saveChannel(@PathVariable Long channelId, @ModelAttribute("channelForm") @Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
    {
        try
        {
            // Has to validate in controller if the name is already used by another channel, since in the validator, we don't know the channelId
            Long nameChannelId = channelService.getChannelIdByName(channelEditForm.getName());
            if (nameChannelId != null && !nameChannelId.equals(channelId))
                result.rejectValue("name", "channel:admin.f1.error.name");
        }
        catch (EmptyResultDataAccessException e)
        {
            // That's fine, new valid unique name (not so fine using an exception for this, but you know...)
        }

        if (result.hasErrors())
        {
            return "channel/admin/channel-edit";
        }

        // Copy properties from form to ChannelEditRequest DTO
        // ...

        // Save
        // ...

        redirectAttributes.addFlashAttribute("successMessage", new SuccessMessage.Builder("channel:admin.f1.success", "Success!").build());
        // POST-REDIRECT-GET
        return "redirect:/channels/" + channelId + "/admin";
    }


    @InitBinder("channelForm")
    protected void initBinder(WebDataBinder binder)
    {
        binder.setValidator(formValidator);
    }
}

Ответы [ 4 ]

0 голосов
/ 07 июня 2018

Я думаю, что наконец-то нашел решение.

Как оказалось, Spring также связывает переменные пути для формирования bean-компонентов!Я нигде не нашел этого документированного и не ожидал бы этого, но, пытаясь переименовать переменную пути, как предложил @DavidW (что я ожидал бы иметь только локальный эффект в моем методе контроллера), я понял,что некоторые вещи сломались из-за вышеупомянутого.

Итак, в основном, решение состоит в том, чтобы иметь свойство ID и для объекта поддержки формы, НО не включая скрытое поле ввода в HTMLформа.Таким образом Spring будет использовать переменную path и заполнять ее в форме.Локальный параметр @PathVariable в методе контроллера может быть даже пропущен.

0 голосов
/ 06 июня 2018

То, что вы сказали, является правильным, правильный способ создания остальных API - это упоминать идентификатор ресурса в переменной пути, если вы посмотрите на некоторые примеры из swagger сейчас как открытые API, вы можете найти похожие примеры там

для вас правильным решением было бы использовать пользовательский валидатор, подобный этому

import javax.validation.Validator;`
import org.apache.commons.lang3.StringUtils;`
import org.springframework.validation.Errors;`
importorg.springframework.validation.beanvalidation.CustomValidatorBean;`

public class MyValidator extends CustomValidatorBean {`
    public void myvalidate(Object target,Errors errors,String flag,Profile profile){
        super.validate(target,errors);
        if(StringUtils.isEmpty(profile.name())){
            errors.rejectValue("name", "NotBlank.profilereg.name", new Object[] { "name" }, "Missing Required Fields");
        }       

    }           

}

Это позволит убедиться, что все поля проверены, и вам не нужно передавать идентификатор в форме.

0 голосов
/ 06 июня 2018

Не могли бы вы просто устранить неоднозначность 2 (переменные шаблона URI и параметры), используя другое имя для переменной шаблона URI?

@RequestMapping(value = "/{chanId}/admin", method = RequestMethod.PUT)
public String saveChannel(@PathVariable Long chanId, @ModelAttribute("channelForm") @Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
{
[...]
0 голосов
/ 04 июня 2018

Я думаю, что самый простой способ решить эту проблему - позволить базе данных обрабатывать дубликаты: добавить уникальное ограничение для столбца базы данных.(или JPA, добавив @UniqueConstraint). Но вам все равно придется перехватить исключение базы данных и преобразовать его в удобное для пользователя сообщение.

Таким образом, вы можете упростить пружинный валидатор MVC: проверять только поля, безнеобходимо запросить базу данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...