跳至主要内容

Getting started with Java EE8 MVC(4)-Handle PUT and DELETE request

Handle PUT and DELETE request

When you try to submit a PUT or DELETE http request via form submission, and you want to invoke the methods annotated with @PUT or @DELETE in the Controller to handle the request.
Unfortunately it does not work.
There is a simple solution which can help us to overcome this issue.
If you have used Spring MVC before and I think you could know there is a HiddenMethodFilter in the Spirng MVC to fix this issue. Java EE 8 MVC does not ship the similar Filter, we can create one.

Solution

  1. Create a Filter to convert the request method to the target HTTP method.
    @WebFilter(filterName = "HiddenHttpMethodFilter", urlPatterns = {"/*"}, dispatcherTypes = {DispatcherType.REQUEST})
    public class HiddenHttpMethodFilter implements Filter {
    
        @Inject
        Logger log;
    
        /**
        * Default method parameter: {@code _method}
        */
        public static final String DEFAULT_METHOD_PARAM = "_method";
    
        private String methodParam = DEFAULT_METHOD_PARAM;
    
        /**
        * Set the parameter name to look for HTTP methods.
        *
        * @see #DEFAULT_METHOD_PARAM
        */
        public void setMethodParam(String methodParam) {
            this.methodParam = methodParam;
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
                throws ServletException, IOException {
            log.log(Level.INFO, "entering HttpHiddenFilter...");
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            String paramValue = request.getParameter(this.methodParam);
            log.log(Level.INFO, "paramValue @" + paramValue);
            log.log(Level.INFO, "request method @" + request.getMethod());
    
            if ("POST".equals(request.getMethod()) && paramValue != null && paramValue.trim().length() > 0) {
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
                filterChain.doFilter(wrapper, response);
            } else {
                filterChain.doFilter(request, response);
            }
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void destroy() {
    
        }
    
        /**
        * Simple {@link HttpServletRequest} wrapper that returns the supplied
        * method for {@link HttpServletRequest#getMethod()}.
        */
        private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
    
            private final String method;
    
            public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
                super(request);
                this.method = method;
            }
    
            @Override
            public String getMethod() {
                return this.method;
            }
        }
    
    }
    
  2. Add a hidden field named _method in the form, which value is the target HTTP method.
    <form action="${markDoingUrl}" method="post">
        <input type="hidden" name="_method" value="PUT"><jsp:text /></input>
        <input type="hidden" name="status" value="DOING"><jsp:text /></input>
        <button type="submit" class="btn btn-sm btn-primary">
            <span class="glyphicon glyphicon-play" aria-hidden="true"><jsp:text /></span>
            START
        </button>
    </form>
    
  3. The @PUT method in the TaskController.
    @PUT
    @Path("{id}/status")
    public Response updateStatus(@PathParam(value = "id") Long id, @NotNull @FormParam(value = "status") String status) {
        log.log(Level.INFO, "updating status of the existed task@id:{0}, status:{1}", new Object[]{id, status});
    
        Task task = taskRepository.findById(id);
    
        task.setStatus(Task.Status.valueOf(status));
    
        taskRepository.update(task);
    
        flashMessage.notify(Type.info, "Task status was updated successfully!");
    
        return Response.ok("redirect:tasks").build();
    }
    

Source codes

  1. Clone the codes from my github.com account.
    https://github.com/hantsy/ee8-sandbox/
  2. Open the mvc project in NetBeans IDE.
  3. Run it on Glassfish.
  4. After it is deployed and runging on Glassfish application server, navigate http://localhost:8080/ee8-mvc/mvc/tasks in browser.

评论

此博客中的热门博文

Build a Reactive application with Angular 5 and Spring Boot 2.0

I have created a post to describe Reactive programming supports in Spring 5 and its subprojects, all codes of this article are updated the latest Spring 5 RELEASE, check spring-reactive-sample under my Github account.
In this post, I will create a simple blog system, including:
A user can sign in and sign out.An authenticated user can create a post.An authenticated user can update a post.Only the user who has ADMIN role can delete a post.All users(including anonymous users) can view post list and post details.An authenticated user can add his comments to a certain post. The backend will be built with the latest Spring 5 reactive stack, including:
Spring Boot 2.0, at the moment the latest version is 2.0.0.M7Spring Data MongoDB supports reactive operations for MongoDBSpring Session adds reactive support for WebSessionSpring Security 5 aligns with Spring 5 reactive stack The frontend is an Angular based SPA and it will be generated by Angular CLI.
The source code is hosted on Github, …

Activating CDI in JSF 2.3

Activating CDI in JSF 2.3 When I upgraed my Java EE 7 sample to the newest Java EE 8, the first thing confused me is the CDI beans are not recoganized in Facelects template in a JSF 2.3 based web applicaiton, which is working in the development version, but in the final release version, they are always resolved as null. I filed an issue on Mojarra and discussed it with the developers from communities and the JSF experts.
According to the content of README, In a JSF 2.3 application, to activate CDI support, declaring a 2.3 versioned faces-config.xml and adding javax.faces.ENABLE_CDI_RESOLVER_CHAIN in web.xml is not enough, you have to declare @FacesConfig annotated class to enable CDI.
Here is the steps I created a workable JSF 2.3 applicatoin in Java EE 8.
Create a Java web application, this can be done easily by NetBeans IDE, or generated by Maven archetype, for exmaple.
$ mvn archetype:generate -DgroupId=com.example -DartifactId=demo -DarchetypeArtifactId=maven-archetype-w…

JPA 2.1: Attribute Converter

JPA 2.1: Attribute Converter If you are using Hibernate, and want a customized type is supported in your Entity class, you could have to write a custom Hibernate Type. JPA 2.1 brings a new feature named attribute converter, which can help you convert your custom class type to JPA supported type. Create an Entity Reuse thePostentity class as example. @Entity @Table(name="POSTS") public class Post implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="ID") private Long id; @Column(name="TITLE") private String title; @Column(name="BODY") private String body; @Temporal(javax.persistence.TemporalType.DATE) @Column(name="CREATED") private Date created; @Column(name="TAGS") private List<String> tags=new ArrayList<>(); } Create an attribute converter In this example…