001package org.kohsuke.stapler.json;
002
003import net.sf.json.JSONObject;
004import org.apache.commons.io.IOUtils;
005import org.kohsuke.stapler.AnnotationHandler;
006import org.kohsuke.stapler.InjectedParameter;
007import org.kohsuke.stapler.StaplerRequest;
008
009import javax.servlet.ServletException;
010import java.io.IOException;
011import java.lang.annotation.Annotation;
012import java.lang.annotation.Documented;
013import java.lang.annotation.Retention;
014import java.lang.annotation.Target;
015
016import static java.lang.annotation.ElementType.PARAMETER;
017import static java.lang.annotation.RetentionPolicy.RUNTIME;
018
019/**
020 * Binds the body payload into POJO via json-lib.
021 *
022 * <p>
023 * On a web-bound <tt>doXyz</tt> method, use this method on a parameter to get the content of the request
024 * data-bound to a bean through {@link JSONObject#fromObject(Object)} and inject it as a parameter.
025 * For example,
026 *
027 * <pre>
028 * &#64;JsonResponse
029 * public Point doDouble(@JsonBody Point p) {
030 *   Point pt = new Point();
031 *   pt.x = p.x*2;
032 *   pt.y = p.y*2;
033 *   return pt;
034 * }
035 *
036 * public class Point { public int x, y; }
037 * </pre>
038 *
039 * Request:
040 * <pre>
041 * POST ..../double
042 * Content-Type: application/json
043 *
044 * {x:10,y:5}
045 * </pre>
046 *
047 * Response:
048 * <pre>
049 * 200 OK
050 * Content-Type: application/json;charset=UTF-8
051 *
052 * {x:20,y:10}
053 * </pre>
054 *
055 * @author Kohsuke Kawaguchi
056 * @see JsonResponse
057 */
058@Target(PARAMETER)
059@Retention(RUNTIME)
060@Documented
061@InjectedParameter(JsonBody.Handler.class)
062public @interface JsonBody {
063    public static class Handler extends AnnotationHandler {
064        @Override
065        public Object parse(StaplerRequest request, Annotation a, Class type, String parameterName) throws ServletException {
066            String ct = request.getContentType();
067            if ((ct == null) || !ct.startsWith("application/json"))
068                throw new ServletException("Expected application/json but got "+ct);
069
070            try {
071                // TODO: exception thrown here results in error page rendered in HTML.
072                JSONObject o = JSONObject.fromObject(IOUtils.toString(request.getReader()));
073                return JSONObject.toBean(o,type);
074            } catch (IOException e) {
075                throw new ServletException("Failed to read JSON",e);
076            }
077        }
078    }
079}