001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.filter; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.nio.charset.Charset; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.Enumeration; 026import java.util.LinkedHashMap; 027import java.util.LinkedHashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031 032import javax.servlet.FilterChain; 033import javax.servlet.ServletException; 034import javax.servlet.http.HttpServletRequest; 035import javax.servlet.http.HttpServletRequestWrapper; 036import javax.servlet.http.HttpServletResponse; 037 038import org.springframework.http.HttpInputMessage; 039import org.springframework.http.MediaType; 040import org.springframework.http.converter.FormHttpMessageConverter; 041import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; 042import org.springframework.http.server.ServletServerHttpRequest; 043import org.springframework.lang.Nullable; 044import org.springframework.util.Assert; 045import org.springframework.util.MultiValueMap; 046import org.springframework.util.StringUtils; 047 048/** 049 * {@link javax.servlet.Filter} that makes form encoded data available through 050 * the {@code ServletRequest.getParameter*()} family of methods during HTTP PUT 051 * or PATCH requests. 052 * 053 * <p>The Servlet spec requires form data to be available for HTTP POST but 054 * not for HTTP PUT or PATCH requests. This filter intercepts HTTP PUT and PATCH 055 * requests where content type is {@code 'application/x-www-form-urlencoded'}, 056 * reads form encoded content from the body of the request, and wraps the ServletRequest 057 * in order to make the form data available as request parameters just like 058 * it is for HTTP POST requests. 059 * 060 * @author Rossen Stoyanchev 061 * @since 3.1 062 * @deprecated as of 5.1 in favor of {@link FormContentFilter} which is the same 063 * but also handles DELETE. 064 */ 065@Deprecated 066public class HttpPutFormContentFilter extends OncePerRequestFilter { 067 068 private FormHttpMessageConverter formConverter = new AllEncompassingFormHttpMessageConverter(); 069 070 071 /** 072 * Set the converter to use for parsing form content. 073 * <p>By default this is an instance of {@link AllEncompassingFormHttpMessageConverter}. 074 */ 075 public void setFormConverter(FormHttpMessageConverter converter) { 076 Assert.notNull(converter, "FormHttpMessageConverter is required."); 077 this.formConverter = converter; 078 } 079 080 public FormHttpMessageConverter getFormConverter() { 081 return this.formConverter; 082 } 083 084 /** 085 * The default character set to use for reading form data. 086 * This is a shortcut for:<br> 087 * {@code getFormConverter.setCharset(charset)}. 088 */ 089 public void setCharset(Charset charset) { 090 this.formConverter.setCharset(charset); 091 } 092 093 094 @Override 095 protected void doFilterInternal(final HttpServletRequest request, HttpServletResponse response, 096 FilterChain filterChain) throws ServletException, IOException { 097 098 if (("PUT".equals(request.getMethod()) || "PATCH".equals(request.getMethod())) && isFormContentType(request)) { 099 HttpInputMessage inputMessage = new ServletServerHttpRequest(request) { 100 @Override 101 public InputStream getBody() throws IOException { 102 return request.getInputStream(); 103 } 104 }; 105 MultiValueMap<String, String> formParameters = this.formConverter.read(null, inputMessage); 106 if (!formParameters.isEmpty()) { 107 HttpServletRequest wrapper = new HttpPutFormContentRequestWrapper(request, formParameters); 108 filterChain.doFilter(wrapper, response); 109 return; 110 } 111 } 112 113 filterChain.doFilter(request, response); 114 } 115 116 private boolean isFormContentType(HttpServletRequest request) { 117 String contentType = request.getContentType(); 118 if (contentType != null) { 119 try { 120 MediaType mediaType = MediaType.parseMediaType(contentType); 121 return (MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)); 122 } 123 catch (IllegalArgumentException ex) { 124 return false; 125 } 126 } 127 else { 128 return false; 129 } 130 } 131 132 133 private static class HttpPutFormContentRequestWrapper extends HttpServletRequestWrapper { 134 135 private MultiValueMap<String, String> formParameters; 136 137 public HttpPutFormContentRequestWrapper(HttpServletRequest request, MultiValueMap<String, String> parameters) { 138 super(request); 139 this.formParameters = parameters; 140 } 141 142 @Override 143 @Nullable 144 public String getParameter(String name) { 145 String queryStringValue = super.getParameter(name); 146 String formValue = this.formParameters.getFirst(name); 147 return (queryStringValue != null ? queryStringValue : formValue); 148 } 149 150 @Override 151 public Map<String, String[]> getParameterMap() { 152 Map<String, String[]> result = new LinkedHashMap<>(); 153 Enumeration<String> names = getParameterNames(); 154 while (names.hasMoreElements()) { 155 String name = names.nextElement(); 156 result.put(name, getParameterValues(name)); 157 } 158 return result; 159 } 160 161 @Override 162 public Enumeration<String> getParameterNames() { 163 Set<String> names = new LinkedHashSet<>(); 164 names.addAll(Collections.list(super.getParameterNames())); 165 names.addAll(this.formParameters.keySet()); 166 return Collections.enumeration(names); 167 } 168 169 @Override 170 @Nullable 171 public String[] getParameterValues(String name) { 172 String[] parameterValues = super.getParameterValues(name); 173 List<String> formParam = this.formParameters.get(name); 174 if (formParam == null) { 175 return parameterValues; 176 } 177 if (parameterValues == null || getQueryString() == null) { 178 return StringUtils.toStringArray(formParam); 179 } 180 else { 181 List<String> result = new ArrayList<>(parameterValues.length + formParam.size()); 182 result.addAll(Arrays.asList(parameterValues)); 183 result.addAll(formParam); 184 return StringUtils.toStringArray(result); 185 } 186 } 187 } 188 189}