001/* 002 * Copyright 2002-2015 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.test.web.servlet.result; 018 019import java.util.Enumeration; 020import java.util.Map; 021 022import javax.servlet.http.Cookie; 023import javax.servlet.http.HttpServletRequest; 024 025import org.springframework.core.style.ToStringCreator; 026import org.springframework.http.HttpHeaders; 027import org.springframework.mock.web.MockHttpServletRequest; 028import org.springframework.mock.web.MockHttpServletResponse; 029import org.springframework.test.web.servlet.MvcResult; 030import org.springframework.test.web.servlet.ResultHandler; 031import org.springframework.util.LinkedMultiValueMap; 032import org.springframework.util.MultiValueMap; 033import org.springframework.util.ObjectUtils; 034import org.springframework.validation.BindingResult; 035import org.springframework.validation.Errors; 036import org.springframework.web.method.HandlerMethod; 037import org.springframework.web.servlet.FlashMap; 038import org.springframework.web.servlet.HandlerInterceptor; 039import org.springframework.web.servlet.ModelAndView; 040import org.springframework.web.servlet.support.RequestContextUtils; 041 042/** 043 * Result handler that prints {@link MvcResult} details to a given output 044 * stream — for example: {@code System.out}, {@code System.err}, a 045 * custom {@code java.io.PrintWriter}, etc. 046 * 047 * <p>An instance of this class is typically accessed via one of the 048 * {@link MockMvcResultHandlers#print print} or {@link MockMvcResultHandlers#log log} 049 * methods in {@link MockMvcResultHandlers}. 050 * 051 * @author Rossen Stoyanchev 052 * @author Sam Brannen 053 * @since 3.2 054 */ 055public class PrintingResultHandler implements ResultHandler { 056 057 private final ResultValuePrinter printer; 058 059 060 /** 061 * Protected constructor. 062 * @param printer a {@link ResultValuePrinter} to do the actual writing 063 */ 064 protected PrintingResultHandler(ResultValuePrinter printer) { 065 this.printer = printer; 066 } 067 068 /** 069 * @return the result value printer 070 */ 071 protected ResultValuePrinter getPrinter() { 072 return this.printer; 073 } 074 075 /** 076 * Print {@link MvcResult} details. 077 */ 078 @Override 079 public final void handle(MvcResult result) throws Exception { 080 this.printer.printHeading("MockHttpServletRequest"); 081 printRequest(result.getRequest()); 082 083 this.printer.printHeading("Handler"); 084 printHandler(result.getHandler(), result.getInterceptors()); 085 086 this.printer.printHeading("Async"); 087 printAsyncResult(result); 088 089 this.printer.printHeading("Resolved Exception"); 090 printResolvedException(result.getResolvedException()); 091 092 this.printer.printHeading("ModelAndView"); 093 printModelAndView(result.getModelAndView()); 094 095 this.printer.printHeading("FlashMap"); 096 printFlashMap(RequestContextUtils.getOutputFlashMap(result.getRequest())); 097 098 this.printer.printHeading("MockHttpServletResponse"); 099 printResponse(result.getResponse()); 100 } 101 102 /** 103 * Print the request. 104 */ 105 protected void printRequest(MockHttpServletRequest request) throws Exception { 106 this.printer.printValue("HTTP Method", request.getMethod()); 107 this.printer.printValue("Request URI", request.getRequestURI()); 108 this.printer.printValue("Parameters", getParamsMultiValueMap(request)); 109 this.printer.printValue("Headers", getRequestHeaders(request)); 110 } 111 112 protected final HttpHeaders getRequestHeaders(MockHttpServletRequest request) { 113 HttpHeaders headers = new HttpHeaders(); 114 Enumeration<?> names = request.getHeaderNames(); 115 while (names.hasMoreElements()) { 116 String name = (String) names.nextElement(); 117 Enumeration<String> values = request.getHeaders(name); 118 while (values.hasMoreElements()) { 119 headers.add(name, values.nextElement()); 120 } 121 } 122 return headers; 123 } 124 125 protected final MultiValueMap<String, String> getParamsMultiValueMap(MockHttpServletRequest request) { 126 Map<String, String[]> params = request.getParameterMap(); 127 MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<String, String>(); 128 for (String name : params.keySet()) { 129 if (params.get(name) != null) { 130 for (String value : params.get(name)) { 131 multiValueMap.add(name, value); 132 } 133 } 134 } 135 return multiValueMap; 136 } 137 138 protected void printAsyncResult(MvcResult result) throws Exception { 139 HttpServletRequest request = result.getRequest(); 140 this.printer.printValue("Async started", request.isAsyncStarted()); 141 Object asyncResult = null; 142 try { 143 asyncResult = result.getAsyncResult(0); 144 } 145 catch (IllegalStateException ex) { 146 // Not set 147 } 148 this.printer.printValue("Async result", asyncResult); 149 } 150 151 /** 152 * Print the handler. 153 */ 154 protected void printHandler(Object handler, HandlerInterceptor[] interceptors) throws Exception { 155 if (handler == null) { 156 this.printer.printValue("Type", null); 157 } 158 else { 159 if (handler instanceof HandlerMethod) { 160 HandlerMethod handlerMethod = (HandlerMethod) handler; 161 this.printer.printValue("Type", handlerMethod.getBeanType().getName()); 162 this.printer.printValue("Method", handlerMethod); 163 } 164 else { 165 this.printer.printValue("Type", handler.getClass().getName()); 166 } 167 } 168 } 169 170 /** 171 * Print exceptions resolved through a HandlerExceptionResolver. 172 */ 173 protected void printResolvedException(Exception resolvedException) throws Exception { 174 if (resolvedException == null) { 175 this.printer.printValue("Type", null); 176 } 177 else { 178 this.printer.printValue("Type", resolvedException.getClass().getName()); 179 } 180 } 181 182 /** 183 * Print the ModelAndView. 184 */ 185 protected void printModelAndView(ModelAndView mav) throws Exception { 186 this.printer.printValue("View name", (mav != null) ? mav.getViewName() : null); 187 this.printer.printValue("View", (mav != null) ? mav.getView() : null); 188 if (mav == null || mav.getModel().size() == 0) { 189 this.printer.printValue("Model", null); 190 } 191 else { 192 for (String name : mav.getModel().keySet()) { 193 if (!name.startsWith(BindingResult.MODEL_KEY_PREFIX)) { 194 Object value = mav.getModel().get(name); 195 this.printer.printValue("Attribute", name); 196 this.printer.printValue("value", value); 197 Errors errors = (Errors) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + name); 198 if (errors != null) { 199 this.printer.printValue("errors", errors.getAllErrors()); 200 } 201 } 202 } 203 } 204 } 205 206 /** 207 * Print "output" flash attributes. 208 */ 209 protected void printFlashMap(FlashMap flashMap) throws Exception { 210 if (ObjectUtils.isEmpty(flashMap)) { 211 this.printer.printValue("Attributes", null); 212 } 213 else { 214 for (String name : flashMap.keySet()) { 215 this.printer.printValue("Attribute", name); 216 this.printer.printValue("value", flashMap.get(name)); 217 } 218 } 219 } 220 221 /** 222 * Print the response. 223 */ 224 protected void printResponse(MockHttpServletResponse response) throws Exception { 225 this.printer.printValue("Status", response.getStatus()); 226 this.printer.printValue("Error message", response.getErrorMessage()); 227 this.printer.printValue("Headers", getResponseHeaders(response)); 228 this.printer.printValue("Content type", response.getContentType()); 229 this.printer.printValue("Body", response.getContentAsString()); 230 this.printer.printValue("Forwarded URL", response.getForwardedUrl()); 231 this.printer.printValue("Redirected URL", response.getRedirectedUrl()); 232 printCookies(response.getCookies()); 233 } 234 235 /** 236 * Print the supplied cookies in a human-readable form, assuming the 237 * {@link Cookie} implementation does not provide its own {@code toString()}. 238 * @since 4.2 239 */ 240 private void printCookies(Cookie[] cookies) { 241 String[] cookieStrings = new String[cookies.length]; 242 for (int i = 0; i < cookies.length; i++) { 243 Cookie cookie = cookies[i]; 244 cookieStrings[i] = new ToStringCreator(cookie) 245 .append("name", cookie.getName()) 246 .append("value", cookie.getValue()) 247 .append("comment", cookie.getComment()) 248 .append("domain", cookie.getDomain()) 249 .append("maxAge", cookie.getMaxAge()) 250 .append("path", cookie.getPath()) 251 .append("secure", cookie.getSecure()) 252 .append("version", cookie.getVersion()) 253 .append("httpOnly", cookie.isHttpOnly()) 254 .toString(); 255 } 256 this.printer.printValue("Cookies", cookieStrings); 257 } 258 259 protected final HttpHeaders getResponseHeaders(MockHttpServletResponse response) { 260 HttpHeaders headers = new HttpHeaders(); 261 for (String name : response.getHeaderNames()) { 262 headers.put(name, response.getHeaders(name)); 263 } 264 return headers; 265 } 266 267 268 /** 269 * A contract for how to actually write result information. 270 */ 271 protected interface ResultValuePrinter { 272 273 void printHeading(String heading); 274 275 void printValue(String label, Object value); 276 } 277 278}