In my spring-boot project, I use freemarker templates for sample forms. I needed to add filter in order to read payload and do some stuff. I know if you read payload in filter, you need to reset request body. Because it can be read once. Since I encountered this problem before, I knew that I must have used wrapper. I expected solve my problem as before. However, in the controller, all fields in input objects are null.
What am I missing in here ?
My filter:
public class KfsInMsgFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
Map<String, String[]> extraParams = new TreeMap<String, String[]>();
WrappedRequest wrappedRequest = new WrappedRequest(request, extraParams);
String body = IOUtils.toString(new BufferedReader(new InputStreamReader(wrappedRequest.getInputStream(), Constants.UTF_8)));
// doing some stuff using body
// ....
// resetting payload
wrappedRequest.resetStream(body.getBytes(Constants.UTF_8));
...
}
}
WrappedRequest class:
#Slf4j
public class WrappedRequest extends HttpServletRequestWrapper {
private final Map<String, String[]> modifiableParameters;
private ResettableServletInputStream servletStream;
private byte[] rawData;
private HttpServletRequest request;
private String payload;
/**
* Create a new request wrapper that will merge additional parameters into
* the request object without prematurely reading parameters from the
* original request.
*
* #param request
* #param additionalParams
*/
public WrappedRequest(final HttpServletRequest request,
final Map<String, String[]> additionalParams) {
super(request);
this.request = request;
this.modifiableParameters = new TreeMap<String, String[]>();
this.modifiableParameters.putAll(additionalParams);
this.servletStream = new ResettableServletInputStream();
}
/**
* #param newRawData
*/
public void resetStream(byte[] newRawData) {
servletStream.stream = new ByteArrayInputStream(newRawData);
}
/**
* #return
* #throws IOException
*/
#Override
public ServletInputStream getInputStream() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return servletStream;
}
/**
* #return
* #throws IOException
*/
#Override
public BufferedReader getReader() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return new BufferedReader(new InputStreamReader(servletStream, Constants.UTF_8));
}
/**
* #return
*/
private String getBodyAsString() {
StringBuffer buff = new StringBuffer();
buff.append(" BODY_DATA START [ ");
char[] charArr = new char[getContentLength()];
try {
BufferedReader reader = new BufferedReader(getReader());
reader.read(charArr, 0, charArr.length);
reader.close();
} catch (IOException e) {
log.error("", e);
}
buff.append(charArr);
buff.append(" ] BODY_DATA END ");
return buff.toString();
}
/**
* #return
*/
public String getPayload() {
return payload;
}
/**
* #param payload
*/
public void setPayload(String payload) {
this.payload = payload;
}
private static class ResettableServletInputStream extends ServletInputStream {
private InputStream stream;
#Override
public int read() throws IOException {
return stream.read();
}
#Override
public boolean isFinished() {
return false;
}
#Override
public boolean isReady() {
return false;
}
#Override
public void setReadListener(ReadListener readListener) {
}
}
}
Body I expected to get in controller:
What I get:
#PostMapping(value = "/edit")
public String editPlatform(EditInfo editInfo, Model model) {
Optional<Platform> p = platformService.findById(editInfo.getId());
List<SafeCustodyOffice> officeList = safeCustodyOfficeService.getAll();
if (p.isPresent()) {
model.addAttribute("platform", p.get());
model.addAttribute("offices", officeList);
return "platform-edit";
} else {
throw new KfsException(ErrorCodes.KFS19);
}
}
Important Edit:
I discovered someting I found interesting and gives me clues about the problem. This may be makes more sense for anybody but me.
I see that the content type of input changes the result like this:
Is there any workaround to make row 5 combination work like row 3?
I've a REST server made with Spring Boot 2.0.1.
I'm customizing exception handling extending ResponseEntityExceptionHandler.
This is my class
#RestControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
private Logger log = LogManager.getLogger();
#Autowired
private MessageSource messageSource;
private MessageSourceAccessor messageSourceAccessor = null;
#PostConstruct
public void postConstruct() {
Assert.notNull(messageSource, "MessageSource must not be null!");
this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
}
/**
* Mapping each constraint name with the corrispondent exception code -> message
*/
private static Map<String, ExceptionCode> constraintCodeMap = new HashMap<String, ExceptionCode>() {
private static final long serialVersionUID = -628747907324708275L;
{
put("account_username", ExceptionCode.ACCOUNT_DUPLICATE_USERNAME);
put("paymenttype_code", ExceptionCode.PAYMENTTYPE_DUPLICATE_CODE);
put("ticketblock_number_type", ExceptionCode.TICKETBLOCK_DUPLICATE_CODE);
put("ticket_number", ExceptionCode.TICKET_DUPLICATED_CODE);
put("licenseplate_plate", ExceptionCode.LICENSEPLATE_DUPLICATED);
}
};
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildGenericError(ex, request, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
#Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildGenericError(ex, request, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
if (ExceptionUtils.getRootCauseMessage(ex).contains("Duplicate entry")) {
/**
* Custom errors and messages for DataIntegrityViolationException checked against the list of indexes names
*/
return response(HttpStatus.CONFLICT, new HttpHeaders(),
buildIntegrityError(ex, request, HttpStatus.CONFLICT, LocaleContextHolder.getLocale()));
} else {
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildGenericError(ex, request, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
}
/**
* #see {#link RepositoryRestExceptionHandler}
*
* #param ex
* #param request
* #param locale
* #return
* #throws Exception
*/
#ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<?> handleConflictException(DataIntegrityViolationException ex, HttpServletRequest request, Locale locale)
throws Exception {
/**
* Keep the default Exception format for Violation exception #see {#link RepositoryRestExceptionHandler}
*/
if (ex instanceof RepositoryConstraintViolationException) {
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
new RepositoryConstraintViolationExceptionMessage((RepositoryConstraintViolationException) ex, messageSourceAccessor));
}
/**
* Custom errors and messages for DataIntegrityViolationException checked against the list of indexes names
*/
return response(HttpStatus.CONFLICT, new HttpHeaders(), buildIntegrityError(ex, request, HttpStatus.CONFLICT, locale));
}
/**
* Handle the exception when the file size is bigger than the maximum set in the configuration
*
* #param ex
* #param request
* #param locale
* #return
* #throws Exception
*/
#ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<?> handleFileUpload(MaxUploadSizeExceededException ex, HttpServletRequest request, Locale locale)
throws Exception {
log.error(String.format("Received a file too big from %s. Error: %s", AppUtils.getRemoteIp(request),
ExceptionUtils.getRootCauseMessage(ex)));
return response(HttpStatus.BAD_REQUEST, new HttpHeaders(),
buildIntegrityError(ex, request, HttpStatus.BAD_REQUEST, LocaleContextHolder.getLocale()));
}
/**
* Build a JSON integrity error compliant to the standard exception
*
* #param exception
* #param request
* #param httpStatus
* #param message
* #return
*/
private JsonException buildIntegrityError(final Throwable exception, final HttpServletRequest request, final HttpStatus httpStatus,
Locale locale) {
return buildIntegrityError(exception, request.getRequestURI(), httpStatus, locale);
}
private JsonException buildIntegrityError(final Throwable exception, final WebRequest request, final HttpStatus httpStatus,
Locale locale) {
return buildIntegrityError(exception, "", httpStatus, locale);
}
/**
* Build a JSON integrity error compliant to the standard exception
*
*/
private JsonException buildIntegrityError(final Throwable exception, String requestUri, final HttpStatus httpStatus, Locale locale) {
String finalMessage = "";
String rootMsg = ExceptionUtils.getRootCauseMessage(exception);
Optional<Map.Entry<String, ExceptionCode>> entry = constraintCodeMap.entrySet().stream()
.filter((it) -> rootMsg.contains(it.getKey())).findAny();
if (entry.isPresent()) {
finalMessage = messageSource.getMessage(entry.get().getValue().getCode(), new Object[] {}, locale);
} else {
finalMessage = messageSource.getMessage(ExceptionCode.INTEGRITY_VIOLATION.getCode(), new Object[] { rootMsg }, locale);
}
JsonException jsonException = new JsonException();
jsonException.setError(httpStatus.getReasonPhrase());
jsonException.setStatus(httpStatus.value());
jsonException.setException(exception.getClass().getName());
jsonException.setMessage(finalMessage);
jsonException.setPath(requestUri);
return jsonException;
}
/**
* Build a JSON integrity error compliant to the standard exception
*
* #param exception
* #param request
* #param httpStatus
* #param message
* #return
*/
private JsonException buildGenericError(final Throwable exception, final HttpServletRequest request, final HttpStatus httpStatus,
Locale locale) {
String rootMsg = ExceptionUtils.getRootCauseMessage(exception);
String finalMessage = messageSource.getMessage(ExceptionCode.INTERNAL_ERROR.getCode(), new Object[] { rootMsg }, locale);
JsonException jsonException = new JsonException();
jsonException.setError(httpStatus.getReasonPhrase());
jsonException.setStatus(httpStatus.value());
jsonException.setException(exception.getClass().getName());
jsonException.setMessage(finalMessage);
jsonException.setPath(request.getRequestURI());
return jsonException;
}
private JsonException buildGenericError(final Throwable exception, final WebRequest request, final HttpStatus httpStatus,
Locale locale) {
String rootMsg = ExceptionUtils.getRootCauseMessage(exception);
String finalMessage = messageSource.getMessage(ExceptionCode.INTERNAL_ERROR.getCode(), new Object[] { rootMsg }, locale);
JsonException jsonException = new JsonException();
jsonException.setError(httpStatus.getReasonPhrase());
jsonException.setStatus(httpStatus.value());
jsonException.setException(exception.getClass().getName());
jsonException.setMessage(finalMessage);
jsonException.setPath("");
return jsonException;
}
private static <T> ResponseEntity<T> response(HttpStatus status, HttpHeaders headers, T body) {
Assert.notNull(headers, "Headers must not be null!");
Assert.notNull(status, "HttpStatus must not be null!");
return new ResponseEntity<T>(body, headers, status);
}
}
I want to override the behaviour for HttpMessageNotReadableException but I have to Override the method handleHttpMessageNotReadable because it is an exception managed by the superclass and I can't create my method annotated with #ExceptionHandler.
The problem is the method exposes WebRequest instead of a HttpServletRequest. I need a HttpServletRequest to get the remote ip address of the client.
Is there a way to do what I did for DataIntegrityViolationException in my class where I can get the HttpServletRequest?
For getting HttpServletRequest,you can do:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
Hope this helps.
I have this class to generate a JSON Web token with I got from this post.
I need an id and a expression date to create a token.
Do I have to set up some kind of server to get the id and the expression date?
/**
* Provides static methods for creating and verifying access tokens and such.
*
* #author davidm
*
*/
public class AuthHelper {
private static final String AUDIENCE = "NotReallyImportant";
private static final String ISSUER = "crazyquote";
private static final String SIGNING_KEY = "LongAndHardToGuessValueWithSpecialCharacters#^($%*$%";
/**
* Creates a json web token which is a digitally signed token that contains
* a payload (e.g. userId to identify the user). The signing key is secret.
* That ensures that the token is authentic and has not been modified. Using
* a jwt eliminates the need to store authentication session information in
* a database.
*
* #param userId
* #param durationDays
* #return
*/
public static String createJsonWebToken(String userId, Long durationDays) {
// Current time and signing algorithm
Calendar cal = Calendar.getInstance();
HmacSHA256Signer signer;
try {
signer = new HmacSHA256Signer(ISSUER, null, SIGNING_KEY.getBytes());
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
// Configure JSON token
JsonToken token = new net.oauth.jsontoken.JsonToken(signer);
token.setAudience(AUDIENCE);
token.setIssuedAt(new org.joda.time.Instant(cal.getTimeInMillis()));
token.setExpiration(new org.joda.time.Instant(cal.getTimeInMillis()
+ 1000L * 60L * 60L * 24L * durationDays));
// Configure request object, which provides information of the item
JsonObject request = new JsonObject();
request.addProperty("userId", userId);
System.out.println("request " + request);
JsonObject payload = token.getPayloadAsJsonObject();
payload.add("info", request);
try {
return token.serializeAndSign();
} catch (SignatureException e) {
throw new RuntimeException(e);
}
}
/**
* Verifies a json web token's validity and extracts the user id and other
* information from it.
*
* #param token
* #return
* #throws SignatureException
* #throws InvalidKeyException
*/
public static TokenInfo verifyToken(String token) {
try {
final Verifier hmacVerifier = new HmacSHA256Verifier(
SIGNING_KEY.getBytes());
VerifierProvider hmacLocator = new VerifierProvider() {
#Override
public List<Verifier> findVerifier(String id, String key) {
return Lists.newArrayList(hmacVerifier);
}
};
VerifierProviders locators = new VerifierProviders();
locators.setVerifierProvider(SignatureAlgorithm.HS256, hmacLocator);
net.oauth.jsontoken.Checker checker = new net.oauth.jsontoken.Checker() {
#Override
public void check(JsonObject payload) throws SignatureException {
// don't throw - allow anything
}
};
// Ignore Audience does not mean that the Signature is ignored
JsonTokenParser parser = new JsonTokenParser(locators, checker);
JsonToken jt;
try {
jt = parser.verifyAndDeserialize(token);
} catch (SignatureException e) {
throw new RuntimeException(e);
}
JsonObject payload = jt.getPayloadAsJsonObject();
TokenInfo t = new TokenInfo();
String issuer = payload.getAsJsonPrimitive("iss").getAsString();
String userIdString = payload.getAsJsonObject("info")
.getAsJsonPrimitive("userId").getAsString();
if (issuer.equals(ISSUER) && !StringUtils.isBlank(userIdString)) {
t.setUserId(new ObjectId(userIdString));
t.setIssued(new DateTime(payload.getAsJsonPrimitive("iat")
.getAsLong()));
t.setExpires(new DateTime(payload.getAsJsonPrimitive("exp")
.getAsLong()));
return t;
} else {
return null;
}
} catch (InvalidKeyException e1) {
throw new RuntimeException(e1);
}
}
}
I would expect the user's ID in this context is either the username sent to the application by the user themselves, or some other kind of ID that you can look up based on the principal the user sent. The expiration date you simply choose. How long do you want the token to be valid before the user has to relogin? Now, on the topic of servers, there's nothing in the OAuth2 protocol mandating a server or a web context. What kind of application are you building?
For some reason I'm getting null pointer exception. It's downloading the image here and logcat points me to call
public Result call(final String method, final String apiKey, final String... params) {
return call(method, apiKey, map(params));
}
/**
* Performs the web-service call. If the <code>session</code> parameter is
* <code>non-null</code> then an authenticated call is made. If it's
* <code>null</code> then an unauthenticated call is made.<br/>
* The <code>apiKey</code> parameter is always required, even when a valid
* session is passed to this method.
*
* #param method The method to call
* #param apiKey A Last.fm API key
* #param params Parameters
* #param session A Session instance or <code>null</code>
* #return the result of the operation
*/
public Result call(final String method, final String apiKey, Map<String, String> params) {
params = new WeakHashMap<String, String>(params);
InputStream inputStream = null;
// no entry in cache, load from web
if (inputStream == null) {
// fill parameter map with apiKey and session info
params.put(PARAM_API_KEY, apiKey);
try {
final HttpURLConnection urlConnection = openPostConnection(method, params);
inputStream = getInputStreamFromConnection(urlConnection);
if (inputStream == null) {
lastResult = Result.createHttpErrorResult(urlConnection.getResponseCode(),
urlConnection.getResponseMessage());
return lastResult;
}
} catch (final IOException ignored) {
}
}
try {
final Result result = createResultFromInputStream(inputStream);
lastResult = result;
return result;
} catch (final IOException ignored) {
} catch (final SAXException ignored) {
}
return null;
}
It finally cracks at the line "new InputSource(new InputStreamReader(inputStream, "UTF-8")));".
/**
* #param inputStream
* #return
* #throws SAXException
* #throws IOException
*/
private Result createResultFromInputStream(final InputStream inputStream) throws SAXException,
IOException {
final Document document = newDocumentBuilder().parse(
new InputSource(new InputStreamReader(inputStream, "UTF-8")));
final Element root = document.getDocumentElement(); // lfm element
final String statusString = root.getAttribute("status");
final Status status = "ok".equals(statusString) ? Status.OK : Status.FAILED;
if (status == Status.FAILED) {
final Element errorElement = (Element)root.getElementsByTagName("error").item(0);
final int errorCode = Integer.parseInt(errorElement.getAttribute("code"));
final String message = errorElement.getTextContent();
return Result.createRestErrorResult(errorCode, message);
} else {
return Result.createOkResult(document);
}
}
Any ideas? I have no idea what might be wrong. If sufficient info is provided then let me know - I'll get what you need. I'm a beginner. :)
I am working on android app and I want to know how to get data from Json object by using http GET the (the http request url is APIary)
It's my first time to use Json and httpRequests so I don't know the syntax needed for this
That's my HttpRequest class I'm using :
public abstract class HttpRequest extends AsyncTask<String, String, String> {
private HttpClient httpClient;
private HttpRequestBase request;
private boolean hasError = false;
private String errorMessage = null;
private boolean hasBody = false;
private int statusCode;
public HttpRequest(){
httpClient = new DefaultHttpClient();
}
/**
* This method is called from the subclasses to pass the request method used to this class
* #param request , The request class passed from the subclass
*/
void setMethod(HttpRequestBase request){
this.request = request;
}
/**
* Adds a header to the current request
* #param header , header key
* #param value , header value
*/
public void addHeader(String header,String value){
this.request.addHeader(header, value);
}
/**
* #return false if the status code was anything other than 2XX after executing the request , true otherwise
*/
public boolean hasError() {
return hasError;
}
/**
* A getter for the error message
* #return String the error message returned from the request if any
*/
public String getErrorMessage() {
return errorMessage;
}
/**
* This is the method responsible for executing the request and handling the response
* #return String , The response body , null in case of errors
*/
#Override
protected String doInBackground(String... args) {
if(hasBody){
this.request.addHeader("content-type", "application/json");
}
ResponseHandler<String> handler = new BasicResponseHandler();
HttpResponse x = null;
try{
x = httpClient.execute(this.request);
this.statusCode = x.getStatusLine().getStatusCode();
return handler.handleResponse(x);
}catch(ClientProtocolException e ){
hasError = true;
errorMessage = e.getMessage();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* A getter method for the status code
* #return int , the status code of executing the request
*/
public int getStatusCode(){
return this.statusCode;
}
/**
* A setter method to set whether the request has a body or not , used between this class and its subclasses
* #param hasBody boolean
*/
void setHasBody(boolean hasBody){
this.hasBody = hasBody;
}
}
I think this post can help you :
How to parse JSON in Android
Tell me if don't understand !