When I tried to user Micro Message Public Platform, the weChat server will invoke one of my API and I need to return a token to validate my identity. However,when I return the token directly like this, the weChat server alerts that validation is error.
#RequestMapping(value="/userFollow", method= RequestMethod.GET)
#ResponseBody
public String weChatToken(HttpServletRequest request,String signature,String timestamp,String nonce,String echostr,HttpServletResponse response) throws IOException, DocumentException {
String result=weChatService.checkSignature(signature,timestamp,nonce,echostr);
return result;
}
Then I changed my code as below. This time, the validation is correct.
#RequestMapping(value="/userFollow", method= RequestMethod.GET)
#ResponseBody
public String weChatToken(HttpServletRequest request,String signature,String timestamp,String nonce,String echostr,HttpServletResponse response) throws IOException, DocumentException {
String result=weChatService.checkSignature(signature,timestamp,nonce,echostr);
PrintWriter pw=response.getWriter();
pw.write(result);
pw.flush();
return null;
}
I Googled and got that when using #Responsebody, Spring write messages to the body of response.
So what's the difference between them? Why the first way is Wrong?
An HTTP response consists of a status code, some headers, and a body. Using #ResponseBody means your method gives the content of the body, and nothing else. Using HttpServletResponse enables your method to set all aspects of the response, but is a little inconvenient to use.
You should use ResponseBody for returning some data structure. Since you need "only" String, you should change the return type of your method to void from String and remove ResponseBody annotation.
#RequestMapping(value="/userFollow", method= RequestMethod.GET)
public void weChatToken(HttpServletRequest request,String signature,String timestamp,String nonce,String echostr,HttpServletResponse response) throws IOException, DocumentException {
String result=weChatService.checkSignature(signature,timestamp,nonce,echostr);
PrintWriter pw=response.getWriter();
pw.write(result);
pw.flush();
}
Related
I have a use case where I need to verify if the incoming request body to my controller contains any special characters in a Hybris storefront. Though it can be achieved from the front-end by blocking any special characters, we require back-end validation.
I tried using HandlerIntercepterAdapter to intercept the request and validate for any special characters. But whenever I use request.getReader() or request.getInputStream() and read the data, request body is cleared.
I tried using IOUtils.copy() but this too reads from the original request and makes the body empty.
Even after wrapping the request with HttpServletRequestWrapper or ContentCachingRequestWrapper, the request body gets cleared. I guess internally somewhere it uses the same reference.
I tried following this thread but was unable to solve this issue.
I am looking for a solution where I can extract the request body and validate it without letting it get cleared so it can be used in different controllers [or] any alternative approach which can help in preventing any special characters to hit the controller.
any alternative approach which can help in preventing any special characters to hit the controller.
What if you try to do the following ...
Get the request body
Process it
Set the request body again in your filter by setting the body to the processed version ?
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest originalRequest = (HttpServletRequest) request;
HttpServletResponse originalResponse = (HttpServletResponse) response;
/**
* 2.Read the original request body and change it
*/
String originalRequestBody = ServletUtil.readRequestBody(originalRequest); // Read the original request body
// Body is processed here !
String modifyRequestBody = processBody(originalRequestBody); // Modify request body (clear text)
HttpServletRequest orginalRequest = (HttpServletRequest) request;
ModifyRequestBodyWrapper requestWrapper = new ModifyRequestBodyWrapper(orginalRequest, modifyRequestBody);
/**
* 3. Build a new response object
*/
ModifyResponseBodyWrapper responseWrapper = new ModifyResponseBodyWrapper(originalResponse);
chain.doFilter(requestWrapper, responseWrapper);
String originalResponseBody = responseWrapper.getResponseBody(); // Original response body (clear text)
String modifyResponseBody = this.encryptBody(originalResponseBody); // Modified response volume (ciphertext)
/**
* 4.Output the modified response body with the output stream of the original response object
* To ensure that the response type is consistent with the original request, and reset the response body size
*/
originalResponse.setContentType(requestWrapper.getOrginalRequest().getContentType()); // Be consistent with the request
byte[] responseData = modifyResponseBody.getBytes(responseWrapper.getCharacterEncoding()); // The coding is consistent with the actual response
originalResponse.setContentLength(responseData.length);
#Cleanup ServletOutputStream out = originalResponse.getOutputStream();
out.write(responseData);
}
Here is a code example, which implements this.
The input should be set inside a form.
In your controller, you can use a validator :
#RequestMapping(value = "/process", method = RequestMethod.POST)
public String doValidateAndPost(final MyForm form, final BindingResult bindingResult,
final HttpServletRequest request, final Model model){
getMyValidator().validate(form, bindingResult);
if (bindingResult.hasErrors())
{
return MY_PAGE;
}
The validator will look like this :
#Override
public void validate(final Object object, final Errors errors)
{
final MyForm form = (MyForm ) object;
final String data = form.getMyData();
Pattern p = Pattern.compile("[^a-z0-9 ]", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(data );
boolean b = m.find();
if (b)
{
errors.rejectValue("myData", "myData.invalid");
}
}
You can also use the #Valid annotation :
public String doValidateAndPost(#Valid final MyForm form ...
And set in your form :
#Pattern(regexp = "[a-z0-9 ]")
private String myData;
I have a rest end point designed in spring boot. Tomcat is being used as embedded server. It takes a query parameter.
When I pass query parameter as param1%uFF07 tomcat internally reads parameter as null
When I pass query parameter as param1%FF07 tomcat reads as some character.
tomcat only reads '%' character when followed by two hexadecimal numbers, if u is placed after '%' character tomcat parse parameter as null with message
Character decoding failed. Parameter [name] with value [param1%uFF07]
has been ignored. Note that the name and value quoted here may be
corrupted due to the failed decoding. Use debug level logging to see
the original, non-corrupted values. Note: further occurrences of
Parameter errors will be logged at DEBUG level.
Here is spring boot controller code
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", required = false) String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
You are passing % sign in your url, but % is symbol in url, to pass % as it is... you will have to pass %25 then it will work as you expected.
So, if you pass %25uFF07 then it will show you %uFF07 as value.
No need to change anything in application.properties or any kind of settings. I have tested this in my project.
Please feel free to ask for any clarification. Hope It Helps.
I found out a way using filters. Basics about filters could be found over here. We can intercept request query string there and use Tomcat UDecoder class to parse the query string and if any exception is thrown we can show response of 400
public class SimpleFilter implements Filter {
private final UDecoder urlDecoder = new UDecoder();
private final Logger logger = LoggerFactory.getLogger(SimpleFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
String queryString = httpServletRequest.getQueryString();
if (queryString != null) {
ByteChunk byteChunk = new ByteChunk();
byteChunk.setBytes(queryString.getBytes(), 0, queryString.length());
try {
urlDecoder.convert(byteChunk, true);
} catch (IOException ioException) {
logger.error("Hazarduos character found in request parameter.");
httpServletResponse.setStatus(HttpStatus.BAD_REQUEST.value());
return;
}
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
I have a simple method where I need to change the contentType to "text/plain" but when I am using Jboss EAP 6.2 it is ignored and the returned value is "application/json.
#RestController
#RequestMapping("/upload")
public class UploadController {
...
#RequestMapping(method = RequestMethod.POST)
public Result foo(MultipartFile arquivo, HttpServletResponse response) {
response.setContentType("text/plain");
return uploadService.saveFile(arquivo);
}
}
I am using SpringMVC (rest controller) and Jackson to parse to JSON. The exactly same method works fine when I use Jetty instead of Jboss.
I can't use #RequestMapping(produces="text/plain") because the actual method has a bit more logic into it and sometimes it will return application/json others text/plain, depending if the request came from IE9 browser or not.
Can someone please tell me how can I set the contentType on Jboss?
Unfortunately the only way I could get this working for Jboss was creating a different method for IE9:
#RequestMapping(value = "/ie9", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE)
public String fooIE9(#RequestParam("arquivo") MultipartFile arquivo) throws JsonProcessingException {
ObjectWriter ow = new ObjectMapper().writer();
return ow.writeValueAsString(uploadService.saveFile(arquivo));
}
I am trying to add file uploading and downloading in my web application.
I am used to don't use raw HttpServletRequest and HttpServletResponse when I use spring mvc. But now I have following controller to download files.
public ModelAndView download(HttpServletRequest request, HttpServletResponse response) throws Exception {
int id = ServletRequestUtils.getRequiredIntParameter(request, "id");
Files file = this.filesService.find(id);
response.setContentType(file.getType());
response.setContentLength(file.getFile().length);
response.setHeader("Content-Disposition","attachment; filename=\"" + file.getFilename() +"\"");
FileCopyUtils.copy(file.getFile(), response.getOutputStream());
return null;
}
As you can see I use HttpServletRequest and HttpServletResponse here.
I want to find way to avoid using of these classes. Is it possible?
The id parameter that you are getting from request can be substituted with the use of #RequestParam or #PathVariable. See bellow for an example of #RequestParam
public ModelAndView download(#RequestParam("id") int id) {
// Now you can use the variable id as Spring MVC has extracted it from the HttpServletRequest
Files file = this.filesService.find(id); // Continue from here...
}
And now the response part
#RequestMapping(value = "/download")
public ResponseEntity<byte[]> download(#RequestParam("id") int id) throws IOException
{
// Use of http headers....
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
InputStream is // Get your file contents read into this input stream
return new ResponseEntity<byte[]>(IOUtils.toByteArray(is), headers, HttpStatus.CREATED);
}
I use Liferay and I have code:
#ResourceMapping(value = "keaFileUpload")
public ModelAndView fileUpload(ResourceRequest request, ResourceResponse response,
ModelMap modelMap) throws PortalException, SystemException {
// code
}
How can I use Multipart as parameter request? For example MultipartHttpServletRequest?
When I use it instead of ResourceRequest request I have error.
When the form is multipart/form-data you should get extra parameter(like "name") from UploadPortletRequest instead of ResourceRequest.
correct form of getting parameter:
UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(request);
String name = ParamUtil.getString(uploadRequest,"name");
Also you can check out this link
https://www.liferay.com/community/forums/-/message_boards/message/17237791